<commit_before>package logbuf

import (
	"bufio"
	"fmt"
	"github.com/Symantec/Dominator/lib/url"
	"io"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"
)

type countingWriter struct {
	count      uint64
	writer     io.Writer
	prefixLine string
}

func (w *countingWriter) Write(p []byte) (n int, err error) {
	if w.prefixLine != "" {
		w.writer.Write([]byte(w.prefixLine))
		w.prefixLine = ""
	}
	n, err = w.writer.Write(p)
	if n > 0 {
		w.count += uint64(n)
	}
	return
}

func (lb *LogBuffer) addHttpHandlers() {
	http.HandleFunc("/logs", lb.httpListHandler)
	http.HandleFunc("/logs/dump", lb.httpDumpHandler)
	http.HandleFunc("/logs/showLast", lb.httpShowLastHandler)
}

func (lb *LogBuffer) httpListHandler(w http.ResponseWriter, req *http.Request) {
	if lb.logDir == "" {
		return
	}
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	parsedQuery := url.ParseQuery(req.URL)
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	names, err := lb.list(recentFirst)
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	recentFirstString := ""
	if recentFirst {
		recentFirstString = "&recentFirst"
	}
	if parsedQuery.OutputType() == url.OutputTypeText {
		for _, name := range names {
			fmt.Fprintln(writer, name)
		}
		return
	}
	fmt.Fprintln(writer, "")
	fmt.Fprint(writer, "Logs: ")
	if recentFirst {
		fmt.Fprintf(writer, "showing recent first ")
		fmt.Fprintln(writer, `<a href="logs">show recent last</a><br>`)
	} else {
		fmt.Fprintf(writer, "showing recent last ")
		fmt.Fprintln(writer,
			`<a href="logs?recentFirst">show recent first</a><br>`)
	}
	showRecentLinks(writer, recentFirstString)
	fmt.Fprintln(writer, "
")
	currentName := ""
	lb.rwMutex.Lock()
	if lb.file != nil {
		currentName = path.Base(lb.file.Name())
	}
	lb.rwMutex.Unlock()
	if recentFirst {
		fmt.Fprintf(writer,
			"current
\n",
			recentFirstString)
	}
	for _, name := range names {
		if name == currentName {
			fmt.Fprintf(writer,
				"%s (current)
\n",
				name, recentFirstString, name)
		} else {
			fmt.Fprintf(writer, "%s
\n",
				name, recentFirstString, name)
		}
	}
	if !recentFirst {
		fmt.Fprintf(writer,
			"current
\n",
			recentFirstString)
	}
	fmt.Fprintln(writer, "")
}

func showRecentLinks(w io.Writer, recentFirstString string) {
	fmt.Fprintf(w, "Show last: minute\n",
		recentFirstString)
	fmt.Fprintf(w, "           10 min\n",
		recentFirstString)
	fmt.Fprintf(w, "           hour\n",
		recentFirstString)
	fmt.Fprintf(w, "           day\n",
		recentFirstString)
	fmt.Fprintf(w, "           week\n",
		recentFirstString)
}

func (lb *LogBuffer) httpDumpHandler(w http.ResponseWriter, req *http.Request) {
	parsedQuery := url.ParseQuery(req.URL)
	name, ok := parsedQuery.Table["name"]
	if !ok {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	if name == "latest" {
		lbFilename := ""
		lb.rwMutex.Lock()
		if lb.file != nil {
			lbFilename = lb.file.Name()
		}
		lb.rwMutex.Unlock()
		if lbFilename == "" {
			writer := bufio.NewWriter(w)
			defer writer.Flush()
			lb.Dump(writer, "", "", recentFirst)
			return
		}
		name = path.Base(lbFilename)
	}
	file, err := os.Open(path.Join(lb.logDir, path.Base(path.Clean(name))))
	if err != nil {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		return
	}
	defer file.Close()
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	if recentFirst {
		scanner := bufio.NewScanner(file)
		lines := make([]string, 0)
		for scanner.Scan() {
			line := scanner.Text()
			if len(line) < 1 {
				continue
			}
			lines = append(lines, line)
		}
		if err = scanner.Err(); err == nil {
			reverseStrings(lines)
			for _, line := range lines {
				fmt.Fprintln(writer, line)
			}
		}
	} else {
		_, err = io.Copy(writer, bufio.NewReader(file))
	}
	if err != nil {
		fmt.Fprintln(writer, err)
	}
	return
}

func (lb *LogBuffer) httpShowLastHandler(w http.ResponseWriter,
	req *http.Request) {
	parsedQuery := url.ParseQuery(req.URL)
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	for flag := range parsedQuery.Flags {
		length := len(flag)
		if length < 2 {
			continue
		}
		unitChar := flag[length-1]
		var unit time.Duration
		switch unitChar {
		case 's':
			unit = time.Second
		case 'm':
			unit = time.Minute
		case 'h':
			unit = time.Hour
		case 'd':
			unit = time.Hour * 24
		case 'w':
			unit = time.Hour * 24 * 7
		default:
			continue
		}
		if val, err := strconv.ParseUint(flag[:length-1], 10, 64); err != nil {
			w.Header().Set("Content-Type", "text/plain; charset=utf-8")
			w.WriteHeader(http.StatusBadRequest)
			return
		} else {
			lb.showRecent(w, time.Duration(val)*unit, recentFirst)
			return
		}
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusBadRequest)
}

func (lb *LogBuffer) showRecent(w io.Writer, duration time.Duration,
	recentFirst bool) {
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	names, err := lb.list(true)
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	earliestTime := time.Now().Add(-duration)
	// Get a list of names which may be recent enough.
	tmpNames := make([]string, 0, len(names))
	for _, name := range names {
		startTime, err := time.ParseInLocation(timeLayout, name, time.Local)
		if err != nil {
			continue
		}
		tmpNames = append(tmpNames, name)
		if startTime.Before(earliestTime) {
			break
		}
	}
	names = tmpNames
	if !recentFirst {
		reverseStrings(names)
	}
	fmt.Fprintln(writer, "")
	cWriter := &countingWriter{writer: writer}
	lb.rwMutex.Lock()
	lb.writer.Flush()
	lb.rwMutex.Unlock()
	for _, name := range names {
		cWriter.count = 0
		lb.dumpSince(cWriter, name, earliestTime, "", "
\n", recentFirst)
		if cWriter.count > 0 {
			cWriter.prefixLine = "
\n"
		}
	}
	fmt.Fprintln(writer, "")
}

func (lb *LogBuffer) list(recentFirst bool) ([]string, error) {
	file, err := os.Open(lb.logDir)
	if err != nil {
		return nil, err
	}
	names, err := file.Readdirnames(-1)
	file.Close()
	if err != nil {
		return nil, err
	}
	tmpNames := make([]string, 0, len(names))
	for _, name := range names {
		if strings.Count(name, ":") == 3 {
			tmpNames = append(tmpNames, name)
		}
	}
	names = tmpNames
	sort.Strings(names)
	if recentFirst {
		reverseStrings(names)
	}
	return names, nil
}

func (lb *LogBuffer) writeHtml(writer io.Writer) {
	fmt.Fprintln(writer, `<a href="logs">Logs:</a><br>`)
	fmt.Fprintln(writer, "
")
	lb.Dump(writer, "", "", false)
	fmt.Fprintln(writer, "
")
}
<commit_msg>Replace open-coded flush with lib/logbuf.LogBuffer.flush() method.<commit_after>package logbuf

import (
	"bufio"
	"fmt"
	"github.com/Symantec/Dominator/lib/url"
	"io"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"
)

type countingWriter struct {
	count      uint64
	writer     io.Writer
	prefixLine string
}

func (w *countingWriter) Write(p []byte) (n int, err error) {
	if w.prefixLine != "" {
		w.writer.Write([]byte(w.prefixLine))
		w.prefixLine = ""
	}
	n, err = w.writer.Write(p)
	if n > 0 {
		w.count += uint64(n)
	}
	return
}

func (lb *LogBuffer) addHttpHandlers() {
	http.HandleFunc("/logs", lb.httpListHandler)
	http.HandleFunc("/logs/dump", lb.httpDumpHandler)
	http.HandleFunc("/logs/showLast", lb.httpShowLastHandler)
}

func (lb *LogBuffer) httpListHandler(w http.ResponseWriter, req *http.Request) {
	if lb.logDir == "" {
		return
	}
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	parsedQuery := url.ParseQuery(req.URL)
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	names, err := lb.list(recentFirst)
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	recentFirstString := ""
	if recentFirst {
		recentFirstString = "&recentFirst"
	}
	if parsedQuery.OutputType() == url.OutputTypeText {
		for _, name := range names {
			fmt.Fprintln(writer, name)
		}
		return
	}
	fmt.Fprintln(writer, "")
	fmt.Fprint(writer, "Logs: ")
	if recentFirst {
		fmt.Fprintf(writer, "showing recent first ")
		fmt.Fprintln(writer, `<a href="logs">show recent last</a><br>`)
	} else {
		fmt.Fprintf(writer, "showing recent last ")
		fmt.Fprintln(writer,
			`<a href="logs?recentFirst">show recent first</a><br>`)
	}
	showRecentLinks(writer, recentFirstString)
	fmt.Fprintln(writer, "
")
	currentName := ""
	lb.rwMutex.Lock()
	if lb.file != nil {
		currentName = path.Base(lb.file.Name())
	}
	lb.rwMutex.Unlock()
	if recentFirst {
		fmt.Fprintf(writer,
			"current
\n",
			recentFirstString)
	}
	for _, name := range names {
		if name == currentName {
			fmt.Fprintf(writer,
				"%s (current)
\n",
				name, recentFirstString, name)
		} else {
			fmt.Fprintf(writer, "%s
\n",
				name, recentFirstString, name)
		}
	}
	if !recentFirst {
		fmt.Fprintf(writer,
			"current
\n",
			recentFirstString)
	}
	fmt.Fprintln(writer, "")
}

func showRecentLinks(w io.Writer, recentFirstString string) {
	fmt.Fprintf(w, "Show last: minute\n",
		recentFirstString)
	fmt.Fprintf(w, "           10 min\n",
		recentFirstString)
	fmt.Fprintf(w, "           hour\n",
		recentFirstString)
	fmt.Fprintf(w, "           day\n",
		recentFirstString)
	fmt.Fprintf(w, "           week\n",
		recentFirstString)
}

func (lb *LogBuffer) httpDumpHandler(w http.ResponseWriter, req *http.Request) {
	parsedQuery := url.ParseQuery(req.URL)
	name, ok := parsedQuery.Table["name"]
	if !ok {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	if name == "latest" {
		lbFilename := ""
		lb.rwMutex.Lock()
		if lb.file != nil {
			lbFilename = lb.file.Name()
		}
		lb.rwMutex.Unlock()
		if lbFilename == "" {
			writer := bufio.NewWriter(w)
			defer writer.Flush()
			lb.Dump(writer, "", "", recentFirst)
			return
		}
		name = path.Base(lbFilename)
	}
	file, err := os.Open(path.Join(lb.logDir, path.Base(path.Clean(name))))
	if err != nil {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		return
	}
	defer file.Close()
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	if recentFirst {
		scanner := bufio.NewScanner(file)
		lines := make([]string, 0)
		for scanner.Scan() {
			line := scanner.Text()
			if len(line) < 1 {
				continue
			}
			lines = append(lines, line)
		}
		if err = scanner.Err(); err == nil {
			reverseStrings(lines)
			for _, line := range lines {
				fmt.Fprintln(writer, line)
			}
		}
	} else {
		_, err = io.Copy(writer, bufio.NewReader(file))
	}
	if err != nil {
		fmt.Fprintln(writer, err)
	}
	return
}

func (lb *LogBuffer) httpShowLastHandler(w http.ResponseWriter,
	req *http.Request) {
	parsedQuery := url.ParseQuery(req.URL)
	_, recentFirst := parsedQuery.Flags["recentFirst"]
	for flag := range parsedQuery.Flags {
		length := len(flag)
		if length < 2 {
			continue
		}
		unitChar := flag[length-1]
		var unit time.Duration
		switch unitChar {
		case 's':
			unit = time.Second
		case 'm':
			unit = time.Minute
		case 'h':
			unit = time.Hour
		case 'd':
			unit = time.Hour * 24
		case 'w':
			unit = time.Hour * 24 * 7
		default:
			continue
		}
		if val, err := strconv.ParseUint(flag[:length-1], 10, 64); err != nil {
			w.Header().Set("Content-Type", "text/plain; charset=utf-8")
			w.WriteHeader(http.StatusBadRequest)
			return
		} else {
			lb.showRecent(w, time.Duration(val)*unit, recentFirst)
			return
		}
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusBadRequest)
}

func (lb *LogBuffer) showRecent(w io.Writer, duration time.Duration,
	recentFirst bool) {
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	names, err := lb.list(true)
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	earliestTime := time.Now().Add(-duration)
	// Get a list of names which may be recent enough.
	tmpNames := make([]string, 0, len(names))
	for _, name := range names {
		startTime, err := time.ParseInLocation(timeLayout, name, time.Local)
		if err != nil {
			continue
		}
		tmpNames = append(tmpNames, name)
		if startTime.Before(earliestTime) {
			break
		}
	}
	names = tmpNames
	if !recentFirst {
		reverseStrings(names)
	}
	fmt.Fprintln(writer, "")
	cWriter := &countingWriter{writer: writer}
	lb.flush()
	for _, name := range names {
		cWriter.count = 0
		lb.dumpSince(cWriter, name, earliestTime, "", "
\n", recentFirst)
		if cWriter.count > 0 {
			cWriter.prefixLine = "
\n"
		}
	}
	fmt.Fprintln(writer, "")
}

func (lb *LogBuffer) list(recentFirst bool) ([]string, error) {
	file, err := os.Open(lb.logDir)
	if err != nil {
		return nil, err
	}
	names, err := file.Readdirnames(-1)
	file.Close()
	if err != nil {
		return nil, err
	}
	tmpNames := make([]string, 0, len(names))
	for _, name := range names {
		if strings.Count(name, ":") == 3 {
			tmpNames = append(tmpNames, name)
		}
	}
	names = tmpNames
	sort.Strings(names)
	if recentFirst {
		reverseStrings(names)
	}
	return names, nil
}

func (lb *LogBuffer) writeHtml(writer io.Writer) {
	fmt.Fprintln(writer, `<a href="logs">Logs:</a><br>`)
	fmt.Fprintln(writer, "
")
	lb.Dump(writer, "", "", false)
	fmt.Fprintln(writer, "
")
}
<|endoftext|>


<commit_before>package svfs

import (
	"bytes"
	"encoding/hex"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/xlucas/swift"
	"golang.org/x/net/context"
)

const (
	objContentType        = "application/octet-stream"
	autoContentHeader     = "X-Detect-Content-Type"
	manifestHeader        = "X-Object-Manifest"
	objectMetaHeader      = "X-Object-Meta-"
	objectMetaHeaderXattr = objectMetaHeader + "Xattr-"
)

var (
	objectMtimeHeader = objectMetaHeader + "Mtime"
	segmentPathRegex  = regexp.MustCompile("^([^/]+)/(.*)$")
)

// Object is a node representing a swift object.
// It belongs to a container and segmented objects
// are bound to a container of segments.
type Object struct {
	name      string
	path      string
	so        *swift.Object
	sh        swift.Headers
	c         *swift.Container
	cs        *swift.Container
	p         *Directory
	m         sync.Mutex
	segmented bool
	writing   bool
}

// Attr fills the file attributes for an object node.
func (o *Object) Attr(ctx context.Context, a *fuse.Attr) (err error) {
	a.Size = o.size()
	a.BlockSize = uint32(BlockSize)
	a.Blocks = (a.Size / uint64(a.BlockSize)) * 8
	a.Mode = os.FileMode(DefaultMode)
	a.Gid = uint32(DefaultGID)
	a.Uid = uint32(DefaultUID)
	a.Mtime = getMtime(o.so, o.sh)
	a.Ctime = a.Mtime
	a.Crtime = a.Mtime
	return nil
}

// Getxattr retrieves extended attributes of an object node.
func (o *Object) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {
	if !Xattr {
		return fuse.ENOTSUP
	}

	key := canonicalHeaderKey(objectMetaHeaderXattr + req.Name)
	value, err := hex.DecodeString(o.sh[key])
	if err != nil {
		return err
	}

	resp.Xattr = []byte(value)
	return nil
}

// Export converts this object node as a direntry.
func (o *Object) Export() fuse.Dirent {
	return fuse.Dirent{
		Name: o.Name(),
		Type: fuse.DT_File,
	}
}

// Listxattr lists extended attributes associated with this object node.
func (o *Object) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
	var keys []string

	if !Xattr {
		return fuse.ENOTSUP
	}

	for k := range o.sh.ObjectMetadataXattr().Headers(objectMetaHeaderXattr) {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	for _, key := range keys {
		resp.Append(strings.TrimPrefix(key, objectMetaHeaderXattr))
	}

	return nil
}

// Open returns the file handle associated with this object node.
func (o *Object) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	return o.open(req.Flags, &resp.Flags)
}

// Fsync synchronizes a file's in-core state with the storage device.
// This is a no-op since we are in a network, fully synchronous, filesystem.
func (o *Object) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
	return nil
}

// Removexattr removes an extended attribute on this object node.
func (o *Object) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
	if !Xattr {
		return fuse.ENOTSUP
	}

	if _, ok := o.sh[objectMetaHeaderXattr+req.Name]; ok {
		if o.writing {
			o.m.Lock()
			defer o.m.Unlock()
		}
		key := canonicalHeaderKey(objectMetaHeaderXattr + req.Name)
		h := o.sh.ObjectMetadataXattr().Headers(objectMetaHeaderXattr)
		delete(h, key)
		delete(o.sh, key)
		if o.segmented {
			return SwiftConnection.ManifestUpdate(o.c.Name, o.so.Name, h)
		}
		return SwiftConnection.ObjectUpdate(o.c.Name, o.so.Name, h)
	}

	return nil
}

// Setattr changes file attributes on the current node.
func (o *Object) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
	// Change file size. Depending on the plaform, it may notably
	// be used by the kernel to truncate files instead of opening
	// them with O_TRUNC flag.
	if req.Valid.Size() {
		o.so.Bytes = int64(req.Size)
		if req.Size == 0 && o.segmented {
			return o.removeSegments()
		}
		return nil
	}

	if !Attr || !req.Valid.Mtime() {
		return fuse.ENOTSUP
	}

	// Change mtime
	if !req.Mtime.Equal(getMtime(o.so, o.sh)) {
		if o.writing {
			o.m.Lock()
			defer o.m.Unlock()
		}
		h := o.sh.ObjectMetadata().Headers(objectMetaHeader)
		o.sh[objectMtimeHeader] = formatTime(req.Mtime)
		h[objectMtimeHeader] = o.sh[objectMtimeHeader]
		if o.segmented {
			return SwiftConnection.ManifestUpdate(o.c.Name, o.so.Name, h)
		}
		return SwiftConnection.ObjectUpdate(o.c.Name, o.so.Name, h)
	}

	return nil
}

// Setxattr changes an extended attribute on the current node.
func (o *Object) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
	if !Xattr {
		return fuse.ENOTSUP
	}

	if !bytes.Equal(req.Xattr, []byte(o.sh[objectMetaHeaderXattr+req.Name])) {
		if o.writing {
			o.m.Lock()
			defer o.m.Unlock()
		}
		key := canonicalHeaderKey(objectMetaHeaderXattr + req.Name)
		value := hex.EncodeToString(req.Xattr)
		h := o.sh.ObjectMetadataXattr().Headers(objectMetaHeaderXattr)
		o.sh[key] = value
		h[key] = o.sh[key]

		if o.segmented {
			return SwiftConnection.ManifestUpdate(o.c.Name, o.so.Name, h)
		}
		return SwiftConnection.ObjectUpdate(o.c.Name, o.so.Name, h)
	}

	return nil
}

// Name gets the name of the underlying swift object.
func (o *Object) Name() string {
	return o.name
}

func (o *Object) copy(dir *Directory, name string) (copy *Object, err error) {
	if o.segmented {
		_, err = SwiftConnection.ManifestCopy(o.c.Name, o.path, dir.c.Name, dir.path+name, nil)
	} else {
		_, err = SwiftConnection.ObjectCopy(o.c.Name, o.path, dir.c.Name, dir.path+name, nil)
	}

	if err != nil {
		return nil, err
	}

	object := *o
	*object.so = *o.so
	object.c = dir.c
	object.cs = dir.cs
	object.p = dir
	object.name = name
	object.path = dir.path + name
	object.so.Name = dir.path + name

	directoryCache.Set(dir.c.Name, dir.path, name, &object)

	return &object, nil
}

func (o *Object) delete() error {
	directoryCache.Delete(o.c.Name, o.p.path, o.name)
	return SwiftConnection.ObjectDelete(o.c.Name, o.path)
}

func (o *Object) open(mode fuse.OpenFlags, flags *fuse.OpenResponseFlags) (*ObjectHandle, error) {
	oh := &ObjectHandle{
		target: o,
		create: mode&fuse.OpenCreate == fuse.OpenCreate,
	}

	// Unsupported flags
	if mode&fuse.OpenAppend == fuse.OpenAppend {
		return nil, fuse.ENOTSUP
	}

	// Supported flags
	if mode.IsReadOnly() {
		if TransferMode&SkipOpenRead == 0 {
			rd, err := newReader(oh)
			if err == swift.TooManyRequests {
				return nil, fuse.EAGAIN
			} else if err != nil {
				return oh, err
			}
			oh.rd = rd
		}

		return oh, nil
	}
	if mode.IsWriteOnly() {
		o.m.Lock()
		changeCache.Add(o.c.Name, o.path, o)

		*flags |= fuse.OpenNonSeekable
		*flags |= fuse.OpenDirectIO

		return oh, nil
	}

	return nil, fuse.ENOTSUP
}

func (o *Object) rename(dir *Directory, name string) error {
	copy, err := o.copy(dir, name)
	if err != nil {
		return err
	}

	err = o.delete()
	if err != nil {
		return err
	}

	*o = *copy

	return nil
}

func (o *Object) removeSegments() error {
	o.segmented = false
	if err := deleteSegments(o.cs.Name, o.sh[manifestHeader]); err != nil {
		return err
	}
	delete(o.sh, manifestHeader)
	return nil
}

func (o *Object) size() uint64 {
	return uint64(o.so.Bytes)
}

var (
	_ Node                 = (*Object)(nil)
	_ fs.Node              = (*Object)(nil)
	_ fs.NodeGetxattrer    = (*Object)(nil)
	_ fs.NodeListxattrer   = (*Object)(nil)
	_ fs.NodeRemovexattrer = (*Object)(nil)
	_ fs.NodeSetattrer     = (*Object)(nil)
	_ fs.NodeSetxattrer    = (*Object)(nil)
	_ fs.NodeOpener        = (*Object)(nil)
)
<commit_msg>Fsync interface check<commit_after>package svfs

import (
	"bytes"
	"encoding/hex"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/xlucas/swift"
	"golang.org/x/net/context"
)

const (
	objContentType        = "application/octet-stream"
	autoContentHeader     = "X-Detect-Content-Type"
	manifestHeader        = "X-Object-Manifest"
	objectMetaHeader      = "X-Object-Meta-"
	objectMetaHeaderXattr = objectMetaHeader + "Xattr-"
)

var (
	objectMtimeHeader = objectMetaHeader + "Mtime"
	segmentPathRegex  = regexp.MustCompile("^([^/]+)/(.*)$")
)

// Object is a node representing a swift object.
// It belongs to a container and segmented objects
// are bound to a container of segments.
type Object struct {
	name      string
	path      string
	so        *swift.Object
	sh        swift.Headers
	c         *swift.Container
	cs        *swift.Container
	p         *Directory
	m         sync.Mutex
	segmented bool
	writing   bool
}

// Attr fills the file attributes for an object node.
func (o *Object) Attr(ctx context.Context, a *fuse.Attr) (err error) {
	a.Size = o.size()
	a.BlockSize = uint32(BlockSize)
	a.Blocks = (a.Size / uint64(a.BlockSize)) * 8
	a.Mode = os.FileMode(DefaultMode)
	a.Gid = uint32(DefaultGID)
	a.Uid = uint32(DefaultUID)
	a.Mtime = getMtime(o.so, o.sh)
	a.Ctime = a.Mtime
	a.Crtime = a.Mtime
	return nil
}

// Getxattr retrieves extended attributes of an object node.
func (o *Object) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {
	if !Xattr {
		return fuse.ENOTSUP
	}

	key := canonicalHeaderKey(objectMetaHeaderXattr + req.Name)
	value, err := hex.DecodeString(o.sh[key])
	if err != nil {
		return err
	}

	resp.Xattr = []byte(value)
	return nil
}

// Export converts this object node as a direntry.
func (o *Object) Export() fuse.Dirent {
	return fuse.Dirent{
		Name: o.Name(),
		Type: fuse.DT_File,
	}
}

// Fsync synchronizes a file's in-core state with the storage device.
// This is a no-op since we are in a network, fully synchronous, filesystem.
func (o *Object) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
	return nil
}

// Listxattr lists extended attributes associated with this object node.
func (o *Object) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
	var keys []string

	if !Xattr {
		return fuse.ENOTSUP
	}

	for k := range o.sh.ObjectMetadataXattr().Headers(objectMetaHeaderXattr) {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	for _, key := range keys {
		resp.Append(strings.TrimPrefix(key, objectMetaHeaderXattr))
	}

	return nil
}

// Open returns the file handle associated with this object node.
func (o *Object) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	return o.open(req.Flags, &resp.Flags)
}

// Removexattr removes an extended attribute on this object node.
func (o *Object) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
	if !Xattr {
		return fuse.ENOTSUP
	}

	if _, ok := o.sh[objectMetaHeaderXattr+req.Name]; ok {
		if o.writing {
			o.m.Lock()
			defer o.m.Unlock()
		}
		key := canonicalHeaderKey(objectMetaHeaderXattr + req.Name)
		h := o.sh.ObjectMetadataXattr().Headers(objectMetaHeaderXattr)
		delete(h, key)
		delete(o.sh, key)
		if o.segmented {
			return SwiftConnection.ManifestUpdate(o.c.Name, o.so.Name, h)
		}
		return SwiftConnection.ObjectUpdate(o.c.Name, o.so.Name, h)
	}

	return nil
}

// Setattr changes file attributes on the current node.
func (o *Object) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
	// Change file size. Depending on the plaform, it may notably
	// be used by the kernel to truncate files instead of opening
	// them with O_TRUNC flag.
	if req.Valid.Size() {
		o.so.Bytes = int64(req.Size)
		if req.Size == 0 && o.segmented {
			return o.removeSegments()
		}
		return nil
	}

	if !Attr || !req.Valid.Mtime() {
		return fuse.ENOTSUP
	}

	// Change mtime
	if !req.Mtime.Equal(getMtime(o.so, o.sh)) {
		if o.writing {
			o.m.Lock()
			defer o.m.Unlock()
		}
		h := o.sh.ObjectMetadata().Headers(objectMetaHeader)
		o.sh[objectMtimeHeader] = formatTime(req.Mtime)
		h[objectMtimeHeader] = o.sh[objectMtimeHeader]
		if o.segmented {
			return SwiftConnection.ManifestUpdate(o.c.Name, o.so.Name, h)
		}
		return SwiftConnection.ObjectUpdate(o.c.Name, o.so.Name, h)
	}

	return nil
}

// Setxattr changes an extended attribute on the current node.
func (o *Object) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
	if !Xattr {
		return fuse.ENOTSUP
	}

	if !bytes.Equal(req.Xattr, []byte(o.sh[objectMetaHeaderXattr+req.Name])) {
		if o.writing {
			o.m.Lock()
			defer o.m.Unlock()
		}
		key := canonicalHeaderKey(objectMetaHeaderXattr + req.Name)
		value := hex.EncodeToString(req.Xattr)
		h := o.sh.ObjectMetadataXattr().Headers(objectMetaHeaderXattr)
		o.sh[key] = value
		h[key] = o.sh[key]

		if o.segmented {
			return SwiftConnection.ManifestUpdate(o.c.Name, o.so.Name, h)
		}
		return SwiftConnection.ObjectUpdate(o.c.Name, o.so.Name, h)
	}

	return nil
}

// Name gets the name of the underlying swift object.
func (o *Object) Name() string {
	return o.name
}

func (o *Object) copy(dir *Directory, name string) (copy *Object, err error) {
	if o.segmented {
		_, err = SwiftConnection.ManifestCopy(o.c.Name, o.path, dir.c.Name, dir.path+name, nil)
	} else {
		_, err = SwiftConnection.ObjectCopy(o.c.Name, o.path, dir.c.Name, dir.path+name, nil)
	}

	if err != nil {
		return nil, err
	}

	object := *o
	*object.so = *o.so
	object.c = dir.c
	object.cs = dir.cs
	object.p = dir
	object.name = name
	object.path = dir.path + name
	object.so.Name = dir.path + name

	directoryCache.Set(dir.c.Name, dir.path, name, &object)

	return &object, nil
}

func (o *Object) delete() error {
	directoryCache.Delete(o.c.Name, o.p.path, o.name)
	return SwiftConnection.ObjectDelete(o.c.Name, o.path)
}

func (o *Object) open(mode fuse.OpenFlags, flags *fuse.OpenResponseFlags) (*ObjectHandle, error) {
	oh := &ObjectHandle{
		target: o,
		create: mode&fuse.OpenCreate == fuse.OpenCreate,
	}

	// Unsupported flags
	if mode&fuse.OpenAppend == fuse.OpenAppend {
		return nil, fuse.ENOTSUP
	}

	// Supported flags
	if mode.IsReadOnly() {
		if TransferMode&SkipOpenRead == 0 {
			rd, err := newReader(oh)
			if err == swift.TooManyRequests {
				return nil, fuse.EAGAIN
			} else if err != nil {
				return oh, err
			}
			oh.rd = rd
		}

		return oh, nil
	}
	if mode.IsWriteOnly() {
		o.m.Lock()
		changeCache.Add(o.c.Name, o.path, o)

		*flags |= fuse.OpenNonSeekable
		*flags |= fuse.OpenDirectIO

		return oh, nil
	}

	return nil, fuse.ENOTSUP
}

func (o *Object) rename(dir *Directory, name string) error {
	copy, err := o.copy(dir, name)
	if err != nil {
		return err
	}

	err = o.delete()
	if err != nil {
		return err
	}

	*o = *copy

	return nil
}

func (o *Object) removeSegments() error {
	o.segmented = false
	if err := deleteSegments(o.cs.Name, o.sh[manifestHeader]); err != nil {
		return err
	}
	delete(o.sh, manifestHeader)
	return nil
}

func (o *Object) size() uint64 {
	return uint64(o.so.Bytes)
}

var (
	_ Node                 = (*Object)(nil)
	_ fs.Node              = (*Object)(nil)
	_ fs.NodeFsyncer       = (*Object)(nil)
	_ fs.NodeGetxattrer    = (*Object)(nil)
	_ fs.NodeListxattrer   = (*Object)(nil)
	_ fs.NodeRemovexattrer = (*Object)(nil)
	_ fs.NodeSetattrer     = (*Object)(nil)
	_ fs.NodeSetxattrer    = (*Object)(nil)
	_ fs.NodeOpener        = (*Object)(nil)
)
<|endoftext|>


<commit_before>package tags

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
)

const dir = "."

var fileFormats = [2]string{".wav", ".aif"}

func filesOfType(ext string, overwriteExisting bool) []string {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		fmt.Print(err)
		os.Exit(1)
	}

	var filesToConvert []string
	for _, file := range files {
		if file.IsDir() {
			continue
		}

		if !strings.Contains(file.Name(), ext) {
			continue
		}

		filePath := path.Join(dir, file.Name())
		if _, err := os.Stat(filePath); err != nil {
			continue
		}

		// If force is true, we want to convert regardless of if a file exists or not
		if !overwriteExisting {
			if _, err := os.Stat(strings.Replace(filePath, ext, ".mp3", 1)); err == nil {
				fmt.Printf("%s already exists, skipping...\n", file.Name())
				continue
			}
		}

		filesToConvert = append(filesToConvert, filePath)
	}

	return filesToConvert
}

func convert(path string, wg *sync.WaitGroup) {
	extension := filepath.Ext(path)
	newPath := strings.Replace(path, extension, ".mp3", 1)
	fmt.Printf("lame --silent -b 320 -h -V2 %s %s\n", path, newPath)
	cmd := exec.Command("lame", "--silent", "-b 320", "-h", "-V2", path, newPath)

	if output, err := cmd.CombinedOutput(); err != nil {
		fmt.Println("Build:", err)
		fmt.Println("Build:", string(output))
		os.Exit(1)
	}

	fmt.Printf("Done converting '%s' \n", path)
	wg.Done()
}

// Format - a command to format files
func Format(overwriteExisting bool) {
	fmt.Printf("overwriteExisting is %v\n", overwriteExisting)
	var files []string

	for _, format := range fileFormats {
		fmt.Printf("Detecting %s files...\n", format)

		filesToConvert := filesOfType(format, overwriteExisting)
		if len(filesToConvert) > 0 {
			files = append(files, filesToConvert...)
		}
	}

	if len(files) == 0 {
		fmt.Println("No files were found.")
		return
	}

	var wg sync.WaitGroup
	wg.Add(len(files))
	for _, p := range files {
		go convert(p, &wg)
	}

	wg.Wait()
}
<commit_msg>Cleanup debug and improve working directory resolution<commit_after>package tags

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
)

var fileFormats = [2]string{".wav", ".aif"}

func filesOfType(dir string, ext string, overwriteExisting bool) []string {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		fmt.Print(err)
		os.Exit(1)
	}

	var filesToConvert []string
	for _, file := range files {
		if file.IsDir() {
			continue
		}

		if !strings.Contains(file.Name(), ext) {
			continue
		}

		filePath := path.Join(dir, file.Name())
		if _, err := os.Stat(filePath); err != nil {
			continue
		}

		// If force is true, we want to convert regardless of if a file exists or not
		if !overwriteExisting {
			if _, err := os.Stat(strings.Replace(filePath, ext, ".mp3", 1)); err == nil {
				fmt.Printf("%s already exists, skipping...\n", file.Name())
				continue
			}
		}

		filesToConvert = append(filesToConvert, filePath)
	}

	return filesToConvert
}

func convert(path string, wg *sync.WaitGroup) {
	extension := filepath.Ext(path)
	newPath := strings.Replace(path, extension, ".mp3", 1)
	cmd := exec.Command("lame", "--silent", "-b 320", "-h", "-V2", path, newPath)

	if output, err := cmd.CombinedOutput(); err != nil {
		fmt.Println("Build:", err)
		fmt.Println("Build:", string(output))
		os.Exit(1)
	}

	fmt.Printf("Done converting '%s' \n", path)
	wg.Done()
}

// Format - a command to format files
func Format(overwriteExisting bool) {
	dir, err := os.Getwd()
	if err != nil {
		log.Println(err)
		return
	}

	var files []string
	for _, format := range fileFormats {
		fmt.Printf("Detecting %s files...\n", format)

		filesToConvert := filesOfType(dir, format, overwriteExisting)
		if len(filesToConvert) > 0 {
			files = append(files, filesToConvert...)
		}
	}

	if len(files) == 0 {
		fmt.Println("No files were found, exiting...")
		return
	}

	var wg sync.WaitGroup
	wg.Add(len(files))
	for _, p := range files {
		go convert(p, &wg)
	}

	wg.Wait()
}
<|endoftext|>


<commit_before>/*
// Copyright (c) 2016 Intel Corporation
//
// 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.
*/

package main

import (
	"errors"
	"fmt"
	"github.com/01org/ciao/ciao-controller/types"
	"github.com/golang/glog"
	"time"
)

func (c *controller) evacuateNode(nodeID string) error {
	// should I bother to see if nodeID is valid?
	go c.client.EvacuateNode(nodeID)
	return nil
}

func (c *controller) restartInstance(instanceID string) error {
	// should I bother to see if instanceID is valid?
	// get node id.  If there is no node id we can't send a restart
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	if i.State != "exited" {
		return errors.New("You may only restart paused instances")
	}

	go c.client.RestartInstance(instanceID, i.NodeID)
	return nil
}

func (c *controller) stopInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	if i.State == "pending" {
		return errors.New("You may not stop a pending instance")
	}

	go c.client.StopInstance(instanceID, i.NodeID)
	return nil
}

func (c *controller) deleteInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	go c.client.DeleteInstance(instanceID, i.NodeID)
	return nil
}

func (c *controller) startWorkload(workloadID string, tenantID string, instances int, trace bool, label string) ([]*types.Instance, error) {
	var e error

	if instances == 0 {
		return nil, errors.New("Missing number of instances to start")
	}

	wl, err := c.ds.GetWorkload(workloadID)
	if err != nil {
		return nil, err
	}

	if !isCNCIWorkload(wl) {
		tenant, err := c.ds.GetTenant(tenantID)
		if err != nil {
			return nil, err
		}

		if tenant == nil {
			if *noNetwork {
				_, err := c.ds.AddTenant(tenantID)
				if err != nil {
					return nil, err
				}
			} else {

				err = c.addTenant(tenantID)
				if err != nil {
					return nil, err
				}
			}
		} else if tenant.CNCIIP == "" {
			if !*noNetwork {
				_ = c.addTenant(tenantID)
				tenant, err = c.ds.GetTenant(tenantID)
				if err != nil {
					return nil, err
				}

				if tenant.CNCIIP == "" {
					return nil, errors.New("Unable to Launch Tenant CNCI")
				}
			}
		}
	}

	var newInstances []*types.Instance

	for i := 0; i < instances; i++ {
		startTime := time.Now()
		instance, err := newInstance(c, tenantID, wl)
		if err != nil {
			glog.V(2).Info("error newInstance")
			e = err
			continue
		}
		instance.startTime = startTime

		ok, err := instance.Allowed()
		if ok {
			err = instance.Add()
			if err != nil {
				glog.V(2).Info("error adding instance")
				instance.Clean()
				e = err
				continue
			}

			newInstances = append(newInstances, &instance.Instance)
			if trace == false {
				go c.client.StartWorkload(instance.newConfig.config)
			} else {
				go c.client.StartTracedWorkload(instance.newConfig.config, instance.startTime, label)
			}
		} else {
			instance.Clean()
			if err != nil {
				e = err
				continue
			} else {
				// stop if we are over limits
				return nil, errors.New("Over Tenant Limits")
			}
		}
	}

	return newInstances, e
}

func (c *controller) launchCNCI(tenantID string) error {
	workloadID, err := c.ds.GetCNCIWorkloadID()
	if err != nil {
		return err
	}

	ch := make(chan bool)

	c.ds.AddTenantChan(ch, tenantID)

	_, err = c.startWorkload(workloadID, tenantID, 1, false, "")
	if err != nil {
		return err
	}

	success := <-ch

	if success {
		return nil
	}
	msg := fmt.Sprintf("Failed to Launch CNCI for %s", tenantID)
	return errors.New(msg)
}

func (c *controller) addTenant(id string) error {
	// create new entry in datastore
	_, err := c.ds.AddTenant(id)
	if err != nil {
		return err
	}

	// start up a CNCI. this will block till the
	// CNCI started event is returned
	return c.launchCNCI(id)
}
<commit_msg>ciao-controller: command: reduce complexity of StartWorkload<commit_after>/*
// Copyright (c) 2016 Intel Corporation
//
// 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.
*/

package main

import (
	"errors"
	"fmt"
	"github.com/01org/ciao/ciao-controller/types"
	"github.com/golang/glog"
	"time"
)

func (c *controller) evacuateNode(nodeID string) error {
	// should I bother to see if nodeID is valid?
	go c.client.EvacuateNode(nodeID)
	return nil
}

func (c *controller) restartInstance(instanceID string) error {
	// should I bother to see if instanceID is valid?
	// get node id.  If there is no node id we can't send a restart
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	if i.State != "exited" {
		return errors.New("You may only restart paused instances")
	}

	go c.client.RestartInstance(instanceID, i.NodeID)
	return nil
}

func (c *controller) stopInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	if i.State == "pending" {
		return errors.New("You may not stop a pending instance")
	}

	go c.client.StopInstance(instanceID, i.NodeID)
	return nil
}

func (c *controller) deleteInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return errors.New("Instance Not Assigned to Node")
	}

	go c.client.DeleteInstance(instanceID, i.NodeID)
	return nil
}

func (c *controller) confirmTenant(tenantID string) error {
	tenant, err := c.ds.GetTenant(tenantID)
	if err != nil {
		return err
	}

	if tenant == nil {
		if *noNetwork {
			_, err := c.ds.AddTenant(tenantID)
			if err != nil {
				return err
			}
		} else {

			err = c.addTenant(tenantID)
			if err != nil {
				return err
			}
		}
	} else if tenant.CNCIIP == "" {
		if !*noNetwork {
			_ = c.addTenant(tenantID)
			tenant, err = c.ds.GetTenant(tenantID)
			if err != nil {
				return err
			}

			if tenant.CNCIIP == "" {
				return errors.New("Unable to Launch Tenant CNCI")
			}
		}
	}

	return nil
}

func (c *controller) startWorkload(workloadID string, tenantID string, instances int, trace bool, label string) ([]*types.Instance, error) {
	var e error

	if instances == 0 {
		return nil, errors.New("Missing number of instances to start")
	}

	wl, err := c.ds.GetWorkload(workloadID)
	if err != nil {
		return nil, err
	}

	if !isCNCIWorkload(wl) {
		err := c.confirmTenant(tenantID)
		if err != nil {
			return nil, err
		}
	}

	var newInstances []*types.Instance

	for i := 0; i < instances; i++ {
		startTime := time.Now()
		instance, err := newInstance(c, tenantID, wl)
		if err != nil {
			glog.V(2).Info("error newInstance")
			e = err
			continue
		}
		instance.startTime = startTime

		ok, err := instance.Allowed()
		if ok {
			err = instance.Add()
			if err != nil {
				glog.V(2).Info("error adding instance")
				instance.Clean()
				e = err
				continue
			}

			newInstances = append(newInstances, &instance.Instance)
			if trace == false {
				go c.client.StartWorkload(instance.newConfig.config)
			} else {
				go c.client.StartTracedWorkload(instance.newConfig.config, instance.startTime, label)
			}
		} else {
			instance.Clean()
			if err != nil {
				e = err
				continue
			} else {
				// stop if we are over limits
				return nil, errors.New("Over Tenant Limits")
			}
		}
	}

	return newInstances, e
}

func (c *controller) launchCNCI(tenantID string) error {
	workloadID, err := c.ds.GetCNCIWorkloadID()
	if err != nil {
		return err
	}

	ch := make(chan bool)

	c.ds.AddTenantChan(ch, tenantID)

	_, err = c.startWorkload(workloadID, tenantID, 1, false, "")
	if err != nil {
		return err
	}

	success := <-ch

	if success {
		return nil
	}
	msg := fmt.Sprintf("Failed to Launch CNCI for %s", tenantID)
	return errors.New(msg)
}

func (c *controller) addTenant(id string) error {
	// create new entry in datastore
	_, err := c.ds.AddTenant(id)
	if err != nil {
		return err
	}

	// start up a CNCI. this will block till the
	// CNCI started event is returned
	return c.launchCNCI(id)
}
<|endoftext|>


<commit_before>package instructions

var (
    _aload_0 = &aload_0{}
    _aload_1 = &aload_1{}
    _aload_2 = &aload_2{}
    _aload_3 = &aload_3{}
    _arraylength = &arraylength{}
    _astore_0 = &astore_0{}
    _astore_1 = &astore_1{}
    _astore_2 = &astore_2{}
    _astore_3 = &astore_3{}
    _athrow = &athrow{}
    _d2f = &d2f{}
    _d2i = &d2i{}
    _d2l = &d2l{}
    _dcmpg = &dcmpg{}
    _dcmpl = &dcmpl{}
    _dload_0 = &dload_0{}
    _dload_1 = &dload_1{}
    _dload_2 = &dload_2{}
    _dload_3 = &dload_3{}
    _dstore_0 = &dstore_0{}
    _dstore_1 = &dstore_1{}
    _dstore_2 = &dstore_2{}
    _dstore_3 = &dstore_3{}
    _dup = &dup{}
    _dup_x1 = &dup_x1{}
    _dup_x2 = &dup_x2{}
    _dup2 = &dup2{}
    _dup2_x1 = &dup2_x1{}
    _dup2_x2 = &dup2_x2{}
    _f2d = &f2d{}
    _f2i = &f2i{}
    _f2l = &f2l{}
    _fcmpg = &fcmpg{}
    _fcmpl = &fcmpl{}
    _fload_0 = &fload_0{}
    _fload_1 = &fload_1{}
    _fload_2 = &fload_2{}
    _fload_3 = &fload_3{}
    _fstore_0 = &fstore_0{}
    _fstore_1 = &fstore_1{}
    _fstore_2 = &fstore_2{}
    _fstore_3 = &fstore_3{}
    _i2b = &i2b{}
    _i2c = &i2c{}
    _i2d = &i2d{}
    _i2f = &i2f{}
    _i2l = &i2l{}
    _i2s = &i2s{}
    _iload_0 = &iload_0{}
    _iload_1 = &iload_1{}
    _iload_2 = &iload_2{}
    _iload_3 = &iload_3{}
    _istore_0 = &istore_0{}
    _istore_1 = &istore_1{}
    _istore_2 = &istore_2{}
    _istore_3 = &istore_3{}
    _l2d = &l2d{}
    _l2f = &l2f{}
    _l2i = &l2i{}
    _lcmp = &lcmp{}
    _lload_0 = &lload_0{}
    _lload_1 = &lload_1{}
    _lload_2 = &lload_2{}
    _lload_3 = &lload_3{}
    _lstore_0 = &lstore_0{}
    _lstore_1 = &lstore_1{}
    _lstore_2 = &lstore_2{}
    _lstore_3 = &lstore_3{}
    _monitorenter = &monitorenter{}
    _monitorexit = &monitorexit{}
)

func Decode(bcr *BytecodeReader) (Instruction) {
    opcode := bcr.readUint8()
    instruction := newInstruction(opcode)
    instruction.fetchOperands(bcr)
    return instruction
}

func newInstruction(opcode byte) (Instruction) {
    switch opcode {
    case 0x00: return &nop{}
    case 0x01: return &aconst_null{}
    case 0x02: return &iconst_m1{}
    case 0x03: return &iconst_0{}
    case 0x04: return &iconst_1{}
    case 0x05: return &iconst_2{}
    case 0x06: return &iconst_3{}
    case 0x07: return &iconst_4{}
    case 0x08: return &iconst_5{}
    case 0x09: return &lconst_0{}
    case 0x0a: return &lconst_1{}
    case 0x0b: return &fconst_0{}
    case 0x0c: return &fconst_1{}
    case 0x0d: return &fconst_2{}
    case 0x0e: return &dconst_0{}
    case 0x0f: return &dconst_1{}
    case 0x10: return &bipush{}
    case 0x11: return &sipush{}
    case 0x12: return &ldc{}
    case 0x13: return &ldc_w{}
    case 0x14: return &ldc2_w{}
    case 0x15: return &iload{}
    case 0x16: return &lload{}
    case 0x17: return &fload{}
    case 0x18: return &dload{}
    case 0x19: return &aload{}
    case 0x1a: return _iload_0
    case 0x1b: return _iload_1
    case 0x1c: return _iload_2
    case 0x1d: return _iload_3
    case 0x1e: return _lload_0
    case 0x1f: return _lload_1
    case 0x20: return _lload_2
    case 0x21: return _lload_3
    case 0x22: return _fload_0
    case 0x23: return _fload_1
    case 0x24: return _fload_2
    case 0x25: return _fload_3
    case 0x26: return _dload_0
    case 0x27: return _dload_1
    case 0x28: return _dload_2
    case 0x29: return _dload_3
    case 0x2a: return _aload_0
    case 0x2b: return _aload_1
    case 0x2c: return _aload_2
    case 0x2d: return _aload_3
    case 0x2e: return &iaload{}
    case 0x2f: return &laload{}
    case 0x30: return &faload{}
    case 0x31: return &daload{}
    case 0x32: return &aaload{}
    case 0x33: return &baload{}
    case 0x34: return &caload{}
    case 0x35: return &saload{}
    case 0x36: return &istore{}
    case 0x37: return &lstore{}
    case 0x38: return &fstore{}
    case 0x39: return &dstore{}
    case 0x3a: return &astore{}
    case 0x3b: return _istore_0
    case 0x3c: return _istore_1
    case 0x3d: return _istore_2
    case 0x3e: return _istore_3
    case 0x3f: return _lstore_0
    case 0x40: return _lstore_1
    case 0x41: return _lstore_2
    case 0x42: return _lstore_3
    case 0x43: return _fstore_0
    case 0x44: return _fstore_1
    case 0x45: return _fstore_2
    case 0x46: return _fstore_3
    case 0x47: return _dstore_0
    case 0x48: return _dstore_1
    case 0x49: return _dstore_2
    case 0x4a: return _dstore_3
    case 0x4b: return _astore_0
    case 0x4c: return _astore_1
    case 0x4d: return _astore_2
    case 0x4e: return _astore_3
    case 0x4f: return &iastore{}
    case 0x50: return &lastore{}
    case 0x51: return &fastore{}
    case 0x52: return &dastore{}
    case 0x53: return &aastore{}
    case 0x54: return &bastore{}
    case 0x55: return &castore{}
    case 0x56: return &sastore{}
    case 0x57: return &pop{}
    case 0x58: return &pop2{}
    case 0x59: return _dup
    case 0x5a: return _dup_x1
    case 0x5b: return _dup_x2
    case 0x5c: return _dup2
    case 0x5d: return _dup2_x1
    case 0x5e: return _dup2_x2
    case 0x5f: return &swap{}
    case 0x60: return &iadd{}
    case 0x61: return &ladd{}
    case 0x62: return &fadd{}
    case 0x63: return &dadd{}
    case 0x64: return &isub{}
    case 0x65: return &lsub{}
    case 0x66: return &fsub{}
    case 0x67: return &dsub{}
    case 0x68: return &imul{}
    case 0x69: return &lmul{}
    case 0x6a: return &fmul{}
    case 0x6b: return &dmul{}
    case 0x6c: return &idiv{}
    case 0x6d: return &ldiv{}
    case 0x6e: return &fdiv{}
    case 0x6f: return &ddiv{}
    case 0x70: return &irem{}
    case 0x71: return &lrem{}
    case 0x72: return &frem{}
    case 0x73: return &drem{}
    case 0x74: return &ineg{}
    case 0x75: return &lneg{}
    case 0x76: return &fneg{}
    case 0x77: return &dneg{}
    case 0x78: return &ishl{}
    case 0x79: return &lshl{}
    case 0x7a: return &ishr{}
    case 0x7b: return &lshr{}
    case 0x7c: return &iushr{}
    case 0x7d: return &lushr{}
    case 0x7e: return &iand{}
    case 0x7f: return &land{}
    case 0x80: return &ior{}
    case 0x81: return &lor{}
    case 0x82: return &ixor{}
    case 0x83: return &lxor{}
    case 0x84: return &iinc{}
    case 0x85: return _i2l
    case 0x86: return _i2f
    case 0x87: return _i2d
    case 0x88: return _l2i
    case 0x89: return _l2f
    case 0x8a: return _l2d
    case 0x8b: return _f2i
    case 0x8c: return _f2l
    case 0x8d: return _f2d
    case 0x8e: return _d2i
    case 0x8f: return _d2l
    case 0x90: return _d2f
    case 0x91: return _i2b
    case 0x92: return _i2c
    case 0x93: return _i2s
    case 0x94: return _lcmp
    case 0x95: return _fcmpl
    case 0x96: return _fcmpg
    case 0x97: return _dcmpl
    case 0x98: return _dcmpg
    case 0x99: return &ifeq{}
    case 0x9a: return &ifne{}
    case 0x9b: return &iflt{}
    case 0x9c: return &ifge{}
    case 0x9d: return &ifgt{}
    case 0x9e: return &ifle{}
    case 0x9f: return &if_icmpeq{}
    case 0xa0: return &if_icmpne{}
    case 0xa1: return &if_icmplt{}
    case 0xa2: return &if_icmpge{}
    case 0xa3: return &if_icmpgt{}
    case 0xa4: return &if_icmple{}
    case 0xa5: return &if_acmpeq{}
    case 0xa6: return &if_acmpne{}
    case 0xa7: return &_goto{}
  //case 0xa8: return &jsr{}
  //case 0xa9: return &ret{}
    case 0xaa: return &tableswitch{}
    case 0xab: return &lookupswitch{}
    case 0xac: return &ireturn{}
    case 0xad: return &lreturn{}
    case 0xae: return &freturn{}
    case 0xaf: return &dreturn{}
    case 0xb0: return &areturn{}
    case 0xb1: return &_return{}
    case 0xb2: return &getstatic{}
    case 0xb3: return &putstatic{}
    case 0xb4: return &getfield{}
    case 0xb5: return &putfield{}
    case 0xb6: return &invokevirtual{}
    case 0xb7: return &invokespecial{}
    case 0xb8: return &invokestatic{}
    case 0xb9: return &invokeinterface{}
    case 0xba: return &invokedynamic{}
    case 0xbb: return &_new{}
    case 0xbc: return &newarray{}
    case 0xbd: return &anewarray{}
    case 0xbe: return _arraylength
    case 0xbf: return _athrow
    case 0xc0: return &checkcast{}
    case 0xc1: return &instanceof{}
    case 0xc2: return _monitorenter
    case 0xc3: return _monitorexit
    case 0xc5: return &multianewarray{}
    case 0xc6: return &ifnull{}
    case 0xc7: return &ifnonnull{}
    case 0xc8: return &goto_w{}
  //case 0xc9: return &jsr_w{}
  //case 0xca: return &breakpoint{}
  //case 0xfe: return &impdep1{}
  //case 0xff: return &impdep2{}
    // todo
    default: panic("BAD opcode!")
    }
}
<commit_msg>optimize<commit_after>package instructions

var (
    _aload_0 = &aload_0{}
    _aload_1 = &aload_1{}
    _aload_2 = &aload_2{}
    _aload_3 = &aload_3{}
    _arraylength = &arraylength{}
    _astore_0 = &astore_0{}
    _astore_1 = &astore_1{}
    _astore_2 = &astore_2{}
    _astore_3 = &astore_3{}
    _athrow = &athrow{}
    _d2f = &d2f{}
    _d2i = &d2i{}
    _d2l = &d2l{}
    _dcmpg = &dcmpg{}
    _dcmpl = &dcmpl{}
    _dload_0 = &dload_0{}
    _dload_1 = &dload_1{}
    _dload_2 = &dload_2{}
    _dload_3 = &dload_3{}
    _dstore_0 = &dstore_0{}
    _dstore_1 = &dstore_1{}
    _dstore_2 = &dstore_2{}
    _dstore_3 = &dstore_3{}
    _dup = &dup{}
    _dup_x1 = &dup_x1{}
    _dup_x2 = &dup_x2{}
    _dup2 = &dup2{}
    _dup2_x1 = &dup2_x1{}
    _dup2_x2 = &dup2_x2{}
    _f2d = &f2d{}
    _f2i = &f2i{}
    _f2l = &f2l{}
    _fcmpg = &fcmpg{}
    _fcmpl = &fcmpl{}
    _fload_0 = &fload_0{}
    _fload_1 = &fload_1{}
    _fload_2 = &fload_2{}
    _fload_3 = &fload_3{}
    _fstore_0 = &fstore_0{}
    _fstore_1 = &fstore_1{}
    _fstore_2 = &fstore_2{}
    _fstore_3 = &fstore_3{}
    _i2b = &i2b{}
    _i2c = &i2c{}
    _i2d = &i2d{}
    _i2f = &i2f{}
    _i2l = &i2l{}
    _i2s = &i2s{}
    _iload_0 = &iload_0{}
    _iload_1 = &iload_1{}
    _iload_2 = &iload_2{}
    _iload_3 = &iload_3{}
    _istore_0 = &istore_0{}
    _istore_1 = &istore_1{}
    _istore_2 = &istore_2{}
    _istore_3 = &istore_3{}
    _l2d = &l2d{}
    _l2f = &l2f{}
    _l2i = &l2i{}
    _lcmp = &lcmp{}
    _lload_0 = &lload_0{}
    _lload_1 = &lload_1{}
    _lload_2 = &lload_2{}
    _lload_3 = &lload_3{}
    _lstore_0 = &lstore_0{}
    _lstore_1 = &lstore_1{}
    _lstore_2 = &lstore_2{}
    _lstore_3 = &lstore_3{}
    _monitorenter = &monitorenter{}
    _monitorexit = &monitorexit{}
    _nop = &nop{}
    _pop = &pop{}
    _pop2 = &pop2{}
)

func Decode(bcr *BytecodeReader) (Instruction) {
    opcode := bcr.readUint8()
    instruction := newInstruction(opcode)
    instruction.fetchOperands(bcr)
    return instruction
}

func newInstruction(opcode byte) (Instruction) {
    switch opcode {
    case 0x00: return _nop
    case 0x01: return &aconst_null{}
    case 0x02: return &iconst_m1{}
    case 0x03: return &iconst_0{}
    case 0x04: return &iconst_1{}
    case 0x05: return &iconst_2{}
    case 0x06: return &iconst_3{}
    case 0x07: return &iconst_4{}
    case 0x08: return &iconst_5{}
    case 0x09: return &lconst_0{}
    case 0x0a: return &lconst_1{}
    case 0x0b: return &fconst_0{}
    case 0x0c: return &fconst_1{}
    case 0x0d: return &fconst_2{}
    case 0x0e: return &dconst_0{}
    case 0x0f: return &dconst_1{}
    case 0x10: return &bipush{}
    case 0x11: return &sipush{}
    case 0x12: return &ldc{}
    case 0x13: return &ldc_w{}
    case 0x14: return &ldc2_w{}
    case 0x15: return &iload{}
    case 0x16: return &lload{}
    case 0x17: return &fload{}
    case 0x18: return &dload{}
    case 0x19: return &aload{}
    case 0x1a: return _iload_0
    case 0x1b: return _iload_1
    case 0x1c: return _iload_2
    case 0x1d: return _iload_3
    case 0x1e: return _lload_0
    case 0x1f: return _lload_1
    case 0x20: return _lload_2
    case 0x21: return _lload_3
    case 0x22: return _fload_0
    case 0x23: return _fload_1
    case 0x24: return _fload_2
    case 0x25: return _fload_3
    case 0x26: return _dload_0
    case 0x27: return _dload_1
    case 0x28: return _dload_2
    case 0x29: return _dload_3
    case 0x2a: return _aload_0
    case 0x2b: return _aload_1
    case 0x2c: return _aload_2
    case 0x2d: return _aload_3
    case 0x2e: return &iaload{}
    case 0x2f: return &laload{}
    case 0x30: return &faload{}
    case 0x31: return &daload{}
    case 0x32: return &aaload{}
    case 0x33: return &baload{}
    case 0x34: return &caload{}
    case 0x35: return &saload{}
    case 0x36: return &istore{}
    case 0x37: return &lstore{}
    case 0x38: return &fstore{}
    case 0x39: return &dstore{}
    case 0x3a: return &astore{}
    case 0x3b: return _istore_0
    case 0x3c: return _istore_1
    case 0x3d: return _istore_2
    case 0x3e: return _istore_3
    case 0x3f: return _lstore_0
    case 0x40: return _lstore_1
    case 0x41: return _lstore_2
    case 0x42: return _lstore_3
    case 0x43: return _fstore_0
    case 0x44: return _fstore_1
    case 0x45: return _fstore_2
    case 0x46: return _fstore_3
    case 0x47: return _dstore_0
    case 0x48: return _dstore_1
    case 0x49: return _dstore_2
    case 0x4a: return _dstore_3
    case 0x4b: return _astore_0
    case 0x4c: return _astore_1
    case 0x4d: return _astore_2
    case 0x4e: return _astore_3
    case 0x4f: return &iastore{}
    case 0x50: return &lastore{}
    case 0x51: return &fastore{}
    case 0x52: return &dastore{}
    case 0x53: return &aastore{}
    case 0x54: return &bastore{}
    case 0x55: return &castore{}
    case 0x56: return &sastore{}
    case 0x57: return _pop
    case 0x58: return _pop2
    case 0x59: return _dup
    case 0x5a: return _dup_x1
    case 0x5b: return _dup_x2
    case 0x5c: return _dup2
    case 0x5d: return _dup2_x1
    case 0x5e: return _dup2_x2
    case 0x5f: return &swap{}
    case 0x60: return &iadd{}
    case 0x61: return &ladd{}
    case 0x62: return &fadd{}
    case 0x63: return &dadd{}
    case 0x64: return &isub{}
    case 0x65: return &lsub{}
    case 0x66: return &fsub{}
    case 0x67: return &dsub{}
    case 0x68: return &imul{}
    case 0x69: return &lmul{}
    case 0x6a: return &fmul{}
    case 0x6b: return &dmul{}
    case 0x6c: return &idiv{}
    case 0x6d: return &ldiv{}
    case 0x6e: return &fdiv{}
    case 0x6f: return &ddiv{}
    case 0x70: return &irem{}
    case 0x71: return &lrem{}
    case 0x72: return &frem{}
    case 0x73: return &drem{}
    case 0x74: return &ineg{}
    case 0x75: return &lneg{}
    case 0x76: return &fneg{}
    case 0x77: return &dneg{}
    case 0x78: return &ishl{}
    case 0x79: return &lshl{}
    case 0x7a: return &ishr{}
    case 0x7b: return &lshr{}
    case 0x7c: return &iushr{}
    case 0x7d: return &lushr{}
    case 0x7e: return &iand{}
    case 0x7f: return &land{}
    case 0x80: return &ior{}
    case 0x81: return &lor{}
    case 0x82: return &ixor{}
    case 0x83: return &lxor{}
    case 0x84: return &iinc{}
    case 0x85: return _i2l
    case 0x86: return _i2f
    case 0x87: return _i2d
    case 0x88: return _l2i
    case 0x89: return _l2f
    case 0x8a: return _l2d
    case 0x8b: return _f2i
    case 0x8c: return _f2l
    case 0x8d: return _f2d
    case 0x8e: return _d2i
    case 0x8f: return _d2l
    case 0x90: return _d2f
    case 0x91: return _i2b
    case 0x92: return _i2c
    case 0x93: return _i2s
    case 0x94: return _lcmp
    case 0x95: return _fcmpl
    case 0x96: return _fcmpg
    case 0x97: return _dcmpl
    case 0x98: return _dcmpg
    case 0x99: return &ifeq{}
    case 0x9a: return &ifne{}
    case 0x9b: return &iflt{}
    case 0x9c: return &ifge{}
    case 0x9d: return &ifgt{}
    case 0x9e: return &ifle{}
    case 0x9f: return &if_icmpeq{}
    case 0xa0: return &if_icmpne{}
    case 0xa1: return &if_icmplt{}
    case 0xa2: return &if_icmpge{}
    case 0xa3: return &if_icmpgt{}
    case 0xa4: return &if_icmple{}
    case 0xa5: return &if_acmpeq{}
    case 0xa6: return &if_acmpne{}
    case 0xa7: return &_goto{}
  //case 0xa8: return &jsr{}
  //case 0xa9: return &ret{}
    case 0xaa: return &tableswitch{}
    case 0xab: return &lookupswitch{}
    case 0xac: return &ireturn{}
    case 0xad: return &lreturn{}
    case 0xae: return &freturn{}
    case 0xaf: return &dreturn{}
    case 0xb0: return &areturn{}
    case 0xb1: return &_return{}
    case 0xb2: return &getstatic{}
    case 0xb3: return &putstatic{}
    case 0xb4: return &getfield{}
    case 0xb5: return &putfield{}
    case 0xb6: return &invokevirtual{}
    case 0xb7: return &invokespecial{}
    case 0xb8: return &invokestatic{}
    case 0xb9: return &invokeinterface{}
    case 0xba: return &invokedynamic{}
    case 0xbb: return &_new{}
    case 0xbc: return &newarray{}
    case 0xbd: return &anewarray{}
    case 0xbe: return _arraylength
    case 0xbf: return _athrow
    case 0xc0: return &checkcast{}
    case 0xc1: return &instanceof{}
    case 0xc2: return _monitorenter
    case 0xc3: return _monitorexit
    case 0xc5: return &multianewarray{}
    case 0xc6: return &ifnull{}
    case 0xc7: return &ifnonnull{}
    case 0xc8: return &goto_w{}
  //case 0xc9: return &jsr_w{}
  //case 0xca: return &breakpoint{}
  //case 0xfe: return &impdep1{}
  //case 0xff: return &impdep2{}
    // todo
    default: panic("BAD opcode!")
    }
}
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"log"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/dutchcoders/goftp"
	"github.com/garyburd/redigo/redis"
)

var (
	sshKey    = flag.String("key", "", "SSH key to use for cloning")
	ftpUrl    = flag.String("ftp", "", "FTP server to save backups to")
	redisUrl  = flag.String("redis", "", "Address of redis")
	frequency = flag.Duration("frequency", 24*time.Hour, "Frequency of backups")
	force     = flag.Bool("force", false, "Force download")
	help      = flag.Bool("help", false, "Show this help")
)

var (
	badCharacters = regexp.MustCompilePOSIX("[/@:!?*\\&]")
)

func main() {
	flag.Parse()
	if *help {
		flag.PrintDefaults()
		return
	}
	if *ftpUrl == "" || *redisUrl == "" {
		log.Fatalf("-ftp and -redis have to be set")
	}

	redisConn, err := connectRedis(*redisUrl)
	if err != nil {
		log.Fatalf("Could not connect to redis: %s", err)
	}
	defer redisConn.Close()

	ftpConn, ftpUrl, err := connectFtp(*ftpUrl)
	if err != nil {
		log.Fatalf("Could not connect to FTP server: %s", err)
	}
	defer ftpConn.Close()
	if err := ftpConn.Cwd(ftpUrl.Path); err != nil {
		log.Fatalf("Could not cd to target directory: %s", err)
	}

	for {
		if !*force {
			nextRun := lastRun(redisConn).Add(*frequency)
			if nextRun.After(time.Now()) {
				time.Sleep(nextRun.Sub(time.Now()))
				continue
			}
		}
		*force = false

		log.Printf("Downloading all the repos...")
		repos := repos(redisConn)
		for _, repo := range repos {
			log.Printf("Downloading %s...", repo)
			safeName := badCharacters.ReplaceAllString(repo, "_")
			buf, err := downloadRepository(repo)
			if err != nil {
				log.Printf("Error downloading repository: %s", err)
				continue
			}

			if err := ftpConn.Stor(safeName+".tar.gz", buf); err != nil {
				log.Printf("Error uploading: %s", err)
			}
		}
		log.Printf("Finished.")
		timestampLastRun(redisConn)
	}
}

func lastRun(conn redis.Conn) time.Time {
	ok, err := redis.Bool(conn.Do("EXISTS", "github-backup:lastrun"))
	if err != nil {
		log.Fatalf("Error querying database: %s", err)
	}
	if !ok {
		return time.Unix(0, 0)
	}

	ts, err := redis.String(conn.Do("GET", "github-backup:lastrun"))
	if err != nil {
		log.Fatalf("Error retrieving timestamp: %s", err)
	}
	t, err := time.Parse(time.RFC3339, ts)
	if err != nil {
		log.Fatalf("Error parsing timestamp: %s", err)
	}
	return t
}

func timestampLastRun(conn redis.Conn) {
	_, err := conn.Do("SET", "github-backup:lastrun", time.Now().Format(time.RFC3339))
	if err != nil {
		log.Fatalf("Error writing timestamp: %s", err)
	}
}

func repos(conn redis.Conn) []string {
	repos, err := redis.Values(conn.Do("LRANGE", "github-backup:repos", 0, 1000))
	if err == redis.ErrNil {
		return []string{}
	}
	if err != nil {
		log.Fatalf("Error retrieving repo list: %s", err)
	}
	r := make([]string, 0, len(repos))
	if err := redis.ScanSlice(repos, &r); err != nil {
		log.Fatalf("Error parsing repo list: %s", err)
	}
	return r
}

func connectRedis(s string) (redis.Conn, error) {
	redisUrl, err := url.Parse(s)
	if err != nil {
		return nil, fmt.Errorf("Could not parse redis url: %s", err)
	}
	if redisUrl.Scheme != "redis" {
		return nil, fmt.Errorf("Unsupported redis scheme %s", redisUrl.Scheme)
	}

	conn, err := redis.Dial("tcp", redisUrl.Host)
	if err != nil {
		return conn, err
	}
	if redisUrl.User != nil {
		pass, ok := redisUrl.User.Password()
		if !ok {
			pass = redisUrl.User.Username()
		}
		_, err := conn.Do("AUTH", pass)
		if err != nil {
			return conn, err
		}
	}
	_, err = conn.Do("EXISTS", "github-backup:lastrun")
	return conn, err
}

func connectFtp(s string) (*goftp.FTP, *url.URL, error) {
	ftpUrl, err := url.Parse(s)
	if err != nil {
		log.Fatalf("Invalid ftp url: %s", err)
	}
	if ftpUrl.Scheme != "ftp" {
		log.Fatalf("Unsupported target scheme %s", ftpUrl.Scheme)
	}
	if !strings.Contains(ftpUrl.Host, ":") {
		ftpUrl.Host += ":21"
	}

	ftp, err := goftp.Connect(ftpUrl.Host)
	if err != nil {
		return ftp, ftpUrl, err
	}
	if ftpUrl.User == nil {
		return ftp, ftpUrl, err
	}
	user := ftpUrl.User.Username()
	pass, _ := ftpUrl.User.Password()
	return ftp, ftpUrl, ftp.Login(user, pass)
}

func downloadRepository(path string) (*bytes.Buffer, error) {
	repo := os.TempDir() + "github-backup"

	if err := os.MkdirAll(repo, os.FileMode(0700)); err != nil {
		return nil, err
	}
	defer os.RemoveAll(repo)

	cmd := exec.Command("git", "clone", "--bare", path)
	cmd.Dir = repo
	if err := cmd.Run(); err != nil {
		return nil, err
	}

	return tarDir(repo)
}

func tarDir(root string) (*bytes.Buffer, error) {
	buf := &bytes.Buffer{}
	gzbuf := gzip.NewWriter(buf)
	defer gzbuf.Close()
	defer gzbuf.Flush()
	archive := tar.NewWriter(gzbuf)
	defer archive.Close()
	defer archive.Flush()
	err := filepath.Walk(root, filepath.WalkFunc(func(path string, info os.FileInfo, err error) error {
		if path == root {
			return nil
		}
		relPath := strings.TrimPrefix(path, root)
		hdr := &tar.Header{
			Name:     strings.TrimPrefix(relPath, "/"),
			Mode:     int64(info.Mode() & os.ModePerm),
			Uid:      1000,
			Gid:      1000,
			Size:     info.Size(),
			Typeflag: tar.TypeReg,
		}
		if info.IsDir() {
			hdr.Typeflag = tar.TypeDir
			hdr.Size = 0
		}

		if err := archive.WriteHeader(hdr); err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()
		if _, err := io.Copy(archive, f); err != nil && err != io.EOF {
			return err
		}
		return nil
	}))
	if err != nil {
		return nil, err
	}
	return buf, nil
}
<commit_msg>Implement SSH key handling<commit_after>package main

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"flag"
	"fmt"
	"io"
	"log"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/dutchcoders/goftp"
	"github.com/garyburd/redigo/redis"
)

var (
	sshKey    = flag.String("key", "", "SSH key to use for cloning")
	ftpUrl    = flag.String("ftp", "", "FTP server to save backups to")
	redisUrl  = flag.String("redis", "", "Address of redis")
	frequency = flag.Duration("frequency", 24*time.Hour, "Frequency of backups")
	force     = flag.Bool("force", false, "Force download")
	help      = flag.Bool("help", false, "Show this help")
)

var (
	badCharacters = regexp.MustCompilePOSIX("[/@:!?*\\&]")
)

func main() {
	flag.Parse()
	if *help {
		flag.PrintDefaults()
		return
	}
	if *ftpUrl == "" || *redisUrl == "" {
		log.Fatalf("-ftp and -redis have to be set")
	}

	if *sshKey != "" {
		if err := addSshKey(*sshKey); err != nil {
			log.Fatalf("Could not add SSH key: %s", err)
		}
	}

	redisConn, err := connectRedis(*redisUrl)
	if err != nil {
		log.Fatalf("Could not connect to redis: %s", err)
	}
	defer redisConn.Close()

	ftpConn, ftpUrl, err := connectFtp(*ftpUrl)
	if err != nil {
		log.Fatalf("Could not connect to FTP server: %s", err)
	}
	defer ftpConn.Close()
	if err := ftpConn.Cwd(ftpUrl.Path); err != nil {
		log.Fatalf("Could not cd to target directory: %s", err)
	}

	for {
		if !*force {
			nextRun := lastRun(redisConn).Add(*frequency)
			if nextRun.After(time.Now()) {
				time.Sleep(nextRun.Sub(time.Now()))
				continue
			}
		}
		*force = false

		log.Printf("Downloading all the repos...")
		repos := repos(redisConn)
		for _, repo := range repos {
			log.Printf("Downloading %s...", repo)
			safeName := badCharacters.ReplaceAllString(repo, "_")
			buf, err := downloadRepository(repo)
			if err != nil {
				log.Printf("Error downloading repository: %s", err)
				continue
			}

			if err := ftpConn.Stor(safeName+".tar.gz", buf); err != nil {
				log.Printf("Error uploading: %s", err)
			}
		}
		log.Printf("Finished.")
		timestampLastRun(redisConn)
	}
}

func lastRun(conn redis.Conn) time.Time {
	ok, err := redis.Bool(conn.Do("EXISTS", "github-backup:lastrun"))
	if err != nil {
		log.Fatalf("Error querying database: %s", err)
	}
	if !ok {
		return time.Unix(0, 0)
	}

	ts, err := redis.String(conn.Do("GET", "github-backup:lastrun"))
	if err != nil {
		log.Fatalf("Error retrieving timestamp: %s", err)
	}
	t, err := time.Parse(time.RFC3339, ts)
	if err != nil {
		log.Fatalf("Error parsing timestamp: %s", err)
	}
	return t
}

func timestampLastRun(conn redis.Conn) {
	_, err := conn.Do("SET", "github-backup:lastrun", time.Now().Format(time.RFC3339))
	if err != nil {
		log.Fatalf("Error writing timestamp: %s", err)
	}
}

func repos(conn redis.Conn) []string {
	repos, err := redis.Values(conn.Do("LRANGE", "github-backup:repos", 0, 1000))
	if err == redis.ErrNil {
		return []string{}
	}
	if err != nil {
		log.Fatalf("Error retrieving repo list: %s", err)
	}
	r := make([]string, 0, len(repos))
	if err := redis.ScanSlice(repos, &r); err != nil {
		log.Fatalf("Error parsing repo list: %s", err)
	}
	return r
}

func connectRedis(s string) (redis.Conn, error) {
	redisUrl, err := url.Parse(s)
	if err != nil {
		return nil, fmt.Errorf("Could not parse redis url: %s", err)
	}
	if redisUrl.Scheme != "redis" {
		return nil, fmt.Errorf("Unsupported redis scheme %s", redisUrl.Scheme)
	}

	conn, err := redis.Dial("tcp", redisUrl.Host)
	if err != nil {
		return conn, err
	}
	if redisUrl.User != nil {
		pass, ok := redisUrl.User.Password()
		if !ok {
			pass = redisUrl.User.Username()
		}
		_, err := conn.Do("AUTH", pass)
		if err != nil {
			return conn, err
		}
	}
	_, err = conn.Do("EXISTS", "github-backup:lastrun")
	return conn, err
}

func connectFtp(s string) (*goftp.FTP, *url.URL, error) {
	ftpUrl, err := url.Parse(s)
	if err != nil {
		log.Fatalf("Invalid ftp url: %s", err)
	}
	if ftpUrl.Scheme != "ftp" {
		log.Fatalf("Unsupported target scheme %s", ftpUrl.Scheme)
	}
	if !strings.Contains(ftpUrl.Host, ":") {
		ftpUrl.Host += ":21"
	}

	ftp, err := goftp.Connect(ftpUrl.Host)
	if err != nil {
		return ftp, ftpUrl, err
	}
	if ftpUrl.User == nil {
		return ftp, ftpUrl, err
	}
	user := ftpUrl.User.Username()
	pass, _ := ftpUrl.User.Password()
	return ftp, ftpUrl, ftp.Login(user, pass)
}

func downloadRepository(path string) (*bytes.Buffer, error) {
	repo := os.TempDir() + "github-backup"

	if err := os.MkdirAll(repo, os.FileMode(0700)); err != nil {
		return nil, err
	}
	defer os.RemoveAll(repo)

	cmd := exec.Command("git", "clone", "--bare", path)
	cmd.Dir = repo
	if err := cmd.Run(); err != nil {
		return nil, err
	}

	return tarDir(repo)
}

func tarDir(root string) (*bytes.Buffer, error) {
	buf := &bytes.Buffer{}
	gzbuf := gzip.NewWriter(buf)
	defer gzbuf.Close()
	defer gzbuf.Flush()
	archive := tar.NewWriter(gzbuf)
	defer archive.Close()
	defer archive.Flush()
	err := filepath.Walk(root, filepath.WalkFunc(func(path string, info os.FileInfo, err error) error {
		if path == root {
			return nil
		}
		relPath := strings.TrimPrefix(path, root)
		hdr := &tar.Header{
			Name:     strings.TrimPrefix(relPath, "/"),
			Mode:     int64(info.Mode() & os.ModePerm),
			Uid:      1000,
			Gid:      1000,
			Size:     info.Size(),
			Typeflag: tar.TypeReg,
		}
		if info.IsDir() {
			hdr.Typeflag = tar.TypeDir
			hdr.Size = 0
		}

		if err := archive.WriteHeader(hdr); err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()
		if _, err := io.Copy(archive, f); err != nil && err != io.EOF {
			return err
		}
		return nil
	}))
	if err != nil {
		return nil, err
	}
	return buf, nil
}

func addSshKey(key string) error {
	cmd := exec.Command("ssh-add", "-")
	cmd.Stdin = strings.NewReader(key)
	return cmd.Run()
}
<|endoftext|>


<commit_before>package apptail

import (
	"encoding/json"
	"fmt"
	"github.com/ActiveState/log"
	"github.com/ActiveState/tail"
	"github.com/ActiveState/zmqpubsub"
	"logyard"
	"os"
	"time"
	"unicode/utf8"
)

// AppInstance is the NATS message sent by dea/stager to notify of new
// instances.
type AppInstance struct {
	AppGUID  string
	AppName  string
	AppSpace string `json:"space"`
	Type     string
	Index    int
	LogFiles map[string]string
}

// AppLogMessage is a struct corresponding to an entry in the app log stream.
type AppLogMessage struct {
	Text          string
	LogFilename   string
	UnixTime      int64
	HumanTime     string
	Source        string // example: app, staging, stackato.dea, stackato.stager
	InstanceIndex int
	AppGUID       string
	AppName       string
	AppSpace      string
	NodeID        string // Host (DEA,stager) IP of this app instance
}

// Publish publishes an AppLogMessage to logyard after sanity checks.
func (line *AppLogMessage) Publish(pub *zmqpubsub.Publisher, allowInvalidJson bool) error {
	// JSON must be a UTF-8 encoded string.
	if !utf8.ValidString(line.Text) {
		line.Text = string([]rune(line.Text))
	}

	data, err := json.Marshal(line)
	if err != nil {
		if allowInvalidJson {
			log.Errorf("Cannot encode %+v into JSON -- %s. Skipping this message", line, err)
		} else {
			return fmt.Errorf("Failed to encode app log record to JSON: ", err)
		}
	}
	key := fmt.Sprintf("apptail.%v", line.AppGUID)
	pub.MustPublish(key, string(data))
	return nil
}

// AppInstanceStarted is a function to be invoked when dea/stager
// starts an application instance.
func AppInstanceStarted(instance *AppInstance, nodeid string) {
	log.Infof("New app instance was started: %+v", instance)

	// convert MB to limit in bytes.
	filesize_limit := GetConfig().FileSizeLimit * 1024 * 1024

	if !(filesize_limit > 0) {
		panic("invalid value for `read_limit' in apptail config")
	}

	for name, filename := range instance.LogFiles {
		go func(name string, filename string) {
			pub := logyard.Broker.NewPublisherMust()
			defer pub.Stop()

			fi, err := os.Stat(filename)
			if err != nil {
				log.Errorf("Cannot stat file (%s); %s", filename, err)
				return
			}
			size := fi.Size()
			limit := filesize_limit
			if size > filesize_limit {
				err := fmt.Errorf("Skipping much of a large log file (%s); size (%v bytes) > read_limit (%v bytes)",
					name, size, filesize_limit)
				// Publish special error message.
				PublishLine(instance, nodeid, name, pub, &tail.Line{
					Text: err.Error(),
					Time: time.Now(),
					Err:  err})
			} else {
				limit = size
			}

			tail, err := tail.TailFile(filename, tail.Config{
				MaxLineSize: GetConfig().MaxRecordSize,
				MustExist:   true,
				Follow:      true,
				Location:    &tail.SeekInfo{-limit, os.SEEK_END},
				ReOpen:      false,
				Poll:        true,
				LimitRate:   GetConfig().RateLimit})
			if err != nil {
				log.Errorf("Cannot tail file (%s); %s", filename, err)
				return
			}

			for line := range tail.Lines {
				PublishLine(instance, nodeid, name, pub, line)
			}

			err = tail.Wait()
			if err != nil {
				log.Error(err)
			}
		}(name, filename)
	}
}

func PublishLine(
	instance *AppInstance, nodeid string,
	name string, pub *zmqpubsub.Publisher,
	line *tail.Line) {

	msg := &AppLogMessage{
		Text:          line.Text,
		LogFilename:   name,
		UnixTime:      line.Time.Unix(),
		HumanTime:     ToHerokuTime(line.Time),
		Source:        instance.Type,
		InstanceIndex: instance.Index,
		AppGUID:       instance.AppGUID,
		AppName:       instance.AppName,
		AppSpace:      instance.AppSpace,
		NodeID:        nodeid,
	}

	if line.Err != nil {
		// Mark this as a special error record, as it is
		// coming from tail, not the app.
		msg.Source = "stackato.apptail"
		msg.LogFilename = ""
		log.Warnf("[%s] %s", instance.AppName, line.Text)
	}

	err := msg.Publish(pub, false)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>use inotify for app logs<commit_after>package apptail

import (
	"encoding/json"
	"fmt"
	"github.com/ActiveState/log"
	"github.com/ActiveState/tail"
	"github.com/ActiveState/zmqpubsub"
	"logyard"
	"os"
	"time"
	"unicode/utf8"
)

// AppInstance is the NATS message sent by dea/stager to notify of new
// instances.
type AppInstance struct {
	AppGUID  string
	AppName  string
	AppSpace string `json:"space"`
	Type     string
	Index    int
	LogFiles map[string]string
}

// AppLogMessage is a struct corresponding to an entry in the app log stream.
type AppLogMessage struct {
	Text          string
	LogFilename   string
	UnixTime      int64
	HumanTime     string
	Source        string // example: app, staging, stackato.dea, stackato.stager
	InstanceIndex int
	AppGUID       string
	AppName       string
	AppSpace      string
	NodeID        string // Host (DEA,stager) IP of this app instance
}

// Publish publishes an AppLogMessage to logyard after sanity checks.
func (line *AppLogMessage) Publish(pub *zmqpubsub.Publisher, allowInvalidJson bool) error {
	// JSON must be a UTF-8 encoded string.
	if !utf8.ValidString(line.Text) {
		line.Text = string([]rune(line.Text))
	}

	data, err := json.Marshal(line)
	if err != nil {
		if allowInvalidJson {
			log.Errorf("Cannot encode %+v into JSON -- %s. Skipping this message", line, err)
		} else {
			return fmt.Errorf("Failed to encode app log record to JSON: ", err)
		}
	}
	key := fmt.Sprintf("apptail.%v", line.AppGUID)
	pub.MustPublish(key, string(data))
	return nil
}

// AppInstanceStarted is a function to be invoked when dea/stager
// starts an application instance.
func AppInstanceStarted(instance *AppInstance, nodeid string) {
	log.Infof("Tailing %v logs for %v:%v -- %+v",
		instance.Type, instance.AppName, instance.Index, instance)

	// convert MB to limit in bytes.
	filesize_limit := GetConfig().FileSizeLimit * 1024 * 1024

	if !(filesize_limit > 0) {
		panic("invalid value for `read_limit' in apptail config")
	}

	for name, filename := range instance.LogFiles {
		go func(name string, filename string) {
			pub := logyard.Broker.NewPublisherMust()
			defer pub.Stop()

			fi, err := os.Stat(filename)
			if err != nil {
				log.Errorf("Cannot stat file (%s); %s", filename, err)
				return
			}
			size := fi.Size()
			limit := filesize_limit
			if size > filesize_limit {
				err := fmt.Errorf("Skipping much of a large log file (%s); size (%v bytes) > read_limit (%v bytes)",
					name, size, filesize_limit)
				// Publish special error message.
				PublishLine(instance, nodeid, name, pub, &tail.Line{
					Text: err.Error(),
					Time: time.Now(),
					Err:  err})
			} else {
				limit = size
			}

			tail, err := tail.TailFile(filename, tail.Config{
				MaxLineSize: GetConfig().MaxRecordSize,
				MustExist:   true,
				Follow:      true,
				Location:    &tail.SeekInfo{-limit, os.SEEK_END},
				ReOpen:      false,
				Poll:        false,
				LimitRate:   GetConfig().RateLimit})
			if err != nil {
				log.Errorf("Cannot tail file (%s); %s", filename, err)
				return
			}

			for line := range tail.Lines {
				PublishLine(instance, nodeid, name, pub, line)
			}

			err = tail.Wait()
			if err != nil {
				log.Error(err)
			}
		}(name, filename)
	}
}

func PublishLine(
	instance *AppInstance, nodeid string,
	name string, pub *zmqpubsub.Publisher,
	line *tail.Line) {

	msg := &AppLogMessage{
		Text:          line.Text,
		LogFilename:   name,
		UnixTime:      line.Time.Unix(),
		HumanTime:     ToHerokuTime(line.Time),
		Source:        instance.Type,
		InstanceIndex: instance.Index,
		AppGUID:       instance.AppGUID,
		AppName:       instance.AppName,
		AppSpace:      instance.AppSpace,
		NodeID:        nodeid,
	}

	if line.Err != nil {
		// Mark this as a special error record, as it is
		// coming from tail, not the app.
		msg.Source = "stackato.apptail"
		msg.LogFilename = ""
		log.Warnf("[%s] %s", instance.AppName, line.Text)
	}

	err := msg.Publish(pub, false)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>// Copyright 2015 The Vanadium 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 main

import (
	"fmt"
	"os"
	"testing"
	"time"

	"v.io/v23"
	"v.io/v23/options"
	"v.io/x/lib/gosh"
	"v.io/x/ref/lib/signals"
	"v.io/x/ref/services/mounttable/mounttablelib"
	"v.io/x/ref/test/v23test"
)

// TODO(sadovsky): Switch to using v23test.Shell.StartRootMountTable.
var rootMT = gosh.RegisterFunc("rootMT", func() error {
	ctx, shutdown := v23.Init()
	defer shutdown()

	mt, err := mounttablelib.NewMountTableDispatcher(ctx, "", "", "mounttable")
	if err != nil {
		return fmt.Errorf("mounttable.NewMountTableDispatcher failed: %s", err)
	}
	_, server, err := v23.WithNewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
	if err != nil {
		return fmt.Errorf("root failed: %v", err)
	}
	fmt.Printf("PID=%d\n", os.Getpid())
	for _, ep := range server.Status().Endpoints {
		fmt.Printf("MT_NAME=%s\n", ep.Name())
	}
	<-signals.ShutdownOnSignals(ctx)
	return nil
})

// Asserts that the channel contains members with expected names and no others.
func AssertMembersWithNames(channel *channel, expectedNames []string, retry bool) error {

	waitForN := func(expected int) ([]*member, error) {
		deadline := time.Now().Add(5 * time.Minute)
		for {
			members, err := channel.getMembers()
			if err != nil || len(members) != expected {
				if retry {
					if time.Now().After(deadline) {
						return nil, fmt.Errorf("timed out expecting %d members", expected)
					}
					time.Sleep(10 * time.Millisecond)
					continue
				}
				if err != nil {
					return nil, fmt.Errorf("channel.getMembers() failed: %v", err)
				}
				return nil, fmt.Errorf("Wrong number of members.  Expected %v, actual %v.", len(members), expected)
			}
			return members, nil
		}
	}

	members, err := waitForN(len(expectedNames))
	if err != nil {
		return err
	}

	for _, expectedName := range expectedNames {
		found := false
		for _, member := range members {
			if member.Name == expectedName {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("Expected member with name %v, but did not find one.", expectedName)
		}
	}
	return nil
}

func TestMembers(t *testing.T) {
	sh := v23test.NewShell(t, v23test.Opts{})
	defer sh.Cleanup()
	ctx := sh.Ctx

	c := sh.FuncCmd(rootMT)
	c.Args = append(c.Args, "--v23.tcp.address=127.0.0.1:0")
	c.Start()
	c.S.ExpectVar("PID")
	mounttable := c.S.ExpectVar("MT_NAME")

	proxy := ""
	path := "path/to/channel"

	// Create a new channel.
	channel, err := newChannel(ctx, mounttable, proxy, path)
	if err != nil {
		t.Fatalf("newChannel(%v, %v, %v) failed: %v", mounttable, proxy, path, err)
	}

	// New channel should be empty.
	if err := AssertMembersWithNames(channel, []string{}, false); err != nil {
		t.Error(err)
	}

	// Join the channel.
	if err := channel.join(); err != nil {
		t.Fatalf("channel.join() failed: %v", err)
	}

	// Channel should contain only current user.
	if err := AssertMembersWithNames(channel, []string{channel.UserName()}, true); err != nil {
		t.Error(err)
	}

	// Create and join the channel a second time.
	channel2, err := newChannel(ctx, mounttable, proxy, path)
	if err != nil {
		t.Fatalf("newChannel(%v, %v, %v) failed: %v", mounttable, proxy, path, err)
	}
	if err := channel2.join(); err != nil {
		t.Fatalf("channel2.join() failed: %v", err)
	}

	// Channel should contain both users.
	if err := AssertMembersWithNames(channel, []string{channel.UserName(), channel2.UserName()}, true); err != nil {
		t.Error(err)
	}

	// Leave first instance of channel.
	if err := channel.leave(); err != nil {
		t.Fatalf("channel.leave() failed: %v", err)
	}

	// Channel should contain only second user.
	if err := AssertMembersWithNames(channel, []string{channel2.UserName()}, true); err != nil {
		t.Error(err)
	}

	// Leave second instance of channel.
	if err := channel2.leave(); err != nil {
		t.Fatalf("channel2.leave() failed: %v", err)
	}

	// Channel should be empty.
	if err := AssertMembersWithNames(channel, []string{}, true); err != nil {
		t.Error(err)
	}
}

func TestBroadcastMessage(t *testing.T) {
	sh := v23test.NewShell(t, v23test.Opts{})
	defer sh.Cleanup()
	ctx := sh.Ctx

	c := sh.FuncCmd(rootMT)
	c.Args = append(c.Args, "--v23.tcp.address=127.0.0.1:0")
	c.Start()
	c.S.ExpectVar("PID")
	mounttable := c.S.ExpectVar("MT_NAME")

	proxy := ""
	path := "path/to/channel"

	channel, err := newChannel(ctx, mounttable, proxy, path)
	if err != nil {
		t.Fatalf("newChannel(%v, %v, %v) failed: %v", mounttable, proxy, path, err)
	}

	defer channel.leave()

	if err := channel.join(); err != nil {
		t.Fatalf("channel.join() failed: %v", err)
	}

	message := "Hello Vanadium world!"

	go func() {
		// Call getMembers(), which will set channel.members, used by
		// channel.broadcastMessage().
		deadline := time.Now().Add(time.Minute)
		for {
			m, err := channel.getMembers()
			if err != nil {
				t.Fatalf("channel.getMembers() failed: %v", err)
			}
			if len(m) > 0 {
				break
			}
			if time.Now().After(deadline) {
				t.Fatalf("channel.getMembers: timed out getting a member")
			}
			time.Sleep(10 * time.Millisecond)
		}
		if err := channel.broadcastMessage(message); err != nil {
			t.Fatalf("channel.broadcastMessage(%v) failed: %v", message, err)
		}
	}()

	select {
	case <-time.After(10 * time.Second):
		t.Errorf("Timeout waiting for message to be received.")
	case m := <-channel.messages:
		if m.Text != message {
			t.Errorf("Expected message text to be %v but got %v", message, m.Text)
		}
		if got, want := m.SenderName, channel.UserName(); got != want {
			t.Errorf("Got m.SenderName = %v, want %v", got, want)
		}
	}
}

func TestMain(m *testing.M) {
	v23test.TestMain(m)
}
<commit_msg>chat: gosh: switch from Logf/Fatalf to TB, and related<commit_after>// Copyright 2015 The Vanadium 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 main

import (
	"fmt"
	"os"
	"testing"
	"time"

	"v.io/v23"
	"v.io/v23/options"
	"v.io/x/lib/gosh"
	"v.io/x/ref/lib/signals"
	"v.io/x/ref/services/mounttable/mounttablelib"
	"v.io/x/ref/test/v23test"
)

// TODO(sadovsky): Switch to using v23test.Shell.StartRootMountTable.
var rootMT = gosh.RegisterFunc("rootMT", func() error {
	ctx, shutdown := v23.Init()
	defer shutdown()

	mt, err := mounttablelib.NewMountTableDispatcher(ctx, "", "", "mounttable")
	if err != nil {
		return fmt.Errorf("mounttable.NewMountTableDispatcher failed: %s", err)
	}
	_, server, err := v23.WithNewDispatchingServer(ctx, "", mt, options.ServesMountTable(true))
	if err != nil {
		return fmt.Errorf("root failed: %v", err)
	}
	fmt.Printf("PID=%d\n", os.Getpid())
	for _, ep := range server.Status().Endpoints {
		fmt.Printf("MT_NAME=%s\n", ep.Name())
	}
	<-signals.ShutdownOnSignals(ctx)
	return nil
})

// Asserts that the channel contains members with expected names and no others.
func AssertMembersWithNames(channel *channel, expectedNames []string, retry bool) error {

	waitForN := func(expected int) ([]*member, error) {
		deadline := time.Now().Add(5 * time.Minute)
		for {
			members, err := channel.getMembers()
			if err != nil || len(members) != expected {
				if retry {
					if time.Now().After(deadline) {
						return nil, fmt.Errorf("timed out expecting %d members", expected)
					}
					time.Sleep(10 * time.Millisecond)
					continue
				}
				if err != nil {
					return nil, fmt.Errorf("channel.getMembers() failed: %v", err)
				}
				return nil, fmt.Errorf("Wrong number of members.  Expected %v, actual %v.", len(members), expected)
			}
			return members, nil
		}
	}

	members, err := waitForN(len(expectedNames))
	if err != nil {
		return err
	}

	for _, expectedName := range expectedNames {
		found := false
		for _, member := range members {
			if member.Name == expectedName {
				found = true
				break
			}
		}
		if !found {
			return fmt.Errorf("Expected member with name %v, but did not find one.", expectedName)
		}
	}
	return nil
}

func TestMembers(t *testing.T) {
	sh := v23test.NewShell(t, nil)
	defer sh.Cleanup()
	ctx := sh.Ctx

	c := sh.FuncCmd(rootMT)
	c.Args = append(c.Args, "--v23.tcp.address=127.0.0.1:0")
	c.Start()
	c.S.ExpectVar("PID")
	mounttable := c.S.ExpectVar("MT_NAME")

	proxy := ""
	path := "path/to/channel"

	// Create a new channel.
	channel, err := newChannel(ctx, mounttable, proxy, path)
	if err != nil {
		t.Fatalf("newChannel(%v, %v, %v) failed: %v", mounttable, proxy, path, err)
	}

	// New channel should be empty.
	if err := AssertMembersWithNames(channel, []string{}, false); err != nil {
		t.Error(err)
	}

	// Join the channel.
	if err := channel.join(); err != nil {
		t.Fatalf("channel.join() failed: %v", err)
	}

	// Channel should contain only current user.
	if err := AssertMembersWithNames(channel, []string{channel.UserName()}, true); err != nil {
		t.Error(err)
	}

	// Create and join the channel a second time.
	channel2, err := newChannel(ctx, mounttable, proxy, path)
	if err != nil {
		t.Fatalf("newChannel(%v, %v, %v) failed: %v", mounttable, proxy, path, err)
	}
	if err := channel2.join(); err != nil {
		t.Fatalf("channel2.join() failed: %v", err)
	}

	// Channel should contain both users.
	if err := AssertMembersWithNames(channel, []string{channel.UserName(), channel2.UserName()}, true); err != nil {
		t.Error(err)
	}

	// Leave first instance of channel.
	if err := channel.leave(); err != nil {
		t.Fatalf("channel.leave() failed: %v", err)
	}

	// Channel should contain only second user.
	if err := AssertMembersWithNames(channel, []string{channel2.UserName()}, true); err != nil {
		t.Error(err)
	}

	// Leave second instance of channel.
	if err := channel2.leave(); err != nil {
		t.Fatalf("channel2.leave() failed: %v", err)
	}

	// Channel should be empty.
	if err := AssertMembersWithNames(channel, []string{}, true); err != nil {
		t.Error(err)
	}
}

func TestBroadcastMessage(t *testing.T) {
	sh := v23test.NewShell(t, nil)
	defer sh.Cleanup()
	ctx := sh.Ctx

	c := sh.FuncCmd(rootMT)
	c.Args = append(c.Args, "--v23.tcp.address=127.0.0.1:0")
	c.Start()
	c.S.ExpectVar("PID")
	mounttable := c.S.ExpectVar("MT_NAME")

	proxy := ""
	path := "path/to/channel"

	channel, err := newChannel(ctx, mounttable, proxy, path)
	if err != nil {
		t.Fatalf("newChannel(%v, %v, %v) failed: %v", mounttable, proxy, path, err)
	}

	defer channel.leave()

	if err := channel.join(); err != nil {
		t.Fatalf("channel.join() failed: %v", err)
	}

	message := "Hello Vanadium world!"

	go func() {
		// Call getMembers(), which will set channel.members, used by
		// channel.broadcastMessage().
		deadline := time.Now().Add(time.Minute)
		for {
			m, err := channel.getMembers()
			if err != nil {
				t.Fatalf("channel.getMembers() failed: %v", err)
			}
			if len(m) > 0 {
				break
			}
			if time.Now().After(deadline) {
				t.Fatalf("channel.getMembers: timed out getting a member")
			}
			time.Sleep(10 * time.Millisecond)
		}
		if err := channel.broadcastMessage(message); err != nil {
			t.Fatalf("channel.broadcastMessage(%v) failed: %v", message, err)
		}
	}()

	select {
	case <-time.After(10 * time.Second):
		t.Errorf("Timeout waiting for message to be received.")
	case m := <-channel.messages:
		if m.Text != message {
			t.Errorf("Expected message text to be %v but got %v", message, m.Text)
		}
		if got, want := m.SenderName, channel.UserName(); got != want {
			t.Errorf("Got m.SenderName = %v, want %v", got, want)
		}
	}
}

func TestMain(m *testing.M) {
	v23test.TestMain(m)
}
<|endoftext|>