<commit_before>package collectors

import (
	"strings"
	"time"

	"github.com/StackExchange/scollector/opentsdb"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_omreport_chassis, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_system, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_storage_enclosure, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_storage_vdisk, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_storage_controller, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_storage_battery, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_ps, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_ps_amps, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_ps_volts, Interval: time.Minute * 5})
}

func c_omreport_chassis() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) != 2 || fields[0] == "SEVERITY" {
			return
		}
		sev := 0
		if fields[0] != "Ok" {
			sev = 1
		}
		component := strings.Replace(fields[1], " ", "_", -1)
		Add(&md, "hw.chassis", sev, opentsdb.TagSet{"component": component})
	}, "omreport", "chassis", "-fmt", "ssv")
	return md
}

func c_omreport_system() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) != 2 || fields[0] == "SEVERITY" {
			return
		}
		sev := 0
		if fields[0] != "Ok" {
			sev = 1
		}
		component := strings.Replace(fields[1], " ", "_", -1)
		Add(&md, "hw.system", sev, opentsdb.TagSet{"component": component})
	}, "omreport", "system", "-fmt", "ssv")
	return md
}

func c_omreport_storage_enclosure() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.storage.enclosure", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "enclosure", "-fmt", "ssv")
	return md
}

func c_omreport_storage_vdisk() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.storage.vdisk", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "vdisk", "-fmt", "ssv")
	return md
}

func c_omreport_ps() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "Index" {
			return
		}
		sev := 0
		if fields[1] != "Ok" {
			sev = 1
		}
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.ps", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "chassis", "pwrsupplies", "-fmt", "ssv")
	return md
}

func c_omreport_ps_amps() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) != 2 || !strings.Contains(fields[0], "Current") {
			return
		}
		i_fields := strings.Split(fields[0], "Current")
		v_fields := strings.Fields(fields[1])
		if len(i_fields) < 2 && len(v_fields) < 2 {
			return
		}
		id := strings.Replace(i_fields[0], " ", "", -1)
		Add(&md, "hw.ps.current", v_fields[0], opentsdb.TagSet{"id": id})
	}, "omreport", "chassis", "pwrmonitoring", "-fmt", "ssv")
	return md
}

func c_omreport_ps_volts() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) != 8 || !strings.Contains(fields[2], "Voltage") || fields[3] == "[N/A]" {
			return
		}
		i_fields := strings.Split(fields[2], "Voltage")
		v_fields := strings.Fields(fields[3])
		if len(i_fields) < 2 && len(v_fields) < 2 {
			return
		}
		id := strings.Replace(i_fields[0], " ", "", -1)
		Add(&md, "hw.ps.volts", v_fields[0], opentsdb.TagSet{"id": id})
	}, "omreport", "chassis", "volts", "-fmt", "ssv")
	return md
}

func c_omreport_storage_battery() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.storage.battery", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "battery", "-fmt", "ssv")
	return md
}

func c_omreport_storage_controller() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		c_omreport_storage_pdisk(fields[0], &md)
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.storage.controller", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "controller", "-fmt", "ssv")
	return md
}

// c_omreport_storage_pdisk is called from the controller func, since it needs the encapsulating id.
func c_omreport_storage_pdisk(id string, md *opentsdb.MultiDataPoint) {
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		//Need to find out what the various ID formats might be
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(md, "hw.storage.pdisk", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "pdisk", "controller="+id, "-fmt", "ssv")
}
<commit_msg>cmd/scollector: Don't care about non-critical on chassis and system<commit_after>package collectors

import (
	"strings"
	"time"

	"github.com/StackExchange/scollector/opentsdb"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_omreport_chassis, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_system, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_storage_enclosure, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_storage_vdisk, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_storage_controller, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_storage_battery, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_ps, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_ps_amps, Interval: time.Minute * 5})
	collectors = append(collectors, &IntervalCollector{F: c_omreport_ps_volts, Interval: time.Minute * 5})
}

func c_omreport_chassis() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) != 2 || fields[0] == "SEVERITY" {
			return
		}
		sev := 0
		if fields[0] != "Ok" && fields[0] != "Non-Critical" {
			sev = 1
		}
		component := strings.Replace(fields[1], " ", "_", -1)
		Add(&md, "hw.chassis", sev, opentsdb.TagSet{"component": component})
	}, "omreport", "chassis", "-fmt", "ssv")
	return md
}

func c_omreport_system() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) != 2 || fields[0] == "SEVERITY" {
			return
		}
		sev := 0
		if fields[0] != "Ok" && fields[0] != "Non-Critical" {
			sev = 1
		}
		component := strings.Replace(fields[1], " ", "_", -1)
		Add(&md, "hw.system", sev, opentsdb.TagSet{"component": component})
	}, "omreport", "system", "-fmt", "ssv")
	return md
}

func c_omreport_storage_enclosure() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.storage.enclosure", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "enclosure", "-fmt", "ssv")
	return md
}

func c_omreport_storage_vdisk() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.storage.vdisk", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "vdisk", "-fmt", "ssv")
	return md
}

func c_omreport_ps() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "Index" {
			return
		}
		sev := 0
		if fields[1] != "Ok" {
			sev = 1
		}
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.ps", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "chassis", "pwrsupplies", "-fmt", "ssv")
	return md
}

func c_omreport_ps_amps() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) != 2 || !strings.Contains(fields[0], "Current") {
			return
		}
		i_fields := strings.Split(fields[0], "Current")
		v_fields := strings.Fields(fields[1])
		if len(i_fields) < 2 && len(v_fields) < 2 {
			return
		}
		id := strings.Replace(i_fields[0], " ", "", -1)
		Add(&md, "hw.ps.current", v_fields[0], opentsdb.TagSet{"id": id})
	}, "omreport", "chassis", "pwrmonitoring", "-fmt", "ssv")
	return md
}

func c_omreport_ps_volts() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) != 8 || !strings.Contains(fields[2], "Voltage") || fields[3] == "[N/A]" {
			return
		}
		i_fields := strings.Split(fields[2], "Voltage")
		v_fields := strings.Fields(fields[3])
		if len(i_fields) < 2 && len(v_fields) < 2 {
			return
		}
		id := strings.Replace(i_fields[0], " ", "", -1)
		Add(&md, "hw.ps.volts", v_fields[0], opentsdb.TagSet{"id": id})
	}, "omreport", "chassis", "volts", "-fmt", "ssv")
	return md
}

func c_omreport_storage_battery() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.storage.battery", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "battery", "-fmt", "ssv")
	return md
}

func c_omreport_storage_controller() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		c_omreport_storage_pdisk(fields[0], &md)
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(&md, "hw.storage.controller", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "controller", "-fmt", "ssv")
	return md
}

// c_omreport_storage_pdisk is called from the controller func, since it needs the encapsulating id.
func c_omreport_storage_pdisk(id string, md *opentsdb.MultiDataPoint) {
	readCommand(func(line string) {
		fields := strings.Split(line, ";")
		if len(fields) < 3 || fields[0] == "ID" {
			return
		}
		sev := 0
		if fields[1] != "Ok" && fields[1] != "Non-Critical" {
			sev = 1
		}
		//Need to find out what the various ID formats might be
		id := strings.Replace(fields[0], ":", "_", -1)
		Add(md, "hw.storage.pdisk", sev, opentsdb.TagSet{"id": id})
	}, "omreport", "storage", "pdisk", "controller="+id, "-fmt", "ssv")
}
<|endoftext|>


<commit_before>package collectors

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/StackExchange/slog"
	"github.com/bosun-monitor/scollector/opentsdb"
	"github.com/bosun-monitor/scollector/util"
)

type ProgramCollector struct {
	Path     string
	Interval time.Duration
}

func InitPrograms(cpath string) {
	cdir, err := os.Open(cpath)
	if err != nil {
		slog.Infoln(err)
		return
	}
	idirs, err := cdir.Readdir(0)
	if err != nil {
		slog.Infoln(err)
		return
	}
	for _, idir := range idirs {
		i, err := strconv.Atoi(idir.Name())
		if err != nil || i < 0 {
			slog.Infoln("invalid collector folder name:", idir.Name())
			continue
		}
		interval := time.Second * time.Duration(i)
		dir, err := os.Open(filepath.Join(cdir.Name(), idir.Name()))
		if err != nil {
			slog.Infoln(err)
			continue
		}
		files, err := dir.Readdir(0)
		if err != nil {
			slog.Infoln(err)
			continue
		}
		for _, file := range files {
			if !isExecutable(file) {
				continue
			}
			collectors = append(collectors, &ProgramCollector{
				Path:     filepath.Join(dir.Name(), file.Name()),
				Interval: interval,
			})
		}
	}
}

func isExecutable(f os.FileInfo) bool {
	switch runtime.GOOS {
	case "windows":
		exts := strings.Split(os.Getenv("PATHEXT"), ";")
		fileExt := filepath.Ext(strings.ToUpper(f.Name()))
		for _, ext := range exts {
			if filepath.Ext(strings.ToUpper(ext)) == fileExt {
				return true
			}
		}
		return false
	default:
		return f.Mode()&0111 != 0
	}
}

func (c *ProgramCollector) Run(dpchan chan<- *opentsdb.DataPoint) {
	if c.Interval == 0 {
		for {
			next := time.After(DefaultFreq)
			if err := c.runProgram(dpchan); err != nil {
				slog.Infoln(err)
			}
			<-next
			slog.Infoln("restarting", c.Path)
		}
	} else {
		for {
			next := time.After(c.Interval)
			c.runProgram(dpchan)
			<-next
		}
	}
}

func (c *ProgramCollector) Init() {
}

func (c *ProgramCollector) runProgram(dpchan chan<- *opentsdb.DataPoint) (progError error) {
	cmd := exec.Command(c.Path)
	pr, pw := io.Pipe()
	s := bufio.NewScanner(pr)
	cmd.Stdout = pw
	er, ew := io.Pipe()
	cmd.Stderr = ew
	if err := cmd.Start(); err != nil {
		return err
	}
	go func() {
		progError = cmd.Wait()
		pw.Close()
		ew.Close()
	}()
	go func() {
		es := bufio.NewScanner(er)
		for es.Scan() {
			line := strings.TrimSpace(es.Text())
			slog.Error(line)
		}
	}()
	for s.Scan() {
		line := strings.TrimSpace(s.Text())
		sp := strings.Fields(line)
		if len(sp) < 3 {
			continue
		}
		ts, err := strconv.ParseInt(sp[1], 10, 64)
		if err != nil {
			continue
		}
		dp := opentsdb.DataPoint{
			Metric:    sp[0],
			Timestamp: ts,
			Value:     sp[2],
			Tags:      opentsdb.TagSet{"host": util.Hostname},
		}
		for _, tag := range sp[3:] {
			tags, err := opentsdb.ParseTags(tag)
			if v, ok := tags["host"]; ok && v == "" {
				delete(dp.Tags, "host")
			} else if err != nil {
				return fmt.Errorf("bad tag in program %s, metric %s: %v", c.Path, sp[0], tag)
			} else {
				dp.Tags.Merge(tags)
			}
		}
		dpchan <- &dp
	}
	if err := s.Err(); err != nil {
		return err
	}
	return
}

func (c *ProgramCollector) Name() string {
	return c.Path
}
<commit_msg>cmd/scollector: Better error printing for external collectors<commit_after>package collectors

import (
	"bufio"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/StackExchange/slog"
	"github.com/bosun-monitor/scollector/opentsdb"
	"github.com/bosun-monitor/scollector/util"
)

type ProgramCollector struct {
	Path     string
	Interval time.Duration
}

func InitPrograms(cpath string) {
	cdir, err := os.Open(cpath)
	if err != nil {
		slog.Infoln(err)
		return
	}
	idirs, err := cdir.Readdir(0)
	if err != nil {
		slog.Infoln(err)
		return
	}
	for _, idir := range idirs {
		i, err := strconv.Atoi(idir.Name())
		if err != nil || i < 0 {
			slog.Infoln("invalid collector folder name:", idir.Name())
			continue
		}
		interval := time.Second * time.Duration(i)
		dir, err := os.Open(filepath.Join(cdir.Name(), idir.Name()))
		if err != nil {
			slog.Infoln(err)
			continue
		}
		files, err := dir.Readdir(0)
		if err != nil {
			slog.Infoln(err)
			continue
		}
		for _, file := range files {
			if !isExecutable(file) {
				continue
			}
			collectors = append(collectors, &ProgramCollector{
				Path:     filepath.Join(dir.Name(), file.Name()),
				Interval: interval,
			})
		}
	}
}

func isExecutable(f os.FileInfo) bool {
	switch runtime.GOOS {
	case "windows":
		exts := strings.Split(os.Getenv("PATHEXT"), ";")
		fileExt := filepath.Ext(strings.ToUpper(f.Name()))
		for _, ext := range exts {
			if filepath.Ext(strings.ToUpper(ext)) == fileExt {
				return true
			}
		}
		return false
	default:
		return f.Mode()&0111 != 0
	}
}

func (c *ProgramCollector) Run(dpchan chan<- *opentsdb.DataPoint) {
	if c.Interval == 0 {
		for {
			next := time.After(DefaultFreq)
			if err := c.runProgram(dpchan); err != nil {
				slog.Infoln(err)
			}
			<-next
			slog.Infoln("restarting", c.Path)
		}
	} else {
		for {
			next := time.After(c.Interval)
			c.runProgram(dpchan)
			<-next
		}
	}
}

func (c *ProgramCollector) Init() {
}

func (c *ProgramCollector) runProgram(dpchan chan<- *opentsdb.DataPoint) (progError error) {
	cmd := exec.Command(c.Path)
	pr, pw := io.Pipe()
	s := bufio.NewScanner(pr)
	cmd.Stdout = pw
	er, ew := io.Pipe()
	cmd.Stderr = ew
	if err := cmd.Start(); err != nil {
		return err
	}
	go func() {
		progError = cmd.Wait()
		pw.Close()
		ew.Close()
	}()
	go func() {
		es := bufio.NewScanner(er)
		for es.Scan() {
			line := strings.TrimSpace(es.Text())
			slog.Error(line)
		}
	}()
Loop:
	for s.Scan() {
		line := strings.TrimSpace(s.Text())
		sp := strings.Fields(line)
		if len(sp) < 3 {
			slog.Errorf("bad line in program %s: %s", c.Path, line)
			continue
		}
		ts, err := strconv.ParseInt(sp[1], 10, 64)
		if err != nil {
			slog.Errorf("bad timestamp in program %s: %s", c.Path, sp[1])
			continue
		}
		val, err := strconv.ParseInt(sp[2], 10, 64)
		if err != nil {
			slog.Errorf("bad value in program %s: %s", c.Path, sp[2])
			continue
		}
		dp := opentsdb.DataPoint{
			Metric:    sp[0],
			Timestamp: ts,
			Value:     val,
			Tags:      opentsdb.TagSet{"host": util.Hostname},
		}
		for _, tag := range sp[3:] {
			tags, err := opentsdb.ParseTags(tag)
			if v, ok := tags["host"]; ok && v == "" {
				delete(dp.Tags, "host")
			} else if err != nil {
				slog.Errorf("bad tag in program %s, metric %s: %v", c.Path, sp[0], tag)
				continue Loop
			} else {
				dp.Tags.Merge(tags)
			}
		}
		dpchan <- &dp
	}
	if err := s.Err(); err != nil {
		return err
	}
	return
}

func (c *ProgramCollector) Name() string {
	return c.Path
}
<|endoftext|>


<commit_before>package server_test

import (
	"bufio"
	"io"
	"net"
	"strings"
	"testing"

	"github.com/emersion/go-imap/server"
)

func testServerAuthenticated(t *testing.T) (s *server.Server, c net.Conn, scanner *bufio.Scanner) {
	s, c, scanner = testServerGreeted(t)

	io.WriteString(c, "a000 LOGIN username password\r\n")
	scanner.Scan() // OK response
	return
}

func TestSelect_Ok(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SELECT INBOX\r\n")

	got := map[string]bool{
		"OK": false,
		"FLAGS": false,
		"EXISTS": false,
		"RECENT": false,
		"UNSEEN": false,
		"PERMANENTFLAGS": false,
		"UIDNEXT": false,
		"UIDVALIDITY": false,
	}

	for scanner.Scan() {
		res := scanner.Text()

		if res == "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)" {
			got["FLAGS"] = true
		} else if res == "* 1 EXISTS" {
			got["EXISTS"] = true
		} else if res == "* 0 RECENT" {
			got["RECENT"] = true
		} else if strings.HasPrefix(res, "* OK [UNSEEN 0]") {
			got["UNSEEN"] = true
		} else if strings.HasPrefix(res, "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)]") {
			got["PERMANENTFLAGS"] = true
		} else if strings.HasPrefix(res, "* OK [UIDNEXT 7]") {
			got["UIDNEXT"] = true
		} else if strings.HasPrefix(res, "* OK [UIDVALIDITY 1]") {
			got["UIDVALIDITY"] = true
		} else if strings.HasPrefix(res, "a001 OK [READ-WRITE] ") {
			got["OK"] = true
			break
		} else {
			t.Fatal("Unexpected response:", res)
		}
	}

	for name, val := range got {
		if !val {
			t.Error("Did not got response:", name)
		}
	}
}

func TestSelect_ReadOnly(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 EXAMINE INBOX\r\n")

	gotOk := true
	for scanner.Scan() {
		res := scanner.Text()

		if strings.HasPrefix(res, "a001 OK [READ-ONLY]") {
			gotOk = true
			break
		}
	}

	if !gotOk {
		t.Error("Did not get a correct OK response")
	}
}

func TestSelect_No(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SELECT idontexist\r\n")

	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestCreate(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 CREATE test\r\n")
	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestDelete(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 CREATE test\r\n")
	scanner.Scan()

	io.WriteString(c, "a001 DELETE test\r\n")
	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestRename(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 CREATE test\r\n")
	scanner.Scan()

	io.WriteString(c, "a001 RENAME test test2\r\n")
	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestSubscribe(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SUBSCRIBE INBOX\r\n")
	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestUnsubscribe(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SUBSCRIBE INBOX\r\n")
	scanner.Scan()

	io.WriteString(c, "a001 UNSUBSCRIBE INBOX\r\n")
	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestList(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 LIST \"\" *\r\n")

	scanner.Scan()
	if scanner.Text() != "* LIST (\\Noinferiors) / INBOX" {
		t.Fatal("Invalid LIST response:", scanner.Text())
	}

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestList_Subscribed(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 LSUB \"\" *\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}

	io.WriteString(c, "a001 SUBSCRIBE INBOX\r\n")
	scanner.Scan()

	io.WriteString(c, "a001 LSUB \"\" *\r\n")

	scanner.Scan()
	if scanner.Text() != "* LSUB (\\Noinferiors) / INBOX" {
		t.Fatal("Invalid LIST response:", scanner.Text())
	}

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestStatus(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 STATUS INBOX (MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)\r\n")

	scanner.Scan()
	if scanner.Text() != "* STATUS INBOX (MESSAGES 1 RECENT 0 UIDNEXT 7 UIDVALIDITY 1 UNSEEN 0)" {
		t.Fatal("Invalid STATUS response:", scanner.Text())
	}

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND INBOX {80}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "From: Edward Snowden <root@nsa.gov>\r\n")
	io.WriteString(c, "To: Julian Assange <root@gchq.gov.uk>\r\n")
	io.WriteString(c, "\r\n")
	io.WriteString(c, "<3\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend_WithFlags(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND INBOX (\\Draft) {11}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "Hello World\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend_WithFlagsAndDate(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND INBOX (\\Draft) \"5-Nov-1984 13:37:00 -0700\" {11}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "Hello World\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}
<commit_msg>server: adds even more tests<commit_after>package server_test

import (
	"bufio"
	"io"
	"net"
	"strings"
	"testing"

	"github.com/emersion/go-imap/server"
)

func testServerAuthenticated(t *testing.T) (s *server.Server, c net.Conn, scanner *bufio.Scanner) {
	s, c, scanner = testServerGreeted(t)

	io.WriteString(c, "a000 LOGIN username password\r\n")
	scanner.Scan() // OK response
	return
}

func TestSelect_Ok(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SELECT INBOX\r\n")

	got := map[string]bool{
		"OK": false,
		"FLAGS": false,
		"EXISTS": false,
		"RECENT": false,
		"UNSEEN": false,
		"PERMANENTFLAGS": false,
		"UIDNEXT": false,
		"UIDVALIDITY": false,
	}

	for scanner.Scan() {
		res := scanner.Text()

		if res == "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)" {
			got["FLAGS"] = true
		} else if res == "* 1 EXISTS" {
			got["EXISTS"] = true
		} else if res == "* 0 RECENT" {
			got["RECENT"] = true
		} else if strings.HasPrefix(res, "* OK [UNSEEN 0]") {
			got["UNSEEN"] = true
		} else if strings.HasPrefix(res, "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)]") {
			got["PERMANENTFLAGS"] = true
		} else if strings.HasPrefix(res, "* OK [UIDNEXT 7]") {
			got["UIDNEXT"] = true
		} else if strings.HasPrefix(res, "* OK [UIDVALIDITY 1]") {
			got["UIDVALIDITY"] = true
		} else if strings.HasPrefix(res, "a001 OK [READ-WRITE] ") {
			got["OK"] = true
			break
		} else {
			t.Fatal("Unexpected response:", res)
		}
	}

	for name, val := range got {
		if !val {
			t.Error("Did not got response:", name)
		}
	}
}

func TestSelect_ReadOnly(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 EXAMINE INBOX\r\n")

	gotOk := true
	for scanner.Scan() {
		res := scanner.Text()

		if strings.HasPrefix(res, "a001 OK [READ-ONLY]") {
			gotOk = true
			break
		}
	}

	if !gotOk {
		t.Error("Did not get a correct OK response")
	}
}

func TestSelect_InvalidMailbox(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SELECT idontexist\r\n")

	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestSelect_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SELECT INBOX\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestCreate(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 CREATE test\r\n")
	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestCreate_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 CREATE test\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestDelete(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 CREATE test\r\n")
	scanner.Scan()

	io.WriteString(c, "a001 DELETE test\r\n")
	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestDelete_InvalidMailbox(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 DELETE test\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestDelete_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 DELETE INBOX\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestRename(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 CREATE test\r\n")
	scanner.Scan()

	io.WriteString(c, "a001 RENAME test test2\r\n")
	scanner.Scan()

	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestRename_InvalidMailbox(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 RENAME test test2\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestRename_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 RENAME test test2\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestSubscribe(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SUBSCRIBE INBOX\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}

	io.WriteString(c, "a001 SUBSCRIBE idontexist\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestSubscribe_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SUBSCRIBE INBOX\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestUnsubscribe(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 SUBSCRIBE INBOX\r\n")
	scanner.Scan()

	io.WriteString(c, "a001 UNSUBSCRIBE INBOX\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}

	io.WriteString(c, "a001 UNSUBSCRIBE idontexist\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestUnsubscribe_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 UNSUBSCRIBE INBOX\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestList(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 LIST \"\" *\r\n")

	scanner.Scan()
	if scanner.Text() != "* LIST (\\Noinferiors) / INBOX" {
		t.Fatal("Invalid LIST response:", scanner.Text())
	}

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestList_Subscribed(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 LSUB \"\" *\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}

	io.WriteString(c, "a001 SUBSCRIBE INBOX\r\n")
	scanner.Scan()

	io.WriteString(c, "a001 LSUB \"\" *\r\n")

	scanner.Scan()
	if scanner.Text() != "* LSUB (\\Noinferiors) / INBOX" {
		t.Fatal("Invalid LIST response:", scanner.Text())
	}

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestList_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 LIST \"\" *\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestStatus(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 STATUS INBOX (MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)\r\n")

	scanner.Scan()
	if scanner.Text() != "* STATUS INBOX (MESSAGES 1 RECENT 0 UIDNEXT 7 UIDVALIDITY 1 UNSEEN 0)" {
		t.Fatal("Invalid STATUS response:", scanner.Text())
	}

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestStatus_InvalidMailbox(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 STATUS idontexist (MESSAGES)\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestStatus_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 STATUS INBOX (MESSAGES)\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND INBOX {80}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "From: Edward Snowden <root@nsa.gov>\r\n")
	io.WriteString(c, "To: Julian Assange <root@gchq.gov.uk>\r\n")
	io.WriteString(c, "\r\n")
	io.WriteString(c, "<3\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend_WithFlags(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND INBOX (\\Draft) {11}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "Hello World\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend_WithFlagsAndDate(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND INBOX (\\Draft) \"5-Nov-1984 13:37:00 -0700\" {11}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "Hello World\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend_Selected(t *testing.T) {
	s, c, scanner := testServerSelected(t, true)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND INBOX {11}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "Hello World\r\n")

	scanner.Scan()
	if scanner.Text() != "* 2 EXISTS" {
		t.Fatal("Invalid untagged response:", scanner.Text())
	}

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 OK ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend_InvalidMailbox(t *testing.T) {
	s, c, scanner := testServerAuthenticated(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND idontexist {11}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "Hello World\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}

func TestAppend_NotAuthenticated(t *testing.T) {
	s, c, scanner := testServerGreeted(t)
	defer c.Close()
	defer s.Close()

	io.WriteString(c, "a001 APPEND INBOX {11}\r\n")
	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "+ ") {
		t.Fatal("Invalid continuation request:", scanner.Text())
	}

	io.WriteString(c, "Hello World\r\n")

	scanner.Scan()
	if !strings.HasPrefix(scanner.Text(), "a001 NO ") {
		t.Fatal("Invalid status response:", scanner.Text())
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// config.go contains configurations for flaky tests reporting

package main

import (
	"github.com/knative/test-infra/shared/prow"
)

const (
	// Builds to be analyzed, this is an arbitrary number
	buildsCount   = 10
	// Minimal number of results to be counted as valid results for each testcase, this is an arbitrary number
	requiredCount = 8
	// Don't do anything if found more than 1% tests flaky, this is an arbitrary number
	threshold     = 0.01

	org           = "knative"
)

var (
	jobConfigs = []JobConfig{
		{"ci-knative-serving-continuous", "serving", prow.PostsubmitJob}, // CI flow for serving repo
	}
	// Temporarily creating issues under "test-infra" for better management
	// TODO(chaodaiG): repo for issue same as the src of the test
	repoIssueMap = map[string]string{
		"serving": "test-infra",
	}
)<commit_msg>serving issues goes to serving (#630)<commit_after>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// config.go contains configurations for flaky tests reporting

package main

import (
	"github.com/knative/test-infra/shared/prow"
)

const (
	// Builds to be analyzed, this is an arbitrary number
	buildsCount = 10
	// Minimal number of results to be counted as valid results for each testcase, this is an arbitrary number
	requiredCount = 8
	// Don't do anything if found more than 1% tests flaky, this is an arbitrary number
	threshold = 0.01

	org = "knative"
)

var (
	jobConfigs = []JobConfig{
		{"ci-knative-serving-continuous", "serving", prow.PostsubmitJob}, // CI flow for serving repo
	}
	repoIssueMap = map[string]string{
		"serving": "serving",
	}
)
<|endoftext|>


<commit_before>// Implementation (work in progress) of the resequencing analysis pipeline used
// to teach the introductory NGS bioinformatics analysis course at SciLifeLab
// as described on this page:
// http://uppnex.se/twiki/do/view/Courses/NgsIntro1502/ResequencingAnalysis.html
// Prerequisites:
// - Samtools
// - BWA
// - Picard
// - GATK
// Install all tools except GATK like this on X/L/K/Ubuntu:
// sudo apt-get install samtools bwa picard-tools
// (GATK needs to be downloaded and installed manually from www.broadinstitute.org/gatk)
package main

import (
	"fmt"

	. "github.com/scipipe/scipipe"
	"github.com/scipipe/scipipe/components"
)

const (
	fastq_base_url = "http://bioinfo.perdanauniversity.edu.my/tein4ngs/ngspractice/"
	fastq_file_pat = "%s.ILLUMINA.low_coverage.4p_%s.fq"
	ref_base_url   = "http://ftp.ensembl.org/pub/release-75/fasta/homo_sapiens/dna/"
	ref_file       = "Homo_sapiens.GRCh37.75.dna.chromosome.17.fa"
	ref_file_gz    = "Homo_sapiens.GRCh37.75.dna.chromosome.17.fa.gz"
	vcf_base_url   = "http://ftp.1000genomes.ebi.ac.uk/vol1/ftp/phase1/analysis_results/integrated_call_sets/"
	vcf_file       = "ALL.chr17.integrated_phase1_v3.20101123.snps_indels_svs.genotypes.vcf.gz"
)

var (
	individuals = []string{"NA06984", "NA12489"}
	samples     = []string{"1", "2"}
)

func main() {

	InitLogDebug()

	// --------------------------------------------------------------------------------
	// Initialize pipeline runner
	// --------------------------------------------------------------------------------

	wf := NewWorkflow("resequencing_wf")

	// --------------------------------------------------------------------------------
	// Download Reference Genome
	// --------------------------------------------------------------------------------
	downloadRefCmd := "wget -O {o:outfile} " + ref_base_url + ref_file_gz
	downloadRef := wf.NewProc("download_ref", downloadRefCmd)
	downloadRef.SetPathStatic("outfile", ref_file_gz)

	// --------------------------------------------------------------------------------
	// Unzip ref file
	// --------------------------------------------------------------------------------
	ungzipRefCmd := "gunzip -c {i:in} > {o:out}"
	ungzipRef := wf.NewProc("ugzip_ref", ungzipRefCmd)
	ungzipRef.SetPathReplace("in", "out", ".gz", "")
	ungzipRef.In("in").Connect(downloadRef.Out("outfile"))

	// Create a FanOut so multiple downstream processes can read from the
	// ungzip process
	refFanOut := components.NewFanOut("ref_fanout")
	refFanOut.InFile.Connect(ungzipRef.Out("out"))
	wf.Add(refFanOut)

	// --------------------------------------------------------------------------------
	// Index Reference Genome
	// --------------------------------------------------------------------------------
	indexRef := wf.NewProc("index_ref", "bwa index -a bwtsw {i:index}; echo done > {o:done}")
	indexRef.SetPathExtend("index", "done", ".indexed")
	indexRef.In("index").Connect(refFanOut.Out("index_ref"))

	indexDoneFanOut := components.NewFanOut("indexdone_fanout")
	indexDoneFanOut.InFile.Connect(indexRef.Out("done"))
	wf.Add(indexDoneFanOut)

	// Create (multi-level) maps where we can gather outports from processes
	// for each for loop iteration and access them in the merge step later
	outPorts := map[string]map[string]map[string]*FilePort{}
	for _, indv := range individuals {
		outPorts[indv] = map[string]map[string]*FilePort{}
		for _, smpl := range samples {
			outPorts[indv][smpl] = map[string]*FilePort{}
			indv_smpl := "_" + indv + "_" + smpl
			// --------------------------------------------------------------------------------
			// Download FastQ component
			// --------------------------------------------------------------------------------
			file_name := fmt.Sprintf(fastq_file_pat, indv, smpl)
			downloadFastQCmd := "wget -O {o:fastq} " + fastq_base_url + file_name
			downloadFastQ := wf.NewProc("download_fastq"+indv_smpl, downloadFastQCmd)
			downloadFastQ.SetPathStatic("fastq", file_name)

			fastQFanOut := components.NewFanOut("fastq_fanout")
			fastQFanOut.InFile.Connect(downloadFastQ.Out("fastq"))
			wf.Add(fastQFanOut)

			// Save outPorts for later use
			outPorts[indv][smpl]["fastq"] = fastQFanOut.Out("merg")

			// --------------------------------------------------------------------------------
			// BWA Align
			// --------------------------------------------------------------------------------
			bwaAlignCmd := "bwa aln {i:ref} {i:fastq} > {o:sai} # {i:idxdone}"
			bwaAlign := wf.NewProc("bwa_aln"+indv_smpl, bwaAlignCmd)
			bwaAlign.SetPathExtend("fastq", "sai", ".sai")
			bwaAlign.In("ref").Connect(refFanOut.Out("bwa_aln_" + indv + "_" + smpl))
			bwaAlign.In("idxdone").Connect(indexDoneFanOut.Out("bwa_aln_" + indv + "_" + smpl))
			bwaAlign.In("fastq").Connect(fastQFanOut.Out("bwa_aln"))

			// Save outPorts for later use
			outPorts[indv][smpl]["sai"] = bwaAlign.Out("sai")
		}

		// --------------------------------------------------------------------------------
		// Merge
		// --------------------------------------------------------------------------------
		// This one is is needed so bwaMergecan take a proper parameter for
		// individual, which it uses to generate output paths
		indParamGen := components.NewStringGen(indv)
		wf.Add(indParamGen)

		// bwa sampe process
		bwaMergeCmd := "bwa sampe {i:ref} {i:sai1} {i:sai2} {i:fq1} {i:fq2} > {o:merged} # {i:refdone} {p:indv}"
		bwaMerge := wf.NewProc("merge_"+indv, bwaMergeCmd)
		bwaMerge.SetPathCustom("merged", func(t *SciTask) string { return fmt.Sprintf("%s.merged.sam", t.Params["indv"]) })
		bwaMerge.In("ref").Connect(refFanOut.Out("bwa_merge_" + indv))
		bwaMerge.In("refdone").Connect(indexDoneFanOut.Out("bwa_merge_" + indv))
		bwaMerge.In("sai1").Connect(outPorts[indv]["1"]["sai"])
		bwaMerge.In("sai2").Connect(outPorts[indv]["2"]["sai"])
		bwaMerge.In("fq1").Connect(outPorts[indv]["1"]["fastq"])
		bwaMerge.In("fq2").Connect(outPorts[indv]["2"]["fastq"])
		bwaMerge.PP("indv").Connect(indParamGen.Out)

		wf.ConnectLast(bwaMerge.Out("merged"))
	}

	// --------------------------------------------------------------------------------
	// Run pipeline
	// --------------------------------------------------------------------------------

	wf.Run()
}
<commit_msg>Fixing bug in resequencing workflow (proc names must be unique)<commit_after>// Implementation (work in progress) of the resequencing analysis pipeline used
// to teach the introductory NGS bioinformatics analysis course at SciLifeLab
// as described on this page:
// http://uppnex.se/twiki/do/view/Courses/NgsIntro1502/ResequencingAnalysis.html
// Prerequisites:
// - Samtools
// - BWA
// - Picard
// - GATK
// Install all tools except GATK like this on X/L/K/Ubuntu:
// sudo apt-get install samtools bwa picard-tools
// (GATK needs to be downloaded and installed manually from www.broadinstitute.org/gatk)
package main

import (
	"fmt"

	. "github.com/scipipe/scipipe"
	comp "github.com/scipipe/scipipe/components"
)

const (
	fastq_base_url = "http://bioinfo.perdanauniversity.edu.my/tein4ngs/ngspractice/"
	fastq_file_pat = "%s.ILLUMINA.low_coverage.4p_%s.fq"
	ref_base_url   = "http://ftp.ensembl.org/pub/release-75/fasta/homo_sapiens/dna/"
	ref_file       = "Homo_sapiens.GRCh37.75.dna.chromosome.17.fa"
	ref_file_gz    = "Homo_sapiens.GRCh37.75.dna.chromosome.17.fa.gz"
	vcf_base_url   = "http://ftp.1000genomes.ebi.ac.uk/vol1/ftp/phase1/analysis_results/integrated_call_sets/"
	vcf_file       = "ALL.chr17.integrated_phase1_v3.20101123.snps_indels_svs.genotypes.vcf.gz"
)

var (
	individuals = []string{"NA06984", "NA12489"}
	samples     = []string{"1", "2"}
)

func main() {

	// --------------------------------------------------------------------------------
	// Initialize pipeline runner
	// --------------------------------------------------------------------------------

	wf := NewWorkflow("resequencing_wf")

	// --------------------------------------------------------------------------------
	// Download Reference Genome
	// --------------------------------------------------------------------------------
	downloadRefCmd := "wget -O {o:outfile} " + ref_base_url + ref_file_gz
	downloadRef := wf.NewProc("download_ref", downloadRefCmd)
	downloadRef.SetPathStatic("outfile", ref_file_gz)

	// --------------------------------------------------------------------------------
	// Unzip ref file
	// --------------------------------------------------------------------------------
	ungzipRefCmd := "gunzip -c {i:in} > {o:out}"
	ungzipRef := wf.NewProc("ugzip_ref", ungzipRefCmd)
	ungzipRef.SetPathReplace("in", "out", ".gz", "")
	ungzipRef.In("in").Connect(downloadRef.Out("outfile"))

	// Create a FanOut so multiple downstream processes can read from the
	// ungzip process
	refFanOut := comp.NewFanOut("ref_fanout")
	refFanOut.InFile.Connect(ungzipRef.Out("out"))
	wf.Add(refFanOut)

	// --------------------------------------------------------------------------------
	// Index Reference Genome
	// --------------------------------------------------------------------------------
	indexRef := wf.NewProc("index_ref", "bwa index -a bwtsw {i:index}; echo done > {o:done}")
	indexRef.SetPathExtend("index", "done", ".indexed")
	indexRef.In("index").Connect(refFanOut.Out("index_ref"))

	indexDoneFanOut := comp.NewFanOut("indexdone_fanout")
	indexDoneFanOut.InFile.Connect(indexRef.Out("done"))
	wf.Add(indexDoneFanOut)

	// Create (multi-level) maps where we can gather outports from processes
	// for each for loop iteration and access them in the merge step later
	outPorts := map[string]map[string]map[string]*FilePort{}
	for _, indv := range individuals {
		outPorts[indv] = map[string]map[string]*FilePort{}
		for _, smpl := range samples {
			outPorts[indv][smpl] = map[string]*FilePort{}
			indv_smpl := "_" + indv + "_" + smpl
			// --------------------------------------------------------------------------------
			// Download FastQ component
			// --------------------------------------------------------------------------------
			file_name := fmt.Sprintf(fastq_file_pat, indv, smpl)
			downloadFastQCmd := "wget -O {o:fastq} " + fastq_base_url + file_name
			downloadFastQ := wf.NewProc("download_fastq"+indv_smpl, downloadFastQCmd)
			downloadFastQ.SetPathStatic("fastq", file_name)

			fastQFanOut := comp.NewFanOut("fastq_fanout" + indv_smpl)
			fastQFanOut.InFile.Connect(downloadFastQ.Out("fastq"))
			wf.Add(fastQFanOut)

			// Save outPorts for later use
			outPorts[indv][smpl]["fastq"] = fastQFanOut.Out("merg")

			// --------------------------------------------------------------------------------
			// BWA Align
			// --------------------------------------------------------------------------------
			bwaAlignCmd := "bwa aln {i:ref} {i:fastq} > {o:sai} # {i:idxdone}"
			bwaAlign := wf.NewProc("bwa_aln"+indv_smpl, bwaAlignCmd)
			bwaAlign.SetPathExtend("fastq", "sai", ".sai")
			bwaAlign.In("ref").Connect(refFanOut.Out("bwa_aln_" + indv + "_" + smpl))
			bwaAlign.In("idxdone").Connect(indexDoneFanOut.Out("bwa_aln_" + indv + "_" + smpl))
			bwaAlign.In("fastq").Connect(fastQFanOut.Out("bwa_aln"))

			// Save outPorts for later use
			outPorts[indv][smpl]["sai"] = bwaAlign.Out("sai")
		}

		// --------------------------------------------------------------------------------
		// Merge
		// --------------------------------------------------------------------------------
		// This one is is needed so bwaMergecan take a proper parameter for
		// individual, which it uses to generate output paths

		// bwa sampe process
		bwaMergeCmd := "bwa sampe {i:ref} {i:sai1} {i:sai2} {i:fq1} {i:fq2} > {o:merged} # {i:refdone}"
		bwaMerge := wf.NewProc("merge_"+indv, bwaMergeCmd)
		bwaMerge.SetPathCustom("merged", func(t *SciTask) string {
			indv := indv
			return fmt.Sprintf("%s.merged.sam", indv)
		})
		bwaMerge.In("ref").Connect(refFanOut.Out("bwa_merge_" + indv))
		bwaMerge.In("refdone").Connect(indexDoneFanOut.Out("bwa_merge_" + indv))
		bwaMerge.In("sai1").Connect(outPorts[indv]["1"]["sai"])
		bwaMerge.In("sai2").Connect(outPorts[indv]["2"]["sai"])
		bwaMerge.In("fq1").Connect(outPorts[indv]["1"]["fastq"])
		bwaMerge.In("fq2").Connect(outPorts[indv]["2"]["fastq"])

		wf.ConnectLast(bwaMerge.Out("merged"))
	}

	// --------------------------------------------------------------------------------
	// Run pipeline
	// --------------------------------------------------------------------------------

	wf.Run()
}
<|endoftext|>


<commit_before>package composition

import (
	"net/http"
)

// NegroniMiddleware is the middleware definition taken from
// https://github.com/urfave/negroni#handlers
type NegroniMiddleware interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
<commit_msg>removing negroni middleware<commit_after><|endoftext|>


<commit_before>package builder

import (
	"github.com/appc/spec/schema/types"
	"github.com/blablacar/cnt/log"
	"github.com/blablacar/cnt/spec"
	"github.com/ghodss/yaml"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

const EXEC_FILES = `
execute_files() {
  fdir=$1
  [ -d "$fdir" ] || return 0

  for file in $fdir; do
    [ -e "$file" ] && {
     [ -x "$file" ] || /cnt/bin/busybox chmod +x "$file"
    }
    echo -e "\e[1m\e[32mRunning script -> $file\e[0m"
    $file
  done
}`

const BUILD_SCRIPT = `#!/cnt/bin/busybox sh
set -x
set -e
export TARGET=$( dirname $0 )
export ROOTFS=%%ROOTFS%%
export TERM=xterm

` + EXEC_FILES + `

execute_files "$ROOTFS/cnt/runlevels/inherit-build-early"
execute_files "$TARGET/runlevels/build"
`

const BUILD_SCRIPT_LATE = `#!/cnt/bin/busybox sh
set -x
set -e
export TARGET=$( dirname $0 )
export ROOTFS=%%ROOTFS%%
export TERM=xterm

` + EXEC_FILES + `

execute_files "$TARGET/runlevels/build-late"
execute_files "$ROOTFS/cnt/runlevels/inherit-build-late"
`

const PRESTART = `#!/cnt/bin/busybox sh
set -x
set -e

BASEDIR=${0%/*}
CNT_PATH=/cnt

` + EXEC_FILES + `

execute_files ${CNT_PATH}/runlevels/prestart-early

${BASEDIR}/attributes-merger -i ${CNT_PATH}/attributes -e CONFD_OVERRIDE
export CONFD_DATA=$(cat attributes.json)
${BASEDIR}/confd -onetime -config-file=${CNT_PATH}/prestart/confd.toml

execute_files ${CNT_PATH}/runlevels/prestart-late
`

const PATH_MANIFEST = "/manifest"
const PATH_IMAGE_ACI = "/image.aci"
const PATH_ROOTFS = "/rootfs"
const PATH_TARGET = "/target"
const PATH_CNT = "/cnt"
const PATH_CNT_MANIFEST = "/cnt-manifest.yml"
const PATH_RUNLEVELS = "/runlevels"
const PATH_PRESTART_EARLY = "/prestart-early"
const PATH_PRESTART_LATE = "/prestart-late"
const PATH_INHERIT_BUILD_LATE = "/inherit-build-late"
const PATH_INHERIT_BUILD_EARLY = "/inherit-build-early"
const PATH_ATTRIBUTES = "/attributes"
const PATH_FILES = "/files"
const PATH_CONFD = "/confd"
const PATH_BUILD_LATE = "/build-late"
const PATH_BUILD_SETUP = "/build-setup"
const PATH_BUILD = "/build"
const PATH_CONFDOTD = "/conf.d"
const PATH_TEMPLATES = "/templates"

type Img struct {
	path     string
	target   string
	rootfs   string
	PodName  *spec.ACFullname
	manifest spec.AciManifest
	args     BuildArgs
}

func Version(nameAndVersion string) string {
	split := strings.Split(nameAndVersion, ":")
	if len(split) == 1 {
		return ""
	}
	return split[1]
}

func ShortNameId(name types.ACIdentifier) string {
	return strings.Split(string(name), "/")[1]
}

func ShortName(nameAndVersion string) string {
	return strings.Split(Name(nameAndVersion), "/")[1]
}

func Name(nameAndVersion string) string {
	return strings.Split(nameAndVersion, ":")[0]
}

////////////////////////////////////////////

func NewAciWithManifest(path string, args BuildArgs, manifest spec.AciManifest) (*Img, error) {
	log.Get().Debug("New aci", path, args, manifest)
	cnt, err := PrepAci(path, args)
	if err != nil {
		return nil, err
	}
	cnt.manifest = manifest
	return cnt, nil
}

func NewAci(path string, args BuildArgs) (*Img, error) {
	manifest, err := readManifest(path + PATH_CNT_MANIFEST)
	if err != nil {
		log.Get().Debug(path, PATH_CNT_MANIFEST+" does not exists")
		return nil, err
	}
	return NewAciWithManifest(path, args, *manifest)
}

func PrepAci(aciPath string, args BuildArgs) (*Img, error) {
	cnt := new(Img)
	cnt.args = args

	if fullPath, err := filepath.Abs(aciPath); err != nil {
		log.Get().Panic("Cannot get fullpath of project", err)
	} else {
		cnt.path = fullPath
		cnt.target = cnt.path + PATH_TARGET
		if args.TargetPath != "" {
			currentAbsDir, err := filepath.Abs(args.TargetPath + "/" + cnt.manifest.NameAndVersion.ShortName())
			if err != nil {
				log.Get().Panic("invalid target path")
			}
			cnt.target = currentAbsDir
		}
		cnt.rootfs = cnt.target + PATH_ROOTFS
	}
	return cnt, nil
}

//////////////////////////////////////////////////////////////////

func readManifest(manifestPath string) (*spec.AciManifest, error) {
	manifest := spec.AciManifest{Aci: spec.AciDefinition{}}

	source, err := ioutil.ReadFile(manifestPath)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal([]byte(source), &manifest)
	if err != nil {
		log.Get().Panic(err)
	}

	return &manifest, nil
}

func (i *Img) checkBuilt() {
	if _, err := os.Stat(i.target + PATH_IMAGE_ACI); os.IsNotExist(err) {
		if err := i.Build(); err != nil {
			log.Get().Panic("Cannot Install since build failed")
		}
	}
}
<commit_msg>fix 2, Missing /*<commit_after>package builder

import (
	"github.com/appc/spec/schema/types"
	"github.com/blablacar/cnt/log"
	"github.com/blablacar/cnt/spec"
	"github.com/ghodss/yaml"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

const EXEC_FILES = `
execute_files() {
  fdir=$1
  [ -d "$fdir" ] || return 0

  for file in $fdir/*; do
    [ -e "$file" ] && {
     [ -x "$file" ] || /cnt/bin/busybox chmod +x "$file"
    }
    echo -e "\e[1m\e[32mRunning script -> $file\e[0m"
    $file
  done
}`

const BUILD_SCRIPT = `#!/cnt/bin/busybox sh
set -x
set -e
export TARGET=$( dirname $0 )
export ROOTFS=%%ROOTFS%%
export TERM=xterm

` + EXEC_FILES + `

execute_files "$ROOTFS/cnt/runlevels/inherit-build-early"
execute_files "$TARGET/runlevels/build"
`

const BUILD_SCRIPT_LATE = `#!/cnt/bin/busybox sh
set -x
set -e
export TARGET=$( dirname $0 )
export ROOTFS=%%ROOTFS%%
export TERM=xterm

` + EXEC_FILES + `

execute_files "$TARGET/runlevels/build-late"
execute_files "$ROOTFS/cnt/runlevels/inherit-build-late"
`

const PRESTART = `#!/cnt/bin/busybox sh
set -x
set -e

BASEDIR=${0%/*}
CNT_PATH=/cnt

` + EXEC_FILES + `

execute_files ${CNT_PATH}/runlevels/prestart-early

${BASEDIR}/attributes-merger -i ${CNT_PATH}/attributes -e CONFD_OVERRIDE
export CONFD_DATA=$(cat attributes.json)
${BASEDIR}/confd -onetime -config-file=${CNT_PATH}/prestart/confd.toml

execute_files ${CNT_PATH}/runlevels/prestart-late
`

const PATH_MANIFEST = "/manifest"
const PATH_IMAGE_ACI = "/image.aci"
const PATH_ROOTFS = "/rootfs"
const PATH_TARGET = "/target"
const PATH_CNT = "/cnt"
const PATH_CNT_MANIFEST = "/cnt-manifest.yml"
const PATH_RUNLEVELS = "/runlevels"
const PATH_PRESTART_EARLY = "/prestart-early"
const PATH_PRESTART_LATE = "/prestart-late"
const PATH_INHERIT_BUILD_LATE = "/inherit-build-late"
const PATH_INHERIT_BUILD_EARLY = "/inherit-build-early"
const PATH_ATTRIBUTES = "/attributes"
const PATH_FILES = "/files"
const PATH_CONFD = "/confd"
const PATH_BUILD_LATE = "/build-late"
const PATH_BUILD_SETUP = "/build-setup"
const PATH_BUILD = "/build"
const PATH_CONFDOTD = "/conf.d"
const PATH_TEMPLATES = "/templates"

type Img struct {
	path     string
	target   string
	rootfs   string
	PodName  *spec.ACFullname
	manifest spec.AciManifest
	args     BuildArgs
}

func Version(nameAndVersion string) string {
	split := strings.Split(nameAndVersion, ":")
	if len(split) == 1 {
		return ""
	}
	return split[1]
}

func ShortNameId(name types.ACIdentifier) string {
	return strings.Split(string(name), "/")[1]
}

func ShortName(nameAndVersion string) string {
	return strings.Split(Name(nameAndVersion), "/")[1]
}

func Name(nameAndVersion string) string {
	return strings.Split(nameAndVersion, ":")[0]
}

////////////////////////////////////////////

func NewAciWithManifest(path string, args BuildArgs, manifest spec.AciManifest) (*Img, error) {
	log.Get().Debug("New aci", path, args, manifest)
	cnt, err := PrepAci(path, args)
	if err != nil {
		return nil, err
	}
	cnt.manifest = manifest
	return cnt, nil
}

func NewAci(path string, args BuildArgs) (*Img, error) {
	manifest, err := readManifest(path + PATH_CNT_MANIFEST)
	if err != nil {
		log.Get().Debug(path, PATH_CNT_MANIFEST+" does not exists")
		return nil, err
	}
	return NewAciWithManifest(path, args, *manifest)
}

func PrepAci(aciPath string, args BuildArgs) (*Img, error) {
	cnt := new(Img)
	cnt.args = args

	if fullPath, err := filepath.Abs(aciPath); err != nil {
		log.Get().Panic("Cannot get fullpath of project", err)
	} else {
		cnt.path = fullPath
		cnt.target = cnt.path + PATH_TARGET
		if args.TargetPath != "" {
			currentAbsDir, err := filepath.Abs(args.TargetPath + "/" + cnt.manifest.NameAndVersion.ShortName())
			if err != nil {
				log.Get().Panic("invalid target path")
			}
			cnt.target = currentAbsDir
		}
		cnt.rootfs = cnt.target + PATH_ROOTFS
	}
	return cnt, nil
}

//////////////////////////////////////////////////////////////////

func readManifest(manifestPath string) (*spec.AciManifest, error) {
	manifest := spec.AciManifest{Aci: spec.AciDefinition{}}

	source, err := ioutil.ReadFile(manifestPath)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal([]byte(source), &manifest)
	if err != nil {
		log.Get().Panic(err)
	}

	return &manifest, nil
}

func (i *Img) checkBuilt() {
	if _, err := os.Stat(i.target + PATH_IMAGE_ACI); os.IsNotExist(err) {
		if err := i.Build(); err != nil {
			log.Get().Panic("Cannot Install since build failed")
		}
	}
}
<|endoftext|>


<commit_before>// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//+build ignore

// gendoc creates the matrix, mat64 and cmat128 package doc comments.
package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"unicode/utf8"
)

var docs = template.Must(template.New("docs").Funcs(funcs).Parse(`{{define "common"}}// Generated by running
//  go generate github.com/gonum/matrix
// DO NOT EDIT.

// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package {{.Name}} provides {{.Provides}}
//
// Overview
//
// This section provides a quick overview of the {{.Name}} package. The following
// sections provide more in depth commentary.
//
{{.Overview}}
//{{end}}
{{define "interfaces"}}// The Matrix Interfaces
//
// The Matrix interface is the common link between the concrete types. The Matrix
// interface is defined by three functions: Dims, which returns the dimensions
// of the Matrix, At, which returns the element in the specified location, and
// T for returning a Transpose (discussed later). All of the concrete types can
// perform these behaviors and so implement the interface. Methods and functions
// are designed to use this interface, so in particular the method
//  func (m *Dense) Mul(a, b Matrix)
// constructs a *Dense from the result of a multiplication with any Matrix types,
// not just *Dense. Where more restrictive requirements must be met, there are also the
// Symmetric and Triangular interfaces. For example, in
//  func (s *SymDense) AddSym(a, b Symmetric)
// the Symmetric interface guarantees a symmetric result.
//
// Transposes
//
// The T method is used for transposition. For example, c.Mul(a.T(), b) computes
// c = a^T * b. The {{if .ExamplePackage}}{{.ExamplePackage}}{{else}}{{.Name}}{{end}} types implement this method using an implicit transpose —
// see the Transpose type for more details. Note that some operations have a
// transpose as part of their definition, as in *SymDense.SymOuterK.
//{{end}}
{{define "factorization"}}// Matrix Factorization
//
// Matrix factorizations, such as the LU decomposition, typically have their own
// specific data storage, and so are each implemented as a specific type. The
// factorization can be computed through a call to Factorize
//  var lu {{if .ExamplePackage}}{{.ExamplePackage}}{{else}}{{.Name}}{{end}}.LU
//  lu.Factorize(a)
// The elements of the factorization can be extracted through methods on the
// appropriate type, i.e. *TriDense.LFromLU and *TriDense.UFromLU. Alternatively,
// they can be used directly, as in *Dense.SolveLU. Some factorizations can be
// updated directly, without needing to update the original matrix and refactorize,
// as in *LU.RankOne.
//{{end}}
{{define "blas"}}// BLAS and LAPACK
//
// BLAS and LAPACK are the standard APIs for linear algebra routines. Many
// operations in {{if .Description}}{{.Description}}{{else}}{{.Name}}{{end}} are implemented using calls to the wrapper functions
// in gonum/blas/{{.BLAS|alts}} and gonum/lapack/{{.LAPACK|alts}}. By default, {{.BLAS|join "/"}} and
// {{.LAPACK|join "/"}} call the native Go implementations of the routines. Alternatively,
// it is possible to use C-based implementations of the APIs through the respective
// cgo packages and "Use" functions. The Go implementation of LAPACK makes calls
// through {{.BLAS|join "/"}}, so if a cgo BLAS implementation is registered, the {{.LAPACK|join "/"}}
// calls will be partially executed in Go and partially executed in C.
//{{end}}
{{define "switching"}}// Type Switching
//
// The Matrix abstraction enables efficiency as well as interoperability. Go's
// type reflection capabilities are used to choose the most efficient routine
// given the specific concrete types. For example, in
//  c.Mul(a, b)
// if a and b both implement RawMatrixer, that is, they can be represented as a
// {{.BLAS|alts}}.General, {{.BLAS|alts}}.Gemm (general matrix multiplication) is called, while
// instead if b is a RawSymmetricer {{.BLAS|alts}}.Symm is used (general-symmetric
// multiplication), and if b is a *Vector {{.BLAS|alts}}.Gemv is used.
//
// There are many possible type combinations and special cases. No specific guarantees
// are made about the performance of any method, and in particular, note that an
// abstract matrix type may be copied into a concrete type of the corresponding
// value. If there are specific special cases that are needed, please submit a
// pull-request or file an issue.
//{{end}}
{{define "invariants"}}// Invariants
//
// Matrix input arguments to functions are never directly modified. If an operation
// changes Matrix data, the mutated matrix will be the receiver of a function.
//
// For convenience, a matrix may be used as both a receiver and as an input, e.g.
//  a.Pow(a, 6)
//  v.SolveVec(a.T(), v)
// though in many cases this will cause an allocation (see Element Aliasing).
// An exception to this rule is Copy, which does not allow a.Copy(a.T()).
//{{end}}
{{define "aliasing"}}// Element Aliasing
//
// Most methods in {{if .Description}}{{.Description}}{{else}}{{.Name}}{{end}} modify receiver data. It is forbidden for the modified
// data region of the receiver to overlap the used data area of the input
// arguments. The exception to this rule is when the method receiver is equal to one
// of the input arguments, as in the a.Pow(a, 6) call above, or its implicit transpose.
//
// This prohibition is to help avoid subtle mistakes when the method needs to read
// from and write to the same data region. There are ways to make mistakes using the
// {{.Name}} API, and {{.Name}} functions will detect and complain about those.
// There are many ways to make mistakes by excursion from the {{.Name}} API via
// interaction with raw matrix values.
//
// If you need to read the rest of this section to understand the behavior of
// your program, you are being clever. Don't be clever. If you must be clever,
// {{.BLAS|join "/"}} and {{.LAPACK|join "/"}} may be used to call the behavior directly.
//
// {{if .Description}}{{.Description|sentence}}{{else}}{{.Name}}{{end}} will use the following rules to detect overlap between the receiver and one
// of the inputs:
//  - the input implements one of the Raw methods, and
//  - the Raw type matches that of the receiver, and
//  - the address ranges of the backing data slices overlap, and
//  - the strides differ or there is an overlap in the used data elements.
// If such an overlap is detected, the method will panic.
//
// The following cases will not panic:
//  - the data slices do not overlap,
//  - there is pointer identity between the receiver and input values after
//    the value has been untransposed if necessary.
//
// {{if .Description}}{{.Description|sentence}}{{else}}{{.Name}}{{end}} will not attempt to detect element overlap if the input does not implement a
// Raw method, or if the Raw method differs from that of the receiver except when a
// conversion has occurred through a {{.Name}} API function. Method behavior is undefined
// if there is undetected overlap.
//{{end}}`))

type Package struct {
	path string

	Name           string
	Provides       string
	Description    string
	ExamplePackage string
	Overview       string

	BLAS   []string
	LAPACK []string

	template string
}

var pkgs = []Package{
	{
		path: ".",

		Name:        "matrix",
		Description: "the matrix packages",
		Provides: `common error handling mechanisms for matrix operations
// in mat64 and cmat128.`,
		ExamplePackage: "mat64",

		Overview: `// matrix provides:
//  - Error type definitions
//  - Error recovery mechanisms
//  - Common constants used by mat64 and cmat128
//
// Errors
//
// The mat64 and cmat128 matrix packages share a common set of errors
// provided by matrix via the matrix.Error type.
//
// Errors are either returned directly or used as the parameter of a panic
// depending on the class of error encountered. Returned errors indicate
// that a call was not able to complete successfully while panics generally
// indicate a programmer or unrecoverable error.
//
// Examples of each type are found in the mat64 Solve methods, which find
// x such that A*x = b.
//
// An error value is returned from the function or method when the operation
// can meaningfully fail. The Solve operation cannot complete if A is
// singular. However, determining the singularity of A is most easily
// discovered during the Solve procedure itself and is a valid result from
// the operation, so in this case an error is returned.
//
// A function will panic when the input parameters are inappropriate for
// the function. In Solve, for example, the number of rows of each input
// matrix must be equal because of the rules of matrix multiplication.
// Similarly, for solving A*x = b, a non-zero receiver must have the same
// number of rows as A has columns and must have the same number of columns
// as b. In all cases where a function will panic, conditions that would
// lead to a panic can easily be checked prior to a call.
//
// Error Recovery
//
// When a matrix.Error is the parameter of a panic, the panic can be
// recovered by a Maybe function, which will then return the error.
// Panics that are not of type matrix.Error are re-panicked by the
// Maybe functions.`,
		BLAS:   []string{"blas64", "cblas128"},
		LAPACK: []string{"lapack64", "clapack128"},

		template: `{{template "common" .}}
{{template "invariants" .}}
{{template "aliasing" .}}
package {{.Name}}

// TODO(kortschak) Update docs to indicate the second special case; we
// will check for Vector/Dense overlap because vector extraction from
// a matrix is directly supported by the mat64 API via RowView and ColView.
`,
	},
	{
		path: "mat64",

		Name: "mat64",
		Provides: `implementations of float64 matrix structures and
// linear algebra operations on them.`,

		Overview: `// mat64 provides:
//  - Interfaces for Matrix classes (Matrix, Symmetric, Triangular)
//  - Concrete implementations (Dense, SymDense, TriDense)
//  - Methods and functions for using matrix data (Add, Trace, SymRankOne)
//  - Types for constructing and using matrix factorizations (QR, LU)
//
// A matrix may be constructed through the corresponding New function. If no
// backing array is provided the matrix will be initialized to all zeros.
//  // Allocate a zeroed array of size 3×5
//  zero := mat64.NewDense(3, 5, nil)
// If a backing data slice is provided, the matrix will have those elements.
// Matrices are all stored in row-major format.
//  // Generate a 6×6 matrix of random values.
//  data := make([]float64, 36)
//  for i := range data {
//		data[i] = rand.NormFloat64()
//  }
//  a := mat64.NewDense(6, 6, data)
//
// Operations involving matrix data are implemented as functions when the values
// of the matrix remain unchanged
//  tr := mat64.Trace(a)
// and are implemented as methods when the operation modifies the receiver.
//  zero.Copy(a)
//
// Receivers must be the correct size for the matrix operations, otherwise the
// operation will panic. As a special case for convenience, a zero-sized matrix
// will be modified to have the correct size, allocating data if necessary.
//  var c mat64.Dense // construct a new zero-sized matrix
//  c.Mul(a, a)       // c is automatically adjusted to be 6×6`,

		BLAS:   []string{"cblas128"},
		LAPACK: []string{"clapack128"},

		template: `{{template "common" .}}
{{template "interfaces" .}}
{{template "factorization" .}}
{{template "blas" .}}
{{template "switching" .}}
{{template "invariants" .}}
{{template "aliasing" .}}
// BUG(kortschak) Currently only RawMatrixer aliasing detection is supported.
//
package {{.Name}}

// TODO(kortschak) Update docs to indicate the second special case; we
// will check for Vector/Dense overlap because vector extraction from
// a matrix is directly supported by the mat64 API via RowView and ColView.
`,
	},
	{
		path: "cmat128",

		Name: "cmat128",
		Provides: `implementations of complex128 matrix structures and
// linear algebra operations on them.`,

		Overview: `// cmat128 provides:
//  - Interfaces for a complex Matrix`,

		BLAS:   []string{"cblas128"},
		LAPACK: []string{"clapack128"},

		template: `{{template "common" . }}
{{template "blas" .}}
{{template "switching" .}}
{{template "invariants" .}}
{{template "aliasing" .}}
package {{.Name}}

// TODO(kortschak) Update docs to indicate the second special case; we
// will check for Vector/Dense overlap because vector extraction from
// a matrix is directly supported by the mat64 API via RowView and ColView.
`,
	},
}

var funcs = template.FuncMap{
	"sentence": sentence,
	"alts":     alts,
	"join":     join,
}

// sentence converts a string to sentence case where the string is the prefix of the sentence.
func sentence(s string) string {
	if len(s) == 0 {
		return ""
	}
	_, size := utf8.DecodeRune([]byte(s))
	return strings.ToUpper(s[:size]) + s[size:]
}

// alts renders a []string as a glob alternatives list.
func alts(s []string) string {
	switch len(s) {
	case 0:
		return ""
	case 1:
		return s[0]
	default:
		return fmt.Sprintf("{%s}", strings.Join(s, ","))
	}
}

// join is strings.Join with the parameter order changed.
func join(sep string, s []string) string {
	return strings.Join(s, sep)
}

func main() {
	for _, pkg := range pkgs {
		t, err := template.Must(docs.Clone()).Parse(pkg.template)
		if err != nil {
			log.Fatalf("failed to parse template: %v", err)
		}
		file := filepath.Join(pkg.path, "doc.go")
		f, err := os.Create(file)
		if err != nil {
			log.Fatalf("failed to create %q: %v", file, err)
		}
		err = t.Execute(f, pkg)
		if err != nil {
			log.Fatalf("failed to execute template: %v", err)
		}
		f.Close()
	}
}
<commit_msg>Remove extra tab in docs<commit_after>// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//+build ignore

// gendoc creates the matrix, mat64 and cmat128 package doc comments.
package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"unicode/utf8"
)

var docs = template.Must(template.New("docs").Funcs(funcs).Parse(`{{define "common"}}// Generated by running
//  go generate github.com/gonum/matrix
// DO NOT EDIT.

// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package {{.Name}} provides {{.Provides}}
//
// Overview
//
// This section provides a quick overview of the {{.Name}} package. The following
// sections provide more in depth commentary.
//
{{.Overview}}
//{{end}}
{{define "interfaces"}}// The Matrix Interfaces
//
// The Matrix interface is the common link between the concrete types. The Matrix
// interface is defined by three functions: Dims, which returns the dimensions
// of the Matrix, At, which returns the element in the specified location, and
// T for returning a Transpose (discussed later). All of the concrete types can
// perform these behaviors and so implement the interface. Methods and functions
// are designed to use this interface, so in particular the method
//  func (m *Dense) Mul(a, b Matrix)
// constructs a *Dense from the result of a multiplication with any Matrix types,
// not just *Dense. Where more restrictive requirements must be met, there are also the
// Symmetric and Triangular interfaces. For example, in
//  func (s *SymDense) AddSym(a, b Symmetric)
// the Symmetric interface guarantees a symmetric result.
//
// Transposes
//
// The T method is used for transposition. For example, c.Mul(a.T(), b) computes
// c = a^T * b. The {{if .ExamplePackage}}{{.ExamplePackage}}{{else}}{{.Name}}{{end}} types implement this method using an implicit transpose —
// see the Transpose type for more details. Note that some operations have a
// transpose as part of their definition, as in *SymDense.SymOuterK.
//{{end}}
{{define "factorization"}}// Matrix Factorization
//
// Matrix factorizations, such as the LU decomposition, typically have their own
// specific data storage, and so are each implemented as a specific type. The
// factorization can be computed through a call to Factorize
//  var lu {{if .ExamplePackage}}{{.ExamplePackage}}{{else}}{{.Name}}{{end}}.LU
//  lu.Factorize(a)
// The elements of the factorization can be extracted through methods on the
// appropriate type, i.e. *TriDense.LFromLU and *TriDense.UFromLU. Alternatively,
// they can be used directly, as in *Dense.SolveLU. Some factorizations can be
// updated directly, without needing to update the original matrix and refactorize,
// as in *LU.RankOne.
//{{end}}
{{define "blas"}}// BLAS and LAPACK
//
// BLAS and LAPACK are the standard APIs for linear algebra routines. Many
// operations in {{if .Description}}{{.Description}}{{else}}{{.Name}}{{end}} are implemented using calls to the wrapper functions
// in gonum/blas/{{.BLAS|alts}} and gonum/lapack/{{.LAPACK|alts}}. By default, {{.BLAS|join "/"}} and
// {{.LAPACK|join "/"}} call the native Go implementations of the routines. Alternatively,
// it is possible to use C-based implementations of the APIs through the respective
// cgo packages and "Use" functions. The Go implementation of LAPACK makes calls
// through {{.BLAS|join "/"}}, so if a cgo BLAS implementation is registered, the {{.LAPACK|join "/"}}
// calls will be partially executed in Go and partially executed in C.
//{{end}}
{{define "switching"}}// Type Switching
//
// The Matrix abstraction enables efficiency as well as interoperability. Go's
// type reflection capabilities are used to choose the most efficient routine
// given the specific concrete types. For example, in
//  c.Mul(a, b)
// if a and b both implement RawMatrixer, that is, they can be represented as a
// {{.BLAS|alts}}.General, {{.BLAS|alts}}.Gemm (general matrix multiplication) is called, while
// instead if b is a RawSymmetricer {{.BLAS|alts}}.Symm is used (general-symmetric
// multiplication), and if b is a *Vector {{.BLAS|alts}}.Gemv is used.
//
// There are many possible type combinations and special cases. No specific guarantees
// are made about the performance of any method, and in particular, note that an
// abstract matrix type may be copied into a concrete type of the corresponding
// value. If there are specific special cases that are needed, please submit a
// pull-request or file an issue.
//{{end}}
{{define "invariants"}}// Invariants
//
// Matrix input arguments to functions are never directly modified. If an operation
// changes Matrix data, the mutated matrix will be the receiver of a function.
//
// For convenience, a matrix may be used as both a receiver and as an input, e.g.
//  a.Pow(a, 6)
//  v.SolveVec(a.T(), v)
// though in many cases this will cause an allocation (see Element Aliasing).
// An exception to this rule is Copy, which does not allow a.Copy(a.T()).
//{{end}}
{{define "aliasing"}}// Element Aliasing
//
// Most methods in {{if .Description}}{{.Description}}{{else}}{{.Name}}{{end}} modify receiver data. It is forbidden for the modified
// data region of the receiver to overlap the used data area of the input
// arguments. The exception to this rule is when the method receiver is equal to one
// of the input arguments, as in the a.Pow(a, 6) call above, or its implicit transpose.
//
// This prohibition is to help avoid subtle mistakes when the method needs to read
// from and write to the same data region. There are ways to make mistakes using the
// {{.Name}} API, and {{.Name}} functions will detect and complain about those.
// There are many ways to make mistakes by excursion from the {{.Name}} API via
// interaction with raw matrix values.
//
// If you need to read the rest of this section to understand the behavior of
// your program, you are being clever. Don't be clever. If you must be clever,
// {{.BLAS|join "/"}} and {{.LAPACK|join "/"}} may be used to call the behavior directly.
//
// {{if .Description}}{{.Description|sentence}}{{else}}{{.Name}}{{end}} will use the following rules to detect overlap between the receiver and one
// of the inputs:
//  - the input implements one of the Raw methods, and
//  - the Raw type matches that of the receiver, and
//  - the address ranges of the backing data slices overlap, and
//  - the strides differ or there is an overlap in the used data elements.
// If such an overlap is detected, the method will panic.
//
// The following cases will not panic:
//  - the data slices do not overlap,
//  - there is pointer identity between the receiver and input values after
//    the value has been untransposed if necessary.
//
// {{if .Description}}{{.Description|sentence}}{{else}}{{.Name}}{{end}} will not attempt to detect element overlap if the input does not implement a
// Raw method, or if the Raw method differs from that of the receiver except when a
// conversion has occurred through a {{.Name}} API function. Method behavior is undefined
// if there is undetected overlap.
//{{end}}`))

type Package struct {
	path string

	Name           string
	Provides       string
	Description    string
	ExamplePackage string
	Overview       string

	BLAS   []string
	LAPACK []string

	template string
}

var pkgs = []Package{
	{
		path: ".",

		Name:        "matrix",
		Description: "the matrix packages",
		Provides: `common error handling mechanisms for matrix operations
// in mat64 and cmat128.`,
		ExamplePackage: "mat64",

		Overview: `// matrix provides:
//  - Error type definitions
//  - Error recovery mechanisms
//  - Common constants used by mat64 and cmat128
//
// Errors
//
// The mat64 and cmat128 matrix packages share a common set of errors
// provided by matrix via the matrix.Error type.
//
// Errors are either returned directly or used as the parameter of a panic
// depending on the class of error encountered. Returned errors indicate
// that a call was not able to complete successfully while panics generally
// indicate a programmer or unrecoverable error.
//
// Examples of each type are found in the mat64 Solve methods, which find
// x such that A*x = b.
//
// An error value is returned from the function or method when the operation
// can meaningfully fail. The Solve operation cannot complete if A is
// singular. However, determining the singularity of A is most easily
// discovered during the Solve procedure itself and is a valid result from
// the operation, so in this case an error is returned.
//
// A function will panic when the input parameters are inappropriate for
// the function. In Solve, for example, the number of rows of each input
// matrix must be equal because of the rules of matrix multiplication.
// Similarly, for solving A*x = b, a non-zero receiver must have the same
// number of rows as A has columns and must have the same number of columns
// as b. In all cases where a function will panic, conditions that would
// lead to a panic can easily be checked prior to a call.
//
// Error Recovery
//
// When a matrix.Error is the parameter of a panic, the panic can be
// recovered by a Maybe function, which will then return the error.
// Panics that are not of type matrix.Error are re-panicked by the
// Maybe functions.`,
		BLAS:   []string{"blas64", "cblas128"},
		LAPACK: []string{"lapack64", "clapack128"},

		template: `{{template "common" .}}
{{template "invariants" .}}
{{template "aliasing" .}}
package {{.Name}}

// TODO(kortschak) Update docs to indicate the second special case; we
// will check for Vector/Dense overlap because vector extraction from
// a matrix is directly supported by the mat64 API via RowView and ColView.
`,
	},
	{
		path: "mat64",

		Name: "mat64",
		Provides: `implementations of float64 matrix structures and
// linear algebra operations on them.`,

		Overview: `// mat64 provides:
//  - Interfaces for Matrix classes (Matrix, Symmetric, Triangular)
//  - Concrete implementations (Dense, SymDense, TriDense)
//  - Methods and functions for using matrix data (Add, Trace, SymRankOne)
//  - Types for constructing and using matrix factorizations (QR, LU)
//
// A matrix may be constructed through the corresponding New function. If no
// backing array is provided the matrix will be initialized to all zeros.
//  // Allocate a zeroed array of size 3×5
//  zero := mat64.NewDense(3, 5, nil)
// If a backing data slice is provided, the matrix will have those elements.
// Matrices are all stored in row-major format.
//  // Generate a 6×6 matrix of random values.
//  data := make([]float64, 36)
//  for i := range data {
//  	data[i] = rand.NormFloat64()
//  }
//  a := mat64.NewDense(6, 6, data)
//
// Operations involving matrix data are implemented as functions when the values
// of the matrix remain unchanged
//  tr := mat64.Trace(a)
// and are implemented as methods when the operation modifies the receiver.
//  zero.Copy(a)
//
// Receivers must be the correct size for the matrix operations, otherwise the
// operation will panic. As a special case for convenience, a zero-sized matrix
// will be modified to have the correct size, allocating data if necessary.
//  var c mat64.Dense // construct a new zero-sized matrix
//  c.Mul(a, a)       // c is automatically adjusted to be 6×6`,

		BLAS:   []string{"cblas128"},
		LAPACK: []string{"clapack128"},

		template: `{{template "common" .}}
{{template "interfaces" .}}
{{template "factorization" .}}
{{template "blas" .}}
{{template "switching" .}}
{{template "invariants" .}}
{{template "aliasing" .}}
// BUG(kortschak) Currently only RawMatrixer aliasing detection is supported.
//
package {{.Name}}

// TODO(kortschak) Update docs to indicate the second special case; we
// will check for Vector/Dense overlap because vector extraction from
// a matrix is directly supported by the mat64 API via RowView and ColView.
`,
	},
	{
		path: "cmat128",

		Name: "cmat128",
		Provides: `implementations of complex128 matrix structures and
// linear algebra operations on them.`,

		Overview: `// cmat128 provides:
//  - Interfaces for a complex Matrix`,

		BLAS:   []string{"cblas128"},
		LAPACK: []string{"clapack128"},

		template: `{{template "common" . }}
{{template "blas" .}}
{{template "switching" .}}
{{template "invariants" .}}
{{template "aliasing" .}}
package {{.Name}}

// TODO(kortschak) Update docs to indicate the second special case; we
// will check for Vector/Dense overlap because vector extraction from
// a matrix is directly supported by the mat64 API via RowView and ColView.
`,
	},
}

var funcs = template.FuncMap{
	"sentence": sentence,
	"alts":     alts,
	"join":     join,
}

// sentence converts a string to sentence case where the string is the prefix of the sentence.
func sentence(s string) string {
	if len(s) == 0 {
		return ""
	}
	_, size := utf8.DecodeRune([]byte(s))
	return strings.ToUpper(s[:size]) + s[size:]
}

// alts renders a []string as a glob alternatives list.
func alts(s []string) string {
	switch len(s) {
	case 0:
		return ""
	case 1:
		return s[0]
	default:
		return fmt.Sprintf("{%s}", strings.Join(s, ","))
	}
}

// join is strings.Join with the parameter order changed.
func join(sep string, s []string) string {
	return strings.Join(s, sep)
}

func main() {
	for _, pkg := range pkgs {
		t, err := template.Must(docs.Clone()).Parse(pkg.template)
		if err != nil {
			log.Fatalf("failed to parse template: %v", err)
		}
		file := filepath.Join(pkg.path, "doc.go")
		f, err := os.Create(file)
		if err != nil {
			log.Fatalf("failed to create %q: %v", file, err)
		}
		err = t.Execute(f, pkg)
		if err != nil {
			log.Fatalf("failed to execute template: %v", err)
		}
		f.Close()
	}
}
<|endoftext|>