<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/igungor/go-putio/putio"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

const DefaultUserAgent = "putiofs - FUSE bridge to Put.io"
const AttrValidityDuration = time.Hour

type FileSystem struct {
	logger  *Logger
	putio   *putio.Client
	account putio.AccountInfo
}

var (
	_ fs.FS         = (*FileSystem)(nil)
	_ fs.FSStatfser = (*FileSystem)(nil)
)

func NewFileSystem(token string, debug bool) *FileSystem {
	oauthClient := oauth2.NewClient(
		oauth2.NoContext,
		oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}),
	)
	client := putio.NewClient(oauthClient)
	client.UserAgent = DefaultUserAgent

	return &FileSystem{
		putio:  client,
		logger: NewLogger("putiofs: ", debug),
	}
}

func (f *FileSystem) List(ctx context.Context, id int64) ([]putio.File, error) {
	files, _, err := f.putio.Files.List(ctx, id)
	return files, err
}

func (f *FileSystem) Get(ctx context.Context, id int64) (putio.File, error) {
	return f.putio.Files.Get(ctx, id)
}

func (f *FileSystem) Delete(ctx context.Context, id int64) error {
	return f.putio.Files.Delete(ctx, id)
}

func (f *FileSystem) Download(ctx context.Context, id int64, rangeHeader http.Header) (io.ReadCloser, error) {
	return f.putio.Files.Download(ctx, id, true, nil)
}

func (f *FileSystem) Rename(ctx context.Context, id int64, newname string) error {
	return f.putio.Files.Rename(ctx, id, newname)
}

func (f *FileSystem) Move(ctx context.Context, parent int64, fileid int64) error {
	return f.putio.Files.Move(ctx, parent, fileid)
}

func (f *FileSystem) Root() (fs.Node, error) {
	f.logger.Debugf("Root() request\n")

	root, err := f.Get(nil, 0)
	if err != nil {
		f.logger.Printf("Root failed: %v\n", err)
		return nil, fuse.EIO
	}

	account, err := f.putio.Account.Info(nil)
	if err != nil {
		f.logger.Debugf("Fetching account info failed: %v\n", err)
		return nil, fuse.EIO
	}
	f.account = account

	return &Dir{
		fs:   f,
		ID:   root.ID,
		Name: root.Filename,
		Size: root.Filesize,
	}, nil
}

func (f *FileSystem) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
	// each block size is 4096 bytes by default.
	const unit = uint64(4096)

	resp.Bsize = uint32(unit)
	resp.Blocks = uint64(f.account.Disk.Size) / unit
	resp.Bavail = uint64(f.account.Disk.Avail) / unit
	resp.Bfree = uint64(f.account.Disk.Avail) / unit

	return nil
}

type Dir struct {
	fs *FileSystem

	ID   int64
	Name string
	Size int64
}

var (
	_ fs.Node                = (*Dir)(nil)
	_ fs.NodeRequestLookuper = (*Dir)(nil)
	_ fs.NodeRemover         = (*Dir)(nil)
	_ fs.HandleReadDirAller  = (*Dir)(nil)
)

func (d *Dir) String() string {
	return fmt.Sprintf("<%v - %q>", d.ID, d.Name)
}

func (d *Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	d.fs.logger.Debugf("Directory stat for %v\n", d)

	attr.Mode = os.ModeDir | 0755
	attr.Size = uint64(d.Size)
	return nil
}

// Lookup looks up a specific entry in the current directory.
func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	// skip junk files to quiet log noise
	filename := req.Name
	if isJunkFile(filename) {
		return nil, fuse.ENOENT
	}

	d.fs.logger.Debugf("Directory lookup for %v in %v\n", req.Name, d)

	// reserved filename lookups
	switch filename {
	case ".account":
		acc, _ := json.MarshalIndent(d.fs.account, "", "  ")
		return staticFileNode(string(acc)), nil
	}

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Lookup failed for %v: %v\n", d, err)
		return nil, fuse.EIO
	}

	for _, file := range files {
		if file.Filename == filename {
			if file.IsDir() {
				return &Dir{
					fs:   d.fs,
					ID:   file.ID,
					Name: file.Filename,
					Size: file.Filesize,
				}, nil
			}
			return &File{
				fs:   d.fs,
				File: &file,
			}, nil
		}
	}

	return nil, fuse.ENOENT
}

func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	d.fs.logger.Debugf("Directory listing for %v\n", d)

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return nil, fuse.EIO
	}

	var entries []fuse.Dirent
	for _, file := range files {
		var entry fuse.Dirent

		var dt fuse.DirentType
		if file.IsDir() {
			dt = fuse.DT_Dir
		} else {
			dt = fuse.DT_File
		}
		entry = fuse.Dirent{
			Name: file.Filename,
			Type: dt,
		}
		entries = append(entries, entry)
	}
	return entries, nil
}

// Remove removes the entry with the given name from the current directory. The
// entry to be removed may correspond to a file or to a directory.
func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	d.fs.logger.Debugf("Remove request for %v in %v\n", req.Name, d)

	filename := req.Name
	if filename == "/" || filename == "Your Files" {
		return fuse.EIO
	}

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return fuse.EIO
	}

	for _, file := range files {
		if file.Filename == filename {
			return d.fs.Delete(ctx, file.ID)
		}
	}

	return fuse.ENOENT
}

func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
	newdir, ok := newDir.(*Dir)
	if !ok {
		d.fs.logger.Debugln("Error converting Node to Dir")
		return fuse.EIO
	}

	oldname := req.OldName
	newname := req.NewName

	d.fs.logger.Debugf("origdirid: %v, newDirid: %v, old: %v, newname: %v\n", d, newdir, req.OldName, req.NewName)

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return fuse.EIO
	}

	fileid := int64(-1)
	for _, file := range files {
		if file.Filename == oldname {
			fileid = file.ID
		}
	}

	if fileid < 0 {
		d.fs.logger.Printf("File not found %v: %v\n", oldname, err)
		return fuse.ENOENT
	}

	// request is to ust change the name
	if newdir.ID == d.ID {
		err := d.rename(ctx, fileid, oldname, newname)
		if err != nil {
			d.fs.logger.Printf("Rename failed: %v\n", err)
			return fuse.EIO
		}
	}

	// file/directory moved into another directory
	err = d.move(ctx, fileid, newdir.ID, oldname, newname)
	if err != nil {
		d.fs.logger.Printf("Move failed: %v\n", err)
		return fuse.EIO
	}
	return nil
}

func (d *Dir) rename(ctx context.Context, fileid int64, oldname, newname string) error {
	d.fs.logger.Debugf("Rename request for %v:%v -> %v\n", fileid, oldname, newname)

	if oldname == newname {
		return nil
	}

	return d.fs.Rename(ctx, fileid, newname)
}

func (d *Dir) move(ctx context.Context, fileid int64, parent int64, oldname string, newname string) error {
	d.fs.logger.Debugf("Move request for %v:%v -> %v:%v\n", fileid, oldname, parent, newname)

	err := d.fs.Move(ctx, parent, fileid)
	if err != nil {
		d.fs.logger.Printf("Error moving file: %v\n", err)
		return fuse.EIO
	}

	if oldname != newname {
		return d.fs.Rename(ctx, fileid, newname)
	}

	return nil
}

type File struct {
	fs *FileSystem

	*putio.File
}

var (
	_ fs.Node       = (*File)(nil)
	_ fs.NodeOpener = (*File)(nil)
)

func (f *File) Attr(ctx context.Context, attr *fuse.Attr) error {
	f.fs.logger.Debugf("File stat for %v\n", f)

	attr.Mode = os.ModePerm | 0644
	attr.Size = uint64(f.Filesize)
	attr.Ctime = f.CreatedAt.Time
	return nil
}

func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	f.fs.logger.Debugf("File open request for %v\n", f)

	return &FileHandle{
		fs: f.fs,
		f:  f,
	}, nil
}

type FileHandle struct {
	fs     *FileSystem
	f      *File
	offset int64 // Read offset
	body   io.ReadCloser
}

var (
	_ fs.HandleReader   = (*FileHandle)(nil)
	_ fs.HandleReleaser = (*FileHandle)(nil)
)

func (fh *FileHandle) String() string {
	return fmt.Sprintf("<%v - %q>", fh.f.ID, fh.f.Filename)
}

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	fh.fs.logger.Debugf("FileHandler Read request. Handle offset: %v, Request offset: %v\n", fh.offset, req.Offset)

	if req.Offset >= fh.f.Filesize {
		return fuse.EIO
	}

	var renew bool
	switch {
	case fh.body == nil: // initial read
		renew = true
	case fh.offset != req.Offset: // seek occurred
		renew = true
		_ = fh.body.Close()
	}

	if renew {
		rangeHeader := http.Header{}
		rangeHeader.Set("Range", fmt.Sprintf("bytes=%v-%v", req.Offset, req.Offset+int64(req.Size)))
		body, err := fh.fs.Download(nil, fh.f.ID, rangeHeader)
		if err != nil {
			fh.fs.logger.Printf("Error downloading %v-%v: %v\n", fh.f.ID, fh.f.Filename, err)
			return fuse.EIO
		}
		// reset offset and the body
		fh.offset = req.Offset
		fh.body = body
	}

	buf := make([]byte, req.Size)
	n, err := io.ReadFull(fh.body, buf)
	if err == io.ErrUnexpectedEOF || err == io.EOF {
		err = nil
	}
	if err != nil {
		fh.fs.logger.Printf("Error reading file %v: %v\n", fh, err)
		return err
	}

	fh.offset += int64(n)
	resp.Data = buf[:n]
	return nil
}

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	fh.fs.logger.Debugln("FileHandler Release request")

	fh.offset = 0
	if fh.body != nil {
		return fh.body.Close()
	}
	return nil
}

type staticFileNode string

var (
	_ fs.Node         = (*staticFileNode)(nil)
	_ fs.HandleReader = (*staticFileNode)(nil)
)

func (s staticFileNode) Attr(ctx context.Context, attr *fuse.Attr) error {
	attr.Mode = 0400
	attr.Size = uint64(len(s))

	return nil
}

func (s staticFileNode) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	if req.Offset > int64(len(s)) {
		return nil
	}

	s = s[req.Offset:]
	size := req.Size
	if size > len(s) {
		size = len(s)
	}
	resp.Data = make([]byte, size)
	copy(resp.Data, s)
	return nil
}

var junkFilePrefixes = []string{
	"._",
	".DS_Store",
	".Spotlight-",
	".git",
	".hidden",
	".metadata_never_index",
	".nomedia",
	".envrc",
}

// isJunkFile reports whether the given file path is considered useless. MacOSX
// Finder is looking for a few hidden files per a file stat request. So this is
// used to speed things a bit.
func isJunkFile(abspath string) bool {
	_, filename := filepath.Split(abspath)
	for _, v := range junkFilePrefixes {
		if strings.HasPrefix(filename, v) {
			return true
		}
	}
	return false
}
<commit_msg>Set uid/gid<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/igungor/go-putio/putio"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

const DefaultUserAgent = "putiofs - FUSE bridge to Put.io"
const AttrValidityDuration = time.Hour

type FileSystem struct {
	logger  *Logger
	putio   *putio.Client
	account putio.AccountInfo
}

var (
	_ fs.FS         = (*FileSystem)(nil)
	_ fs.FSStatfser = (*FileSystem)(nil)
)

func NewFileSystem(token string, debug bool) *FileSystem {
	oauthClient := oauth2.NewClient(
		oauth2.NoContext,
		oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}),
	)
	client := putio.NewClient(oauthClient)
	client.UserAgent = DefaultUserAgent

	return &FileSystem{
		putio:  client,
		logger: NewLogger("putiofs: ", debug),
	}
}

func (f *FileSystem) List(ctx context.Context, id int64) ([]putio.File, error) {
	files, _, err := f.putio.Files.List(ctx, id)
	return files, err
}

func (f *FileSystem) Get(ctx context.Context, id int64) (putio.File, error) {
	return f.putio.Files.Get(ctx, id)
}

func (f *FileSystem) Delete(ctx context.Context, id int64) error {
	return f.putio.Files.Delete(ctx, id)
}

func (f *FileSystem) Download(ctx context.Context, id int64, rangeHeader http.Header) (io.ReadCloser, error) {
	return f.putio.Files.Download(ctx, id, true, nil)
}

func (f *FileSystem) Rename(ctx context.Context, id int64, newname string) error {
	return f.putio.Files.Rename(ctx, id, newname)
}

func (f *FileSystem) Move(ctx context.Context, parent int64, fileid int64) error {
	return f.putio.Files.Move(ctx, parent, fileid)
}

func (f *FileSystem) Root() (fs.Node, error) {
	f.logger.Debugf("Root() request\n")

	root, err := f.Get(nil, 0)
	if err != nil {
		f.logger.Printf("Root failed: %v\n", err)
		return nil, fuse.EIO
	}

	account, err := f.putio.Account.Info(nil)
	if err != nil {
		f.logger.Debugf("Fetching account info failed: %v\n", err)
		return nil, fuse.EIO
	}
	f.account = account

	return &Dir{
		fs:   f,
		ID:   root.ID,
		Name: root.Filename,
		Size: root.Filesize,
	}, nil
}

func (f *FileSystem) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
	// each block size is 4096 bytes by default.
	const unit = uint64(4096)

	resp.Bsize = uint32(unit)
	resp.Blocks = uint64(f.account.Disk.Size) / unit
	resp.Bavail = uint64(f.account.Disk.Avail) / unit
	resp.Bfree = uint64(f.account.Disk.Avail) / unit

	return nil
}

type Dir struct {
	fs *FileSystem

	ID   int64
	Name string
	Size int64
}

var (
	_ fs.Node                = (*Dir)(nil)
	_ fs.NodeRequestLookuper = (*Dir)(nil)
	_ fs.NodeRemover         = (*Dir)(nil)
	_ fs.HandleReadDirAller  = (*Dir)(nil)
)

func (d *Dir) String() string {
	return fmt.Sprintf("<%v - %q>", d.ID, d.Name)
}

func (d *Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	d.fs.logger.Debugf("Directory stat for %v\n", d)

	attr.Mode = os.ModeDir | 0755
	attr.Uid = uint32(os.Getuid())
	attr.Gid = uint32(os.Getgid())
	attr.Size = uint64(d.Size)
	return nil
}

// Lookup looks up a specific entry in the current directory.
func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	// skip junk files to quiet log noise
	filename := req.Name
	if isJunkFile(filename) {
		return nil, fuse.ENOENT
	}

	d.fs.logger.Debugf("Directory lookup for %v in %v\n", req.Name, d)

	// reserved filename lookups
	switch filename {
	case ".account":
		acc, _ := json.MarshalIndent(d.fs.account, "", "  ")
		return staticFileNode(string(acc)), nil
	}

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Lookup failed for %v: %v\n", d, err)
		return nil, fuse.EIO
	}

	for _, file := range files {
		if file.Filename == filename {
			if file.IsDir() {
				return &Dir{
					fs:   d.fs,
					ID:   file.ID,
					Name: file.Filename,
					Size: file.Filesize,
				}, nil
			}
			return &File{
				fs:   d.fs,
				File: &file,
			}, nil
		}
	}

	return nil, fuse.ENOENT
}

func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	d.fs.logger.Debugf("Directory listing for %v\n", d)

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return nil, fuse.EIO
	}

	var entries []fuse.Dirent
	for _, file := range files {
		var entry fuse.Dirent

		var dt fuse.DirentType
		if file.IsDir() {
			dt = fuse.DT_Dir
		} else {
			dt = fuse.DT_File
		}
		entry = fuse.Dirent{
			Name: file.Filename,
			Type: dt,
		}
		entries = append(entries, entry)
	}
	return entries, nil
}

// Remove removes the entry with the given name from the current directory. The
// entry to be removed may correspond to a file or to a directory.
func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	d.fs.logger.Debugf("Remove request for %v in %v\n", req.Name, d)

	filename := req.Name
	if filename == "/" || filename == "Your Files" {
		return fuse.EIO
	}

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return fuse.EIO
	}

	for _, file := range files {
		if file.Filename == filename {
			return d.fs.Delete(ctx, file.ID)
		}
	}

	return fuse.ENOENT
}

func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
	newdir, ok := newDir.(*Dir)
	if !ok {
		d.fs.logger.Debugln("Error converting Node to Dir")
		return fuse.EIO
	}

	oldname := req.OldName
	newname := req.NewName

	d.fs.logger.Debugf("origdirid: %v, newDirid: %v, old: %v, newname: %v\n", d, newdir, req.OldName, req.NewName)

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return fuse.EIO
	}

	fileid := int64(-1)
	for _, file := range files {
		if file.Filename == oldname {
			fileid = file.ID
		}
	}
	if fileid < 0 {
		d.fs.logger.Printf("File not found %v: %v\n", oldname, err)
		return fuse.ENOENT
	}

	// request is to ust change the name
	if newdir.ID == d.ID {
		err := d.rename(ctx, fileid, oldname, newname)
		if err != nil {
			d.fs.logger.Printf("Rename failed: %v\n", err)
			return fuse.EIO
		}
	}

	// file/directory moved into another directory
	err = d.move(ctx, fileid, newdir.ID, oldname, newname)
	if err != nil {
		d.fs.logger.Printf("Move failed: %v\n", err)
		return fuse.EIO
	}
	return nil
}

func (d *Dir) rename(ctx context.Context, fileid int64, oldname, newname string) error {
	d.fs.logger.Debugf("Rename request for %v:%v -> %v\n", fileid, oldname, newname)

	if oldname == newname {
		return nil
	}

	return d.fs.Rename(ctx, fileid, newname)
}

func (d *Dir) move(ctx context.Context, fileid int64, parent int64, oldname string, newname string) error {
	d.fs.logger.Debugf("Move request for %v:%v -> %v:%v\n", fileid, oldname, parent, newname)

	err := d.fs.Move(ctx, parent, fileid)
	if err != nil {
		d.fs.logger.Printf("Error moving file: %v\n", err)
		return fuse.EIO
	}

	if oldname != newname {
		return d.fs.Rename(ctx, fileid, newname)
	}

	return nil
}

type File struct {
	fs *FileSystem

	*putio.File
}

var (
	_ fs.Node       = (*File)(nil)
	_ fs.NodeOpener = (*File)(nil)
)

func (f *File) Attr(ctx context.Context, attr *fuse.Attr) error {
	f.fs.logger.Debugf("File stat for %v\n", f)

	attr.Mode = 0400
	attr.Uid = uint32(os.Getuid())
	attr.Gid = uint32(os.Getgid())
	attr.Size = uint64(f.Filesize)
	attr.Ctime = f.CreatedAt.Time
	attr.Mtime = f.CreatedAt.Time
	attr.Crtime = f.CreatedAt.Time
	return nil
}

func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	f.fs.logger.Debugf("File open request for %v\n", f)

	return &FileHandle{
		fs: f.fs,
		f:  f,
	}, nil
}

type FileHandle struct {
	fs     *FileSystem
	f      *File
	offset int64 // Read offset
	body   io.ReadCloser
}

var (
	_ fs.HandleReader   = (*FileHandle)(nil)
	_ fs.HandleReleaser = (*FileHandle)(nil)
)

func (fh *FileHandle) String() string {
	return fmt.Sprintf("<%v - %q>", fh.f.ID, fh.f.Filename)
}

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	fh.fs.logger.Debugf("FileHandler Read request. Handle offset: %v, Request offset: %v\n", fh.offset, req.Offset)

	if req.Offset >= fh.f.Filesize {
		return fuse.EIO
	}

	var renew bool
	switch {
	case fh.body == nil: // initial read
		renew = true
	case fh.offset != req.Offset: // seek occurred
		renew = true
		_ = fh.body.Close()
	}

	if renew {
		rangeHeader := http.Header{}
		rangeHeader.Set("Range", fmt.Sprintf("bytes=%v-%v", req.Offset, req.Offset+int64(req.Size)))
		body, err := fh.fs.Download(nil, fh.f.ID, rangeHeader)
		if err != nil {
			fh.fs.logger.Printf("Error downloading %v-%v: %v\n", fh.f.ID, fh.f.Filename, err)
			return fuse.EIO
		}
		// reset offset and the body
		fh.offset = req.Offset
		fh.body = body
	}

	buf := make([]byte, req.Size)
	n, err := io.ReadFull(fh.body, buf)
	if err == io.ErrUnexpectedEOF || err == io.EOF {
		err = nil
	}
	if err != nil {
		fh.fs.logger.Printf("Error reading file %v: %v\n", fh, err)
		return err
	}

	fh.offset += int64(n)
	resp.Data = buf[:n]
	return nil
}

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	fh.fs.logger.Debugln("FileHandler Release request")

	fh.offset = 0
	if fh.body != nil {
		return fh.body.Close()
	}
	return nil
}

type staticFileNode string

var (
	_ fs.Node         = (*staticFileNode)(nil)
	_ fs.HandleReader = (*staticFileNode)(nil)
)

func (s staticFileNode) Attr(ctx context.Context, attr *fuse.Attr) error {
	attr.Mode = 0400
	attr.Uid = uint32(os.Getuid())
	attr.Gid = uint32(os.Getgid())
	attr.Size = uint64(len(s))
	return nil
}

func (s staticFileNode) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	if req.Offset > int64(len(s)) {
		return nil
	}

	s = s[req.Offset:]
	size := req.Size
	if size > len(s) {
		size = len(s)
	}
	resp.Data = make([]byte, size)
	copy(resp.Data, s)
	return nil
}

var junkFilePrefixes = []string{
	"._",
	".DS_Store",
	".Spotlight-",
	".git",
	".hidden",
	".metadata_never_index",
	".nomedia",
	".envrc",
}

// isJunkFile reports whether the given file path is considered useless. MacOSX
// Finder is looking for a few hidden files per a file stat request. So this is
// used to speed things a bit.
func isJunkFile(abspath string) bool {
	_, filename := filepath.Split(abspath)
	for _, v := range junkFilePrefixes {
		if strings.HasPrefix(filename, v) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/kbrgl/fu/matchers"
	"github.com/kbrgl/fu/shallowradix"
	"github.com/mattn/go-isatty"
	"github.com/stretchr/powerwalk"
)

const (
	// Version is the program version
	Version = "1.1.0"
)

func main() {
	kingpin.CommandLine.HelpFlag.Short('h')
	fz := kingpin.Flag("fuzzy", "Use fuzzy search").Short('f').Bool()
	re := kingpin.Flag("regexp", "Use regexp-based search").Short('r').Bool()
	sfx := kingpin.Flag("suffix", "Use suffix-based search").Short('s').Bool()
	pfx := kingpin.Flag("prefix", "Use prefix-based search").Short('p').Bool()
	dir := kingpin.Flag("dir", "Show only directories").Short('d').Bool()
	perm := kingpin.Flag("perm", "Filter by Unix permissions").Short('m').Int()
	pll := kingpin.Flag("parallel",
		"Walk directories in parallel, may result in substantial speedups "+
			"for directories with many files").
		Short('c').
		Bool()

	query := kingpin.Arg("query", "Search query").Required().String()
	paths := kingpin.Arg("paths", "Paths to search").Default(".").ExistingDirs()
	kingpin.Version(Version)
	kingpin.Parse()

	walk := filepath.Walk
	if *pll {
		runtime.GOMAXPROCS(runtime.NumCPU())
		walk = powerwalk.Walk
	}

	var (
		nm  matchers.FileMatcher
		err error
	)
	switch {
	case *re:
		nm, err = matchers.NewREMatcher(*query)
		if err != nil {
			fail(err)
		}
	case *sfx:
		nm = matchers.NewSuffixMatcher(*query)
	case *pfx:
		nm = matchers.NewPrefixMatcher(*query)
	case *fz:
		nm = matchers.NewFuzzyMatcher(*query)
	default:
		nm = matchers.NewExactMatcher(*query)
	}

	ms := make([]matchers.FileMatcher, 0, 3)
	ms = append(ms, nm)

	if *perm != 0 {
		ms = append(ms, matchers.NewPermMatcher(os.FileMode(*perm)))
	}
	if *dir {
		ms = append(ms, matchers.NewDirMatcher())
	}

	s := shallowradix.New()

	for _, path := range *paths {
		abs, err := filepath.Abs(path)
		if err != nil {
			fail(err)
		}
		s.Insert(fmt.Sprintf("%s%c", abs, os.PathSeparator))
	}

	// number of files found
	var found, traversed int
	start := time.Now()
	for _, path := range s.Prefixes() {
		walk(path, func(path string, fi os.FileInfo, err error) error {
			traversed++

			if err != nil {
				fmt.Fprintf(os.Stderr, "%v\n", err)
				return nil
			}

			matches := true
			for _, matcher := range ms {
				if !matcher.Match(fi) {
					matches = false
				}
			}
			if /* the file */ matches {
				fmt.Println(path)
				found++
			}
			return nil
		})
	}

	if isatty.IsTerminal(os.Stdout.Fd()) {
		fmt.Printf("\nTraversed %d files in %s, found %d matches.\n",
			traversed,
			time.Since(start),
			found)
	}
}

func fail(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}
<commit_msg>Add -v short flag for --version<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/kbrgl/fu/matchers"
	"github.com/kbrgl/fu/shallowradix"
	"github.com/mattn/go-isatty"
	"github.com/stretchr/powerwalk"
)

const (
	// Version is the program version
	Version = "1.1.1"
)

func main() {
	kingpin.CommandLine.HelpFlag.Short('h')
	kingpin.CommandLine.VersionFlag.Short('v')
	fz := kingpin.Flag("fuzzy", "Use fuzzy search").Short('f').Bool()
	re := kingpin.Flag("regexp", "Use regexp-based search").Short('r').Bool()
	sfx := kingpin.Flag("suffix", "Use suffix-based search").Short('s').Bool()
	pfx := kingpin.Flag("prefix", "Use prefix-based search").Short('p').Bool()
	dir := kingpin.Flag("dir", "Show only directories").Short('d').Bool()
	perm := kingpin.Flag("perm", "Filter by Unix permissions").Short('m').Int()
	pll := kingpin.Flag("parallel",
		"Walk directories in parallel, may result in substantial speedups "+
			"for directories with many files").
		Short('c').
		Bool()

	query := kingpin.Arg("query", "Search query").Required().String()
	paths := kingpin.Arg("paths", "Paths to search").Default(".").ExistingDirs()
	kingpin.Version(Version)
	kingpin.Parse()

	walk := filepath.Walk
	if *pll {
		runtime.GOMAXPROCS(runtime.NumCPU())
		walk = powerwalk.Walk
	}

	var (
		nm  matchers.FileMatcher
		err error
	)
	switch {
	case *re:
		nm, err = matchers.NewREMatcher(*query)
		if err != nil {
			fail(err)
		}
	case *sfx:
		nm = matchers.NewSuffixMatcher(*query)
	case *pfx:
		nm = matchers.NewPrefixMatcher(*query)
	case *fz:
		nm = matchers.NewFuzzyMatcher(*query)
	default:
		nm = matchers.NewExactMatcher(*query)
	}

	ms := make([]matchers.FileMatcher, 0, 3)
	ms = append(ms, nm)

	if *perm != 0 {
		ms = append(ms, matchers.NewPermMatcher(os.FileMode(*perm)))
	}
	if *dir {
		ms = append(ms, matchers.NewDirMatcher())
	}

	s := shallowradix.New()

	for _, path := range *paths {
		abs, err := filepath.Abs(path)
		if err != nil {
			fail(err)
		}
		s.Insert(fmt.Sprintf("%s%c", abs, os.PathSeparator))
	}

	// number of files found
	var found, traversed int
	start := time.Now()
	for _, path := range s.Prefixes() {
		walk(path, func(path string, fi os.FileInfo, err error) error {
			traversed++

			if err != nil {
				fmt.Fprintf(os.Stderr, "%v\n", err)
				return nil
			}

			matches := true
			for _, matcher := range ms {
				if !matcher.Match(fi) {
					matches = false
				}
			}
			if /* the file */ matches {
				fmt.Println(path)
				found++
			}
			return nil
		})
	}

	if isatty.IsTerminal(os.Stdout.Fd()) {
		fmt.Printf("\nTraversed %d files in %s, found %d matches.\n",
			traversed,
			time.Since(start),
			found)
	}
}

func fail(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>// Copyright 2014 Thomas Keschl. All rights reserved.  Use of this
// source code is governed by a MIT-style license that can be found in
// the LICENSE file.

package main

import(
	"io/ioutil"
	"crypto/md5"
	"crypto/sha1"
	"fmt"
	"flag"
	"hash"
	"os"
)

func main() {
	inputFilePtr := flag.String("f", "", "path to the file to generate the hash for")
	inputHashPtr := flag.String("c", "", "the hash string to compare against")
	inputTypePtr := flag.String("t", "md5", "the hashing function to use (valid options are md5, sha1)")
	flag.Parse()

	// ensure a file has been entered.
	if len(*inputFilePtr) == 0 {
		fmt.Println("Need to enter a file to hash.")
		os.Exit(1)
	}

	filepath := *inputFilePtr

	f,err := ioutil.ReadFile(filepath)
	if err != nil {
		fmt.Println("%s", err)
		os.Exit(2)
	}

	hashStr := *inputTypePtr

	hash := generateSum(hashStr)
	if hash == nil {
		fmt.Println("No such hashing function: ", hashStr)
		os.Exit(3)
	}
	hash.Write(f)

	sum := hash.Sum(nil)
	sumStr := fmt.Sprintf("%x", sum)

	if len(*inputHashPtr) != 0 {
		compareStr := *inputHashPtr
		if compareStr == sumStr {
			fmt.Println("They match!")
		} else {
			fmt.Println("They don't match!")
		}
		os.Exit(0)
	}

	fmt.Println(sumStr)
	os.Exit(0)
}

func generateSum(hashStr string) hash.Hash {
	switch {
		case hashStr == "md5":
			return md5.New()
		case hashStr == "sha1":
			return sha1.New()
	}
	return nil
}<commit_msg>Load file from stream, use log.Fatal() on error.<commit_after>// Copyright 2014 Thomas Keschl. All rights reserved.  Use of this
// source code is governed by a MIT-style license that can be found in
// the LICENSE file.

package main

import(
    "io"
    "crypto/md5"
    "crypto/sha1"
    "fmt"
    "flag"
    "hash"
    "os"
    "log"
)

func main() {
    inputFilePtr := flag.String("f", "", "path to the file to generate the hash for")
    inputHashPtr := flag.String("c", "", "the hash string to compare against")
    inputTypePtr := flag.String("t", "md5", "the hashing function to use (valid options are md5, sha1)")
    flag.Parse()

    // ensure a file has been entered.
    if len(*inputFilePtr) == 0 {
        log.Fatal("Need to enter a file to hash.")
    }

    filepath := *inputFilePtr

    f,err := os.Open(filepath)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    hashStr := *inputTypePtr

    hasher := generateSum(hashStr)
    if hasher == nil {
        log.Fatal("No such hashing function: ", hashStr)
    }

    _, err = io.Copy(hasher, f)

    if err != nil {
        log.Fatal(err)
    }

    sum := hasher.Sum(nil)
    sumStr := fmt.Sprintf("%x", sum)

    if len(*inputHashPtr) != 0 {
        compareStr := *inputHashPtr
        if compareStr == sumStr {
            fmt.Println("They match!")
        } else {
            fmt.Println("They don't match!")
        }
        os.Exit(0)
    }

    fmt.Println(sumStr)
    os.Exit(0)
}

func generateSum(hashStr string) hash.Hash {
    switch {
        case hashStr == "md5":
            return md5.New()
        case hashStr == "sha1":
            return sha1.New()
    }
    return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Jari Takkala. 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 (
	"crypto/sha1"
	"fmt"
	sysio "io"
	"path/filepath"
	"launchpad.net/tomb"
	"log"
	"os"
)

type IO struct {
	metaInfo MetaInfo
	files []*os.File
	t tomb.Tomb
}

// Reads in files and verifies them, returns a map of pieces we already have
func (io *IO) Verify() {
	pieceLength := io.metaInfo.Info.PieceLength

	// Create a buffer of size PieceLength
	buf := make([]byte, pieceLength)
	if len(io.metaInfo.Info.Files) > 0 {
		// Multiple File Mode
		var offset int64
		var piece, n, m int
		var err error
		h := sha1.New()
		// Iterate over each file
		for i, file := range io.metaInfo.Info.Files {
			for offset = 0; offset < int64(file.Length); {
				// Reset out hash
				h.Reset()
				// Read from file at offset, up to buf size or
				// less if last read was incomplete due to EOF
				n, err = io.files[i].ReadAt(buf[m:], offset)
				if err != nil {
					if err == sysio.EOF {
						fmt.Printf("%s %d\n", file.Path, n)
						// Reached EOF
						m += n
						break
					}
					log.Fatal(err)
				}
				// We have a full buf, generate a hash and compare with
				// corresponding pieces part of the torrent file
				h.Write(buf)
				fmt.Printf("sha1: %x %x\n", h.Sum(nil), io.metaInfo.Info.Pieces[piece:piece + 20])
				// Increment offset by number of bytes read
				offset += int64(n)
				m = 0
				// Increment piece by the length of a SHA-1 hash (20 bytes)
				piece += 20
			}
			if (n == pieceLength) {
				h.Write(buf)
				fmt.Printf("sha1: %x %x\n", h.Sum(nil), io.metaInfo.Info.Pieces[piece:piece + 20])
			}
		}
	} else {
		// Single File Mode
	}

}

func (io *IO) Stop() error {
	io.t.Kill(nil)
	return io.t.Wait()
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func (io *IO) Init() {
	if len(io.metaInfo.Info.Files) > 0 {
		// Multiple File Mode
		directory := io.metaInfo.Info.Name
		// Create the directory if it doesn't exist
		if _, err := os.Stat(directory); os.IsNotExist(err) {
			err = os.Mkdir(directory, os.ModeDir | os.ModePerm)
			checkError(err)
		}
		err := os.Chdir(directory)
		checkError(err)
		for _, file := range io.metaInfo.Info.Files {
			// Create any sub-directories if required
			if len(file.Path) > 1 {
				directory = filepath.Join(file.Path[1:]...)
				if _, err := os.Stat(directory); os.IsNotExist(err) {
					err = os.MkdirAll(directory, os.ModeDir | os.ModePerm)
					checkError(err)
				}
			}
			// Create the file if it doesn't exist
			path := filepath.Join(file.Path...)
			var fh *os.File
			if _, err := os.Stat(path); os.IsNotExist(err) {
				// Create the file and return a handle
				fh, err = os.Create(path)
				checkError(err)
			} else {
				// Open the file and return a handle
				fh, err = os.Open(path)
				checkError(err)
			}
			io.files = append(io.files, fh)
		}
	} else {
		// Single File Mode
	}
}

func (io *IO) Run() {
	log.Println("IO : Run : Started")
	defer io.t.Done()
	defer log.Println("IO : Run : Completed")

	io.Init()
	io.Verify()

	for {
		select {
		case <- io.t.Dying():
			return
		}
	}
}
<commit_msg>Added stop log<commit_after>// Copyright 2013 Jari Takkala. 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 (
	"crypto/sha1"
	"fmt"
	sysio "io"
	"path/filepath"
	"launchpad.net/tomb"
	"log"
	"os"
)

type IO struct {
	metaInfo MetaInfo
	files []*os.File
	t tomb.Tomb
}

// Reads in files and verifies them, returns a map of pieces we already have
func (io *IO) Verify() {
	pieceLength := io.metaInfo.Info.PieceLength

	// Create a buffer of size PieceLength
	buf := make([]byte, pieceLength)
	if len(io.metaInfo.Info.Files) > 0 {
		// Multiple File Mode
		var offset int64
		var piece, n, m int
		var err error
		h := sha1.New()
		// Iterate over each file
		for i, file := range io.metaInfo.Info.Files {
			for offset = 0; offset < int64(file.Length); {
				// Reset out hash
				h.Reset()
				// Read from file at offset, up to buf size or
				// less if last read was incomplete due to EOF
				n, err = io.files[i].ReadAt(buf[m:], offset)
				if err != nil {
					if err == sysio.EOF {
						fmt.Printf("%s %d\n", file.Path, n)
						// Reached EOF
						m += n
						break
					}
					log.Fatal(err)
				}
				// We have a full buf, generate a hash and compare with
				// corresponding pieces part of the torrent file
				h.Write(buf)
				fmt.Printf("sha1: %x %x\n", h.Sum(nil), io.metaInfo.Info.Pieces[piece:piece + 20])
				// Increment offset by number of bytes read
				offset += int64(n)
				m = 0
				// Increment piece by the length of a SHA-1 hash (20 bytes)
				piece += 20
			}
			if (n == pieceLength) {
				h.Write(buf)
				fmt.Printf("sha1: %x %x\n", h.Sum(nil), io.metaInfo.Info.Pieces[piece:piece + 20])
			}
		}
	} else {
		// Single File Mode
	}

}

func (io *IO) Stop() error {
	log.Println("IO : Stop : Stopping")
	io.t.Kill(nil)
	return io.t.Wait()
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func (io *IO) Init() {
	if len(io.metaInfo.Info.Files) > 0 {
		// Multiple File Mode
		directory := io.metaInfo.Info.Name
		// Create the directory if it doesn't exist
		if _, err := os.Stat(directory); os.IsNotExist(err) {
			err = os.Mkdir(directory, os.ModeDir | os.ModePerm)
			checkError(err)
		}
		err := os.Chdir(directory)
		checkError(err)
		for _, file := range io.metaInfo.Info.Files {
			// Create any sub-directories if required
			if len(file.Path) > 1 {
				directory = filepath.Join(file.Path[1:]...)
				if _, err := os.Stat(directory); os.IsNotExist(err) {
					err = os.MkdirAll(directory, os.ModeDir | os.ModePerm)
					checkError(err)
				}
			}
			// Create the file if it doesn't exist
			path := filepath.Join(file.Path...)
			var fh *os.File
			if _, err := os.Stat(path); os.IsNotExist(err) {
				// Create the file and return a handle
				fh, err = os.Create(path)
				checkError(err)
			} else {
				// Open the file and return a handle
				fh, err = os.Open(path)
				checkError(err)
			}
			io.files = append(io.files, fh)
		}
	} else {
		// Single File Mode
	}
}

func (io *IO) Run() {
	log.Println("IO : Run : Started")
	defer io.t.Done()
	defer log.Println("IO : Run : Completed")

	io.Init()
	io.Verify()

	for {
		select {
		case <- io.t.Dying():
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"
	"os/exec"
)

func wrapStdin(proc *exec.Cmd, stdin io.Reader, done chan bool) int {
	logger.Println("Wrapping stdin")

	pipe, err := proc.StdinPipe()
	fatal_if(err)

	go inLoop(pipe, stdin, done)
	return 1
}

func wrapStdout(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) int {
	logger.Printf("Wrapping stdout with %v\n", opt)

	pipe, err := proc.StdoutPipe()
	fatal_if(err)

	go outLoop(pipe, outstream, opt, done)
	return 1
}

func wrapStderr(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) int {
	logger.Printf("Wrapping stderr with %v\n", opt)

	pipe, err := proc.StderrPipe()
	fatal_if(err)

	go outLoop(pipe, outstream, opt, done)
	return 1
}

///

func inLoop(pipe io.WriteCloser, stdin io.Reader, done chan bool) {
	buf := make([]byte, 2)
	logger.Println("Entering stdin loop")
	for {
		bytes_read, read_err := io.ReadFull(stdin, buf)
		if read_err == io.EOF && bytes_read == 0 {
			break
		}
		fatal_if(read_err)

		length := read16_be(buf)
		logger.Printf("in: packet length = %v\n", length)
		if length == 0 {
			// this is how Porcelain signals EOF from Elixir
			break
		}

		bytes_written, write_err := io.CopyN(pipe, stdin, int64(length))
		logger.Printf("in: copied %v bytes\n", bytes_written)
		fatal_if(write_err)
	}
	pipe.Close()
	done <- true
}

///

// Maximum buffer size for protocol 0.0 is 2 + 2^16-1 - 1
//
//   * 2 is the packet length
//   * 2^16-1 is the maximum amount of data that can be encoded in a
//     2-byte-length packet
//   * 1 byte is used for framing, so it has to be included in the total length
//
var outBuf [1<<16]byte

func outLoop(pipe io.ReadCloser, outstream io.Writer, char byte, done chan bool) {
	buf := outBuf
	buf[2] = char
	logger.Printf("Entering out loop with %v\n", char)
	for {
		bytes_read, read_err := pipe.Read(buf[3:])
		logger.Printf("out: read bytes: %v\n", bytes_read)
		if bytes_read > 0 {
			write16_be(buf[:2], bytes_read+1)
			bytes_written, write_err := outstream.Write(buf[:2+bytes_read+1])
			logger.Printf("out: written bytes: %v\n", bytes_written)
			fatal_if(write_err)
		}
		if read_err == io.EOF || bytes_read == 0 {
			// From io.Reader docs:
			//
			//   Implementations of Read are discouraged from returning a zero
			//   byte count with a nil error, and callers should treat that
			//   situation as a no-op.
			//
			// In this case it appears that 0 bytes may sometimes be returned
			// indefinitely. Therefore we close the pipe.
			if read_err == io.EOF {
				logger.Println("Encountered EOF on input")
			} else {
				logger.Println("Read 0 bytes with no error")
			}
			break
		}
		if read_err != nil {
			switch read_err.(type) {
			case *os.PathError:
				// known error
				break
			default:
				fatal(read_err)
			}
		}
	}
	pipe.Close()
	done <- true
}

// Unpack the 2-byte integer stored in big endian order
func read16_be(data []byte) int16 {
	return int16(data[0]) << 8 | int16(data[1])
}

// Pack a 2-byte integer in big endian order
func write16_be(data []byte, num int) {
	data[0] = byte(num >> 8)
	data[1] = byte(num)
}
<commit_msg>Minor code cleanup<commit_after>package main

import (
	"io"
	"os"
	"os/exec"
)

func wrapStdin(proc *exec.Cmd, stdin io.Reader, done chan bool) {
	logger.Println("Wrapping stdin")

	pipe, err := proc.StdinPipe()
	fatal_if(err)

	go inLoop(pipe, stdin, done)
}

func wrapStdout(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) {
	logger.Printf("Wrapping stdout with %v\n", opt)

	pipe, err := proc.StdoutPipe()
	fatal_if(err)

	go outLoop(pipe, outstream, opt, done)
}

func wrapStderr(proc *exec.Cmd, outstream io.Writer, opt byte, done chan bool) {
	logger.Printf("Wrapping stderr with %v\n", opt)

	pipe, err := proc.StderrPipe()
	fatal_if(err)

	go outLoop(pipe, outstream, opt, done)
}

///

func inLoop(pipe io.WriteCloser, stdin io.Reader, done chan bool) {
	buf := make([]byte, 2)
	logger.Println("Entering stdin loop")
	for {
		bytes_read, read_err := io.ReadFull(stdin, buf)
		if read_err == io.EOF && bytes_read == 0 {
			break
		}
		fatal_if(read_err)

		length := read16_be(buf)
		logger.Printf("in: packet length = %v\n", length)
		if length == 0 {
			// this is how Porcelain signals EOF from Elixir
			break
		}

		bytes_written, write_err := io.CopyN(pipe, stdin, int64(length))
		logger.Printf("in: copied %v bytes\n", bytes_written)
		fatal_if(write_err)
	}
	pipe.Close()
	done <- true
}

///

// Maximum buffer size for protocol 0.0 is 2 + 2^16-1 - 1
//
//   * 2 is the packet length
//   * 2^16-1 is the maximum amount of data that can be encoded in a
//     2-byte-length packet
//   * 1 byte is used for framing, so it has to be included in the total length
//
var outBuf [1<<16]byte

func outLoop(pipe io.ReadCloser, outstream io.Writer, char byte, done chan bool) {
	buf := outBuf
	buf[2] = char
	logger.Printf("Entering out loop with %v\n", char)
	for {
		bytes_read, read_err := pipe.Read(buf[3:])
		logger.Printf("out: read bytes: %v\n", bytes_read)
		if bytes_read > 0 {
			write16_be(buf[:2], bytes_read+1)
			bytes_written, write_err := outstream.Write(buf[:2+bytes_read+1])
			logger.Printf("out: written bytes: %v\n", bytes_written)
			fatal_if(write_err)
		}
		if read_err == io.EOF || bytes_read == 0 {
			// From io.Reader docs:
			//
			//   Implementations of Read are discouraged from returning a zero
			//   byte count with a nil error, and callers should treat that
			//   situation as a no-op.
			//
			// In this case it appears that 0 bytes may sometimes be returned
			// indefinitely. Therefore we close the pipe.
			if read_err == io.EOF {
				logger.Println("Encountered EOF on input")
			} else {
				logger.Println("Read 0 bytes with no error")
			}
			break
		}
		if read_err != nil {
			switch read_err.(type) {
			case *os.PathError:
				// known error
				break
			default:
				fatal(read_err)
			}
		}
	}
	pipe.Close()
	done <- true
}

// Unpack the 2-byte integer stored in big endian order
func read16_be(data []byte) int16 {
	return int16(data[0]) << 8 | int16(data[1])
}

// Pack a 2-byte integer in big endian order
func write16_be(data []byte, num int) {
	data[0] = byte(num >> 8)
	data[1] = byte(num)
}
<|endoftext|>


<commit_before>package is

import (
	"fmt"
	"reflect"
	"sync"
)

// I represents the is interface.
type I interface {
	// OK asserts that the specified objects are all OK.
	OK(o ...interface{})
	// Equal asserts that the two values are
	// considered equal. Non strict.
	Equal(a, b interface{})
	// NotEqual asserts that the two values are not
	// considered equal. Non strict.
	NotEqual(a, b interface{})
	// NoErr asserts that the value is not an
	// error.
	NoErr(err ...error)
	// Nil asserts that the specified objects are
	// all nil.
	Nil(obj ...interface{})
	// Panic asserts that the specified function
	// panics.
	Panic(fn func())
	// PanicWith asserts that the specified function
	// panics with the specific message.
	PanicWith(m string, fn func())
	// Fail indicates that the test has failed with the
	// specified formatted arguments.
	Fail(args ...interface{})
	// Failf indicates that the test has failed with the
	// formatted arguments.
	Failf(format string, args ...interface{})
}

// T represents the an interface for reporting
// failures.
// testing.T satisfied this interface.
type T interface {
	FailNow()
}

// i represents an implementation of interface I.
type i struct {
	t       T
	fails   []string
	l       sync.Mutex
	relaxed bool
}

func (i *i) Log(args ...interface{}) {
	i.l.Lock()
	fail := fmt.Sprint(args...)
	i.fails = append(i.fails, fail)
	fmt.Print(decorate(fail))
	i.l.Unlock()
	if !i.relaxed {
		i.t.FailNow()
	}
}
func (i *i) Logf(format string, args ...interface{}) {
	i.l.Lock()
	fail := fmt.Sprintf(format, args...)
	i.fails = append(i.fails, fail)
	fmt.Print(decorate(fail))
	i.l.Unlock()
	if !i.relaxed {
		i.t.FailNow()
	}
}

// OK asserts that the specified objects are all OK.
func (i *i) OK(o ...interface{}) {
	for _, obj := range o {

		if isNil(obj) {
			i.Log("unexpected nil")
		}

		switch co := obj.(type) {
		case func():
			// shouldn't panic
			var r interface{}
			func() {
				defer func() {
					r = recover()
				}()
				co()
			}()
			if r != nil {
				i.Logf("unexpected panic: %v", r)
			}
			return
		case string:
			if len(co) == 0 {
				i.Log("unexpected \"\"")
			}
			return
		case bool:
			// false
			if co == false {
				i.Log("unexpected false")
				return
			}
		}
		if isNil(o) {
			if _, ok := obj.(error); ok {
				// nil errors are ok
				return
			}
			i.Log("unexpected nil")
			return
		}

		if obj == 0 {
			i.Log("unexpected zero")
		}
	}
}

func (i *i) NoErr(errs ...error) {
	for n, err := range errs {
		if !isNil(err) {
			p := "unexpected error"
			if len(errs) > 1 {
				p += fmt.Sprintf(" (%d)", n)
			}
			p += ": " + err.Error()
			i.Logf(p)
		}
	}
}

func (i *i) Nil(o ...interface{}) {
	for n, obj := range o {
		if !isNil(obj) {
			p := "expected nil"
			if len(o) > 1 {
				p += fmt.Sprintf(" (%d)", n)
			}
			p += ": " + fmt.Sprintf("%#v", obj)
			i.Logf(p)
		}
	}
}

// Equal asserts that the two values are
// considered equal. Non strict.
func (i *i) Equal(a, b interface{}) {
	if !areEqual(a, b) {
		i.Logf("%v != %v", a, b)
	}
}

// NotEqual asserts that the two values are not
// considered equal. Non strict.
func (i *i) NotEqual(a, b interface{}) {
	if areEqual(a, b) {
		i.Logf("%v == %v", a, b)
	}
}

func (i *i) Fail(args ...interface{}) {
	i.Log(args...)
}

func (i *i) Failf(format string, args ...interface{}) {
	i.Logf(format, args...)
}

// Panic asserts that the specified function
// panics.
func (i *i) Panic(fn func()) {
	var r interface{}
	func() {
		defer func() {
			r = recover()
		}()
		fn()
	}()
	if r == nil {
		i.Log("expected panic")
	}
}

// PanicWith asserts that the specified function
// panics with the specific message.
func (i *i) PanicWith(m string, fn func()) {
	var r interface{}
	func() {
		defer func() {
			r = recover()
		}()
		fn()
	}()
	if r != m {
		i.Logf("expected panic: \"%s\"", m)
	}
}

// New creates a new I capable of making
// assertions.
func New(t T) I {
	return &i{t: t}
}

// Relaxed creates a new I capable of making
// assertions, but will not fail immediately
// allowing all assertions to run.
func Relaxed(t T) I {
	return &i{t: t, relaxed: true}
}

// isNil gets whether the object is nil or not.
func isNil(object interface{}) bool {
	if object == nil {
		return true
	}
	value := reflect.ValueOf(object)
	kind := value.Kind()
	if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
		return true
	}
	return false
}

// areEqual gets whether a equals b or not.
func areEqual(a, b interface{}) bool {
	if isNil(a) || isNil(b) {
		if isNil(a) && !isNil(b) {
			return false
		}
		if !isNil(a) && isNil(b) {
			return false
		}
		return a == b
	}
	if reflect.DeepEqual(a, b) {
		return true
	}
	aValue := reflect.ValueOf(a)
	bValue := reflect.ValueOf(b)
	if aValue == bValue {
		return true
	}
	// Attempt comparison after type conversion
	if bValue.Type().ConvertibleTo(aValue.Type()) && aValue == bValue.Convert(aValue.Type()) {
		return true
	}

	return false
}
<commit_msg>Put back last ditch<commit_after>package is

import (
	"fmt"
	"reflect"
	"sync"
)

// I represents the is interface.
type I interface {
	// OK asserts that the specified objects are all OK.
	OK(o ...interface{})
	// Equal asserts that the two values are
	// considered equal. Non strict.
	Equal(a, b interface{})
	// NotEqual asserts that the two values are not
	// considered equal. Non strict.
	NotEqual(a, b interface{})
	// NoErr asserts that the value is not an
	// error.
	NoErr(err ...error)
	// Nil asserts that the specified objects are
	// all nil.
	Nil(obj ...interface{})
	// Panic asserts that the specified function
	// panics.
	Panic(fn func())
	// PanicWith asserts that the specified function
	// panics with the specific message.
	PanicWith(m string, fn func())
	// Fail indicates that the test has failed with the
	// specified formatted arguments.
	Fail(args ...interface{})
	// Failf indicates that the test has failed with the
	// formatted arguments.
	Failf(format string, args ...interface{})
}

// T represents the an interface for reporting
// failures.
// testing.T satisfied this interface.
type T interface {
	FailNow()
}

// i represents an implementation of interface I.
type i struct {
	t       T
	fails   []string
	l       sync.Mutex
	relaxed bool
}

func (i *i) Log(args ...interface{}) {
	i.l.Lock()
	fail := fmt.Sprint(args...)
	i.fails = append(i.fails, fail)
	fmt.Print(decorate(fail))
	i.l.Unlock()
	if !i.relaxed {
		i.t.FailNow()
	}
}
func (i *i) Logf(format string, args ...interface{}) {
	i.l.Lock()
	fail := fmt.Sprintf(format, args...)
	i.fails = append(i.fails, fail)
	fmt.Print(decorate(fail))
	i.l.Unlock()
	if !i.relaxed {
		i.t.FailNow()
	}
}

// OK asserts that the specified objects are all OK.
func (i *i) OK(o ...interface{}) {
	for _, obj := range o {

		if isNil(obj) {
			i.Log("unexpected nil")
		}

		switch co := obj.(type) {
		case func():
			// shouldn't panic
			var r interface{}
			func() {
				defer func() {
					r = recover()
				}()
				co()
			}()
			if r != nil {
				i.Logf("unexpected panic: %v", r)
			}
			return
		case string:
			if len(co) == 0 {
				i.Log("unexpected \"\"")
			}
			return
		case bool:
			// false
			if co == false {
				i.Log("unexpected false")
				return
			}
		}
		if isNil(o) {
			if _, ok := obj.(error); ok {
				// nil errors are ok
				return
			}
			i.Log("unexpected nil")
			return
		}

		if obj == 0 {
			i.Log("unexpected zero")
		}
	}
}

func (i *i) NoErr(errs ...error) {
	for n, err := range errs {
		if !isNil(err) {
			p := "unexpected error"
			if len(errs) > 1 {
				p += fmt.Sprintf(" (%d)", n)
			}
			p += ": " + err.Error()
			i.Logf(p)
		}
	}
}

func (i *i) Nil(o ...interface{}) {
	for n, obj := range o {
		if !isNil(obj) {
			p := "expected nil"
			if len(o) > 1 {
				p += fmt.Sprintf(" (%d)", n)
			}
			p += ": " + fmt.Sprintf("%#v", obj)
			i.Logf(p)
		}
	}
}

// Equal asserts that the two values are
// considered equal. Non strict.
func (i *i) Equal(a, b interface{}) {
	if !areEqual(a, b) {
		i.Logf("%v != %v", a, b)
	}
}

// NotEqual asserts that the two values are not
// considered equal. Non strict.
func (i *i) NotEqual(a, b interface{}) {
	if areEqual(a, b) {
		i.Logf("%v == %v", a, b)
	}
}

func (i *i) Fail(args ...interface{}) {
	i.Log(args...)
}

func (i *i) Failf(format string, args ...interface{}) {
	i.Logf(format, args...)
}

// Panic asserts that the specified function
// panics.
func (i *i) Panic(fn func()) {
	var r interface{}
	func() {
		defer func() {
			r = recover()
		}()
		fn()
	}()
	if r == nil {
		i.Log("expected panic")
	}
}

// PanicWith asserts that the specified function
// panics with the specific message.
func (i *i) PanicWith(m string, fn func()) {
	var r interface{}
	func() {
		defer func() {
			r = recover()
		}()
		fn()
	}()
	if r != m {
		i.Logf("expected panic: \"%s\"", m)
	}
}

// New creates a new I capable of making
// assertions.
func New(t T) I {
	return &i{t: t}
}

// Relaxed creates a new I capable of making
// assertions, but will not fail immediately
// allowing all assertions to run.
func Relaxed(t T) I {
	return &i{t: t, relaxed: true}
}

// isNil gets whether the object is nil or not.
func isNil(object interface{}) bool {
	if object == nil {
		return true
	}
	value := reflect.ValueOf(object)
	kind := value.Kind()
	if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
		return true
	}
	return false
}

// areEqual gets whether a equals b or not.
func areEqual(a, b interface{}) bool {
	if isNil(a) || isNil(b) {
		if isNil(a) && !isNil(b) {
			return false
		}
		if !isNil(a) && isNil(b) {
			return false
		}
		return a == b
	}
	if reflect.DeepEqual(a, b) {
		return true
	}
	aValue := reflect.ValueOf(a)
	bValue := reflect.ValueOf(b)
	if aValue == bValue {
		return true
	}
	// Attempt comparison after type conversion
	if bValue.Type().ConvertibleTo(aValue.Type()) && aValue == bValue.Convert(aValue.Type()) {
		return true
	}
	// Last ditch effort
	if fmt.Sprintf("%#v", a) == fmt.Sprintf("%#v", b) {
		return true
	}

	return false
}
<|endoftext|>


<commit_before>package le

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"time"
)

var (
	Address  = "data.logentries.com:10000"
	newlines = []byte{'\n'}
)

// Writer implements the io.Writer interface for sending token-based logs to
// Logentries.
type Writer struct {
	Token string
	conn  net.Conn
}

// NewWriter returns a new Writer with a given Logentries token. If the token
// is invalid, Logentries will ignore all submitted logs.
func NewWriter(token string) *Writer {
	return &Writer{Token: token}
}

// Write sends lines of text to Logentries with our associated token. If the
// write fails at any point, we return 0 regardless of how many bytes were
// actually written.
func (w *Writer) Write(p []byte) (n int, err error) {
	if w.conn == nil {
		err = w.connect()
		if err != nil {
			return 0, err
		}
	}

	scanner := bufio.NewScanner(bytes.NewReader(p))
	for scanner.Scan() {
		_, err = fmt.Fprintf(w.conn, "%s %s\n", w.Token, scanner.Bytes())
		if err != nil {
			w.Close()
			return 0, err
		}
	}

	return len(p), scanner.Err()
}

func (w *Writer) Close() (err error) {
	if w.conn != nil {
		err = w.conn.Close()
		w.conn = nil
	}
	return err
}

func (w *Writer) connect() (err error) {
	w.conn, err = net.DialTimeout("tcp", Address, time.Second)
	return err
}
<commit_msg>Fix comment.<commit_after>package le

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"time"
)

var (
	Address  = "data.logentries.com:10000"
	newlines = []byte{'\n'}
)

// Writer implements the io.WriteCloser interface for sending token-based logs
// to Logentries.
type Writer struct {
	Token string
	conn  net.Conn
}

// NewWriter returns a new Writer with a given Logentries token. If the token
// is invalid, Logentries will ignore all submitted logs.
func NewWriter(token string) *Writer {
	return &Writer{Token: token}
}

// Write sends lines of text to Logentries with our associated token. If the
// write fails at any point, we return 0 regardless of how many bytes were
// actually written.
func (w *Writer) Write(p []byte) (n int, err error) {
	if w.conn == nil {
		err = w.connect()
		if err != nil {
			return 0, err
		}
	}

	scanner := bufio.NewScanner(bytes.NewReader(p))
	for scanner.Scan() {
		_, err = fmt.Fprintf(w.conn, "%s %s\n", w.Token, scanner.Bytes())
		if err != nil {
			w.Close()
			return 0, err
		}
	}

	return len(p), scanner.Err()
}

func (w *Writer) Close() (err error) {
	if w.conn != nil {
		err = w.conn.Close()
		w.conn = nil
	}
	return err
}

func (w *Writer) connect() (err error) {
	w.conn, err = net.DialTimeout("tcp", Address, time.Second)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2017 Istio 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.

package bindings

import (
	"fmt"
	"net/http"
	"os"
	"sort"
	"strings"
	"text/template"

	"github.com/apigee/istio-mixer-adapter/adapter/product"
	"github.com/apigee/istio-mixer-adapter/apigee-istio/shared"
	"github.com/spf13/cobra"
)

const (
	servicesAttr         = "istio-services"
	productsURLFormat    = "%s/products"                                         // customerProxyURL
	productAttrURLFormat = "%s/v1/o/theganyo1-eval/apiproducts/%s/attributes/%s" // ManagementBase, prod, attr
)

type bindings struct {
	*shared.RootArgs
	products []product.APIProduct
}

// Cmd returns base command
func Cmd(rootArgs *shared.RootArgs, printf, fatalf shared.FormatFn) *cobra.Command {
	cfg := &bindings{RootArgs: rootArgs}

	c := &cobra.Command{
		Use:   "bindings",
		Short: "Manage Apigee Product to Istio Service bindings",
		Long:  "Manage Apigee Product to Istio Service bindings.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			return rootArgs.Resolve(false)
		},
	}

	c.AddCommand(cmdBindingsList(cfg, printf, fatalf))
	c.AddCommand(cmdBindingsAdd(cfg, printf, fatalf))
	c.AddCommand(cmdBindingsRemove(cfg, printf, fatalf))

	return c
}

func cmdBindingsList(b *bindings, printf, fatalf shared.FormatFn) *cobra.Command {
	c := &cobra.Command{
		Use:   "list",
		Short: "List Apigee Product to Istio Service bindings",
		Long:  "List Apigee Product to Istio Service bindings",
		Args:  cobra.NoArgs,

		Run: func(cmd *cobra.Command, _ []string) {
			b.cmdList(printf, fatalf)
		},
	}

	return c
}

func cmdBindingsAdd(b *bindings, printf, fatalf shared.FormatFn) *cobra.Command {
	c := &cobra.Command{
		Use:   "add [service name] [product name]",
		Short: "Add Istio Service binding to Apigee Product",
		Long:  "Add Istio Service binding to Apigee Product",
		Args:  cobra.ExactArgs(2),

		Run: func(cmd *cobra.Command, args []string) {
			serviceName := args[0]
			productName := args[1]
			p, err := b.getProduct(productName)
			if err != nil {
				fatalf("%v", err)
			}
			if p == nil {
				fatalf("invalid product name: %s", args[0])
			}

			b.bindService(p, serviceName, printf, fatalf)
		},
	}

	return c
}

func cmdBindingsRemove(b *bindings, printf, fatalf shared.FormatFn) *cobra.Command {
	c := &cobra.Command{
		Use:   "remove [service name] [product name]",
		Short: "Remove Istio Service binding from Apigee Product",
		Long:  "Remove Istio Service binding from Apigee Product",
		Args:  cobra.ExactArgs(2),

		Run: func(cmd *cobra.Command, args []string) {
			serviceName := args[0]
			productName := args[1]
			p, err := b.getProduct(productName)
			if err != nil {
				fatalf("%v", err)
			}
			if p == nil {
				fatalf("invalid product name: %s", args[0])
			}

			b.unbindService(p, serviceName, printf, fatalf)
		},
	}

	return c
}

func (b *bindings) getProduct(name string) (*product.APIProduct, error) {
	products, err := b.getProducts()
	if err != nil {
		return nil, err
	}
	for _, p := range products {
		if p.Name == name {
			return &p, nil
		}
	}
	return nil, nil
}

func (b *bindings) getProducts() ([]product.APIProduct, error) {
	if b.products != nil {
		return b.products, nil
	}
	productsURL := fmt.Sprintf(productsURLFormat, b.CustomerProxyURL)
	req, err := http.NewRequest(http.MethodGet, productsURL, nil)
	if err != nil {
		return nil, fmt.Errorf("error creating request: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")

	var res product.APIResponse
	resp, err := b.Client.Do(req, &res)
	if err != nil {
		return nil, fmt.Errorf("error retrieving products: %v", err)
	}
	defer resp.Body.Close()

	return res.APIProducts, nil
}

func (b *bindings) cmdList(printf, fatalf shared.FormatFn) error {
	products, err := b.getProducts()
	if err != nil {
		fatalf("%v", err)
	}
	var bound, unbound []product.APIProduct
	for _, p := range products {
		// server returns empty scopes as array with a single empty string, remove for consistency
		if len(p.Scopes) == 1 && p.Scopes[0] == "" {
			p.Scopes = []string{}
		}
		// server may return empty quota field as "null"
		if p.QuotaLimit == "null" {
			p.QuotaLimit = ""
		}
		p.Targets = p.GetBoundServices()
		if p.Targets == nil {
			unbound = append(bound, p)
		} else {
			bound = append(bound, p)
		}
	}

	sort.Sort(byName(bound))
	sort.Sort(byName(unbound))
	data := struct {
		Bound   []product.APIProduct
		Unbound []product.APIProduct
	}{
		Bound:   bound,
		Unbound: unbound,
	}
	tmp := template.New("products")
	tmp.Funcs(template.FuncMap{
		"scopes": func(in []string) string { return strings.Join(in, ",") },
	})
	tmp, err = tmp.Parse(productsTemplate)
	if err != nil {
		fatalf("failed to create template: %v", err)
	}
	err = tmp.Execute(os.Stdout, data)
	if err != nil {
		fatalf("failed to execute template: %v", err)
	}

	return nil
}

func (b *bindings) bindService(p *product.APIProduct, service string, printf, fatalf shared.FormatFn) {
	boundServices := p.GetBoundServices()
	if _, ok := indexOf(boundServices, service); ok {
		fatalf("service %s is already bound to %s", service, p.Name)
	}
	err := b.updateServiceBindings(p, append(boundServices, service))
	if err != nil {
		fatalf("error removing service %s from %s: %v", service, p.Name, err)
	}
	printf("product %s is now bound to: %s", p.Name, service)
}

func (b *bindings) unbindService(p *product.APIProduct, service string, printf, fatalf shared.FormatFn) {
	boundServices := p.GetBoundServices()
	i, ok := indexOf(boundServices, service)
	if !ok {
		fatalf("service %s is not bound to %s", service, p.Name)
	}
	boundServices = append(boundServices[:i], boundServices[i+1:]...)
	err := b.updateServiceBindings(p, boundServices)
	if err != nil {
		fatalf("error removing service %s from %s: %v", service, p.Name, err)
	}
	printf("product %s is no longer bound to: %s", p.Name, service)
}

func (b *bindings) updateServiceBindings(p *product.APIProduct, bindings []string) error {
	newAttr := struct {
		Value string `json:"value"`
	}{
		Value: strings.Join(bindings, ","),
	}
	req, err := b.Client.NewRequest(http.MethodPost, "", newAttr)
	if err != nil {
		return err
	}
	path := fmt.Sprintf(productAttrURLFormat, b.ManagementBase, p.Name, servicesAttr)
	req.URL.Path = path // hack: negate client's incorrect method of determining base URL
	var attr product.Attribute
	_, err = b.Client.Do(req, &attr)
	return err
}

func indexOf(array []string, val string) (index int, exists bool) {
	index = -1
	for i, v := range array {
		if val == v {
			index = i
			exists = true
			break
		}
	}
	return
}

type byName []product.APIProduct

func (a byName) Len() int           { return len(a) }
func (a byName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name }

const productsTemplate = `
{{- define "product"}}
{{.Name}}:
 {{- if .Scopes}}
  Scopes: {{scopes (.Scopes)}}
 {{- end}}
 {{- if .QuotaLimit}}
  Quota: {{.QuotaLimit}} requests every {{.QuotaInterval}} {{.QuotaTimeUnit}}
 {{- end}}
 {{- if .Targets}}
  Service bindings:
  {{- range .Targets}}
    {{.}}
  {{- end}}
  Paths:
  {{- range .Resources}}
    {{.}}
  {{- end}}
 {{- end}}
{{- end}}
API Products
============
{{- if .Bound}}
Bound
-----
 {{- range .Bound}}
 {{- template "product" .}}
 {{- end}}
{{- end}}
{{- if .Unbound}}

Unbound
-------
 {{- range .Unbound}}
 {{- template "product" .}}
 {{- end}}
{{- end}}
`
<commit_msg>Removed hardcoded org name to use org name passed in from command line.<commit_after>// Copyright 2017 Istio 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.

package bindings

import (
	"fmt"
	"net/http"
	"os"
	"sort"
	"strings"
	"text/template"

	"github.com/apigee/istio-mixer-adapter/adapter/product"
	"github.com/apigee/istio-mixer-adapter/apigee-istio/shared"
	"github.com/spf13/cobra"
)

const (
	servicesAttr         = "istio-services"
	productsURLFormat    = "%s/products"                                         // customerProxyURL
	productAttrURLFormat = "%s/v1/o/%s/apiproducts/%s/attributes/%s" // ManagementBase, prod, attr
)

type bindings struct {
	*shared.RootArgs
	products []product.APIProduct
}

// Cmd returns base command
func Cmd(rootArgs *shared.RootArgs, printf, fatalf shared.FormatFn) *cobra.Command {
	cfg := &bindings{RootArgs: rootArgs}

	c := &cobra.Command{
		Use:   "bindings",
		Short: "Manage Apigee Product to Istio Service bindings",
		Long:  "Manage Apigee Product to Istio Service bindings.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			return rootArgs.Resolve(false)
		},
	}

	c.AddCommand(cmdBindingsList(cfg, printf, fatalf))
	c.AddCommand(cmdBindingsAdd(cfg, printf, fatalf))
	c.AddCommand(cmdBindingsRemove(cfg, printf, fatalf))

	return c
}

func cmdBindingsList(b *bindings, printf, fatalf shared.FormatFn) *cobra.Command {
	c := &cobra.Command{
		Use:   "list",
		Short: "List Apigee Product to Istio Service bindings",
		Long:  "List Apigee Product to Istio Service bindings",
		Args:  cobra.NoArgs,

		Run: func(cmd *cobra.Command, _ []string) {
			b.cmdList(printf, fatalf)
		},
	}

	return c
}

func cmdBindingsAdd(b *bindings, printf, fatalf shared.FormatFn) *cobra.Command {
	c := &cobra.Command{
		Use:   "add [service name] [product name]",
		Short: "Add Istio Service binding to Apigee Product",
		Long:  "Add Istio Service binding to Apigee Product",
		Args:  cobra.ExactArgs(2),

		Run: func(cmd *cobra.Command, args []string) {
			serviceName := args[0]
			productName := args[1]
			p, err := b.getProduct(productName)
			if err != nil {
				fatalf("%v", err)
			}
			if p == nil {
				fatalf("invalid product name: %s", args[0])
			}

			b.bindService(p, serviceName, printf, fatalf)
		},
	}

	return c
}

func cmdBindingsRemove(b *bindings, printf, fatalf shared.FormatFn) *cobra.Command {
	c := &cobra.Command{
		Use:   "remove [service name] [product name]",
		Short: "Remove Istio Service binding from Apigee Product",
		Long:  "Remove Istio Service binding from Apigee Product",
		Args:  cobra.ExactArgs(2),

		Run: func(cmd *cobra.Command, args []string) {
			serviceName := args[0]
			productName := args[1]
			p, err := b.getProduct(productName)
			if err != nil {
				fatalf("%v", err)
			}
			if p == nil {
				fatalf("invalid product name: %s", args[0])
			}

			b.unbindService(p, serviceName, printf, fatalf)
		},
	}

	return c
}

func (b *bindings) getProduct(name string) (*product.APIProduct, error) {
	products, err := b.getProducts()
	if err != nil {
		return nil, err
	}
	for _, p := range products {
		if p.Name == name {
			return &p, nil
		}
	}
	return nil, nil
}

func (b *bindings) getProducts() ([]product.APIProduct, error) {
	if b.products != nil {
		return b.products, nil
	}
	productsURL := fmt.Sprintf(productsURLFormat, b.CustomerProxyURL)
	req, err := http.NewRequest(http.MethodGet, productsURL, nil)
	if err != nil {
		return nil, fmt.Errorf("error creating request: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")

	var res product.APIResponse
	resp, err := b.Client.Do(req, &res)
	if err != nil {
		return nil, fmt.Errorf("error retrieving products: %v", err)
	}
	defer resp.Body.Close()

	return res.APIProducts, nil
}

func (b *bindings) cmdList(printf, fatalf shared.FormatFn) error {
	products, err := b.getProducts()
	if err != nil {
		fatalf("%v", err)
	}
	var bound, unbound []product.APIProduct
	for _, p := range products {
		// server returns empty scopes as array with a single empty string, remove for consistency
		if len(p.Scopes) == 1 && p.Scopes[0] == "" {
			p.Scopes = []string{}
		}
		// server may return empty quota field as "null"
		if p.QuotaLimit == "null" {
			p.QuotaLimit = ""
		}
		p.Targets = p.GetBoundServices()
		if p.Targets == nil {
			unbound = append(bound, p)
		} else {
			bound = append(bound, p)
		}
	}

	sort.Sort(byName(bound))
	sort.Sort(byName(unbound))
	data := struct {
		Bound   []product.APIProduct
		Unbound []product.APIProduct
	}{
		Bound:   bound,
		Unbound: unbound,
	}
	tmp := template.New("products")
	tmp.Funcs(template.FuncMap{
		"scopes": func(in []string) string { return strings.Join(in, ",") },
	})
	tmp, err = tmp.Parse(productsTemplate)
	if err != nil {
		fatalf("failed to create template: %v", err)
	}
	err = tmp.Execute(os.Stdout, data)
	if err != nil {
		fatalf("failed to execute template: %v", err)
	}

	return nil
}

func (b *bindings) bindService(p *product.APIProduct, service string, printf, fatalf shared.FormatFn) {
	boundServices := p.GetBoundServices()
	if _, ok := indexOf(boundServices, service); ok {
		fatalf("service %s is already bound to %s", service, p.Name)
	}
	err := b.updateServiceBindings(p, append(boundServices, service))
	if err != nil {
		fatalf("error removing service %s from %s: %v", service, p.Name, err)
	}
	printf("product %s is now bound to: %s", p.Name, service)
}

func (b *bindings) unbindService(p *product.APIProduct, service string, printf, fatalf shared.FormatFn) {
	boundServices := p.GetBoundServices()
	i, ok := indexOf(boundServices, service)
	if !ok {
		fatalf("service %s is not bound to %s", service, p.Name)
	}
	boundServices = append(boundServices[:i], boundServices[i+1:]...)
	err := b.updateServiceBindings(p, boundServices)
	if err != nil {
		fatalf("error removing service %s from %s: %v", service, p.Name, err)
	}
	printf("product %s is no longer bound to: %s", p.Name, service)
}

func (b *bindings) updateServiceBindings(p *product.APIProduct, bindings []string) error {
	newAttr := struct {
		Value string `json:"value"`
	}{
		Value: strings.Join(bindings, ","),
	}
	req, err := b.Client.NewRequest(http.MethodPost, "", newAttr)
	if err != nil {
		return err
	}
	path := fmt.Sprintf(productAttrURLFormat, b.ManagementBase, b.Org, p.Name, servicesAttr)
	req.URL.Path = path // hack: negate client's incorrect method of determining base URL
	var attr product.Attribute
	_, err = b.Client.Do(req, &attr)
	return err
}

func indexOf(array []string, val string) (index int, exists bool) {
	index = -1
	for i, v := range array {
		if val == v {
			index = i
			exists = true
			break
		}
	}
	return
}

type byName []product.APIProduct

func (a byName) Len() int           { return len(a) }
func (a byName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name }

const productsTemplate = `
{{- define "product"}}
{{.Name}}:
 {{- if .Scopes}}
  Scopes: {{scopes (.Scopes)}}
 {{- end}}
 {{- if .QuotaLimit}}
  Quota: {{.QuotaLimit}} requests every {{.QuotaInterval}} {{.QuotaTimeUnit}}
 {{- end}}
 {{- if .Targets}}
  Service bindings:
  {{- range .Targets}}
    {{.}}
  {{- end}}
  Paths:
  {{- range .Resources}}
    {{.}}
  {{- end}}
 {{- end}}
{{- end}}
API Products
============
{{- if .Bound}}
Bound
-----
 {{- range .Bound}}
 {{- template "product" .}}
 {{- end}}
{{- end}}
{{- if .Unbound}}

Unbound
-------
 {{- range .Unbound}}
 {{- template "product" .}}
 {{- end}}
{{- end}}
`
<|endoftext|>


<commit_before>// Package zalando contains Zalando specific definitions for
// authorization.
package zalando

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/gin-gonic/gin"
	"github.com/golang/glog"
	"github.com/zalando/gin-oauth2"
	"golang.org/x/oauth2"
)

// AccessTuples has to be set by the client to grant access.
var AccessTuples []AccessTuple

// AccessTuple is the type defined for use in AccessTuples.
type AccessTuple struct {
	Realm string `yaml:"realm,omitempty"` // p.e. "employees", "services"
	Uid   string `yaml:"uid,omitempty"`   // UnixName
	Cn    string `yaml:"cn,omitempty"`    // RealName
}

// TeamInfo is defined like in TeamAPI json.
type TeamInfo struct {
	Id      string
	Id_name string
	Team_id string
	Type    string
	Name    string
	Mail    []string
}

// OAuth2Endpoint is similar to the definitions in golang.org/x/oauth2
var OAuth2Endpoint = oauth2.Endpoint{
	AuthURL:  "https://identity.zalando.com/oauth2/token",
	TokenURL: "https://info.services.auth.zalando.com/oauth2/tokeninfo",
}

// TeamAPI is a custom API
var TeamAPI string = "https://teams.auth.zalando.com/api/teams"

// RequestTeamInfo is a function that returns team information for a
// given token.
func RequestTeamInfo(tc *ginoauth2.TokenContainer, uri string) ([]byte, error) {
	var uv = make(url.Values)
	uv.Set("member", tc.Scopes["uid"].(string))
	info_url := uri + "?" + uv.Encode()
	client := &http.Client{Transport: &ginoauth2.Transport}
	req, err := http.NewRequest("GET", info_url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tc.Token.AccessToken))

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

// GroupCheck is an authorization function that checks, if the Token
// was issued for an employee of a specified team. The given
// TokenContainer must be valid. As side effect it sets "uid" and
// "team" in the gin.Context to the "official" team.
func GroupCheck(at []AccessTuple) func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	ats := at
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		blob, err := RequestTeamInfo(tc, TeamAPI)
		if err != nil {
			glog.Errorf("[Gin-OAuth] failed to get team info, caused by: %s", err)
			return false
		}
		var data []TeamInfo
		err = json.Unmarshal(blob, &data)
		if err != nil {
			glog.Errorf("[Gin-OAuth] JSON.Unmarshal failed, caused by: %s", err)
			return false
		}
		granted := false
		for _, teamInfo := range data {
			for idx := range ats {
				at := ats[idx]
				if teamInfo.Id == at.Uid {
					granted = true
					glog.Infof("[Gin-OAuth] Grant access to %s as team member of \"%s\"\n", tc.Scopes["uid"].(string), teamInfo.Id)
				}
				if teamInfo.Type == "official" {
					ctx.Set("uid", tc.Scopes["uid"].(string))
					ctx.Set("team", teamInfo.Id)
				}
			}
		}
		return granted
	}
}

// UidCheck is an authorization function that checks UID scope
// TokenContainer must be Valid. As side effect it sets "uid" and
// "cn" in the gin.Context to the authorized uid and cn (Realname).
func UidCheck(at []AccessTuple) func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	ats := at
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		uid := tc.Scopes["uid"].(string)
		for idx := range ats {
			at := ats[idx]
			if tc.Realm == at.Realm && uid == at.Uid {
				ctx.Set("uid", uid)  //in this way I can set the authorized uid
				ctx.Set("cn", at.Cn) //in this way I can set the authorized Realname
				glog.Infof("[Gin-OAuth] Grant access to %s\n", uid)
				return true
			}
		}
		return false
	}
}

// ScopeCheck does an OR check of scopes given from token of the
// request to all provided scopes. If one of provided scopes is in the
// Scopes of the token it grants access to the resource.
func ScopeCheck(name string, scopes ...string) func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	glog.Infof("ScopeCheck %s configured to grant access for scopes: %v", name, scopes)
	configuredScopes := scopes
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		scopesFromToken := make([]string, 0)
		for _, s := range configuredScopes {
			if cur, ok := tc.Scopes[s].(string); ok {
				glog.V(2).Infof("Found configured scope %s", cur)
				scopesFromToken = append(scopesFromToken, cur)
				ctx.Set(s, cur) // set value from token of configured scope to the context, which you can use in your application.
			}
		}
		return len(scopesFromToken) > 0
	}
}

// ScopeAndCheck does an AND check of scopes given from token of the
// request to all provided scopes. Only if all of provided scopes are found in the
// Scopes of the token it grants access to the resource.
func ScopeAndCheck(name string, scopes ...string) func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	glog.Infof("ScopeCheck %s configured to grant access only if scopes: %v are present", name, scopes)
	configuredScopes := scopes
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		scopesFromToken := make([]string, 0)
		for _, s := range configuredScopes {
			if cur, ok := tc.Scopes[s].(string); ok {
				glog.V(2).Infof("Found configured scope %s", cur)
				scopesFromToken = append(scopesFromToken, cur)
				ctx.Set(s, cur) // set value from token of configured scope to the context, which you can use in your application.
			} else {
				return false
			}
		}
		return true
	}
}

// NoAuthorization sets "team" and "uid" in the context without
// checking if the user/team is authorized.
func NoAuthorization() func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		blob, err := RequestTeamInfo(tc, TeamAPI)
		var data []TeamInfo
		err = json.Unmarshal(blob, &data)
		if err != nil {
			glog.Errorf("[Gin-OAuth] JSON.Unmarshal failed, caused by: %s", err)
		}
		for _, teamInfo := range data {
			if teamInfo.Type == "official" {
				ctx.Set("uid", tc.Scopes["uid"].(string))
				ctx.Set("team", teamInfo.Id)
				return true
			}
		}
		return true
	}
}
<commit_msg>zalando/gin-oauth2#48 removing the string cast<commit_after>// Package zalando contains Zalando specific definitions for
// authorization.
package zalando

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/gin-gonic/gin"
	"github.com/golang/glog"
	"github.com/zalando/gin-oauth2"
	"golang.org/x/oauth2"
)

// AccessTuples has to be set by the client to grant access.
var AccessTuples []AccessTuple

// AccessTuple is the type defined for use in AccessTuples.
type AccessTuple struct {
	Realm string `yaml:"realm,omitempty"` // p.e. "employees", "services"
	Uid   string `yaml:"uid,omitempty"`   // UnixName
	Cn    string `yaml:"cn,omitempty"`    // RealName
}

// TeamInfo is defined like in TeamAPI json.
type TeamInfo struct {
	Id      string
	Id_name string
	Team_id string
	Type    string
	Name    string
	Mail    []string
}

// OAuth2Endpoint is similar to the definitions in golang.org/x/oauth2
var OAuth2Endpoint = oauth2.Endpoint{
	AuthURL:  "https://identity.zalando.com/oauth2/token",
	TokenURL: "https://info.services.auth.zalando.com/oauth2/tokeninfo",
}

// TeamAPI is a custom API
var TeamAPI string = "https://teams.auth.zalando.com/api/teams"

// RequestTeamInfo is a function that returns team information for a
// given token.
func RequestTeamInfo(tc *ginoauth2.TokenContainer, uri string) ([]byte, error) {
	var uv = make(url.Values)
	uv.Set("member", tc.Scopes["uid"].(string))
	info_url := uri + "?" + uv.Encode()
	client := &http.Client{Transport: &ginoauth2.Transport}
	req, err := http.NewRequest("GET", info_url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tc.Token.AccessToken))

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

// GroupCheck is an authorization function that checks, if the Token
// was issued for an employee of a specified team. The given
// TokenContainer must be valid. As side effect it sets "uid" and
// "team" in the gin.Context to the "official" team.
func GroupCheck(at []AccessTuple) func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	ats := at
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		blob, err := RequestTeamInfo(tc, TeamAPI)
		if err != nil {
			glog.Errorf("[Gin-OAuth] failed to get team info, caused by: %s", err)
			return false
		}
		var data []TeamInfo
		err = json.Unmarshal(blob, &data)
		if err != nil {
			glog.Errorf("[Gin-OAuth] JSON.Unmarshal failed, caused by: %s", err)
			return false
		}
		granted := false
		for _, teamInfo := range data {
			for idx := range ats {
				at := ats[idx]
				if teamInfo.Id == at.Uid {
					granted = true
					glog.Infof("[Gin-OAuth] Grant access to %s as team member of \"%s\"\n", tc.Scopes["uid"].(string), teamInfo.Id)
				}
				if teamInfo.Type == "official" {
					ctx.Set("uid", tc.Scopes["uid"].(string))
					ctx.Set("team", teamInfo.Id)
				}
			}
		}
		return granted
	}
}

// UidCheck is an authorization function that checks UID scope
// TokenContainer must be Valid. As side effect it sets "uid" and
// "cn" in the gin.Context to the authorized uid and cn (Realname).
func UidCheck(at []AccessTuple) func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	ats := at
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		uid := tc.Scopes["uid"].(string)
		for idx := range ats {
			at := ats[idx]
			if tc.Realm == at.Realm && uid == at.Uid {
				ctx.Set("uid", uid)  //in this way I can set the authorized uid
				ctx.Set("cn", at.Cn) //in this way I can set the authorized Realname
				glog.Infof("[Gin-OAuth] Grant access to %s\n", uid)
				return true
			}
		}
		return false
	}
}

// ScopeCheck does an OR check of scopes given from token of the
// request to all provided scopes. If one of provided scopes is in the
// Scopes of the token it grants access to the resource.
func ScopeCheck(name string, scopes ...string) func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	glog.Infof("ScopeCheck %s configured to grant access for scopes: %v", name, scopes)
	configuredScopes := scopes
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		scopesFromToken := make([]string, 0)
		for _, s := range configuredScopes {
			if cur, ok := tc.Scopes[s]; ok {
				glog.V(2).Infof("Found configured scope %s", s)
				scopesFromToken = append(scopesFromToken, s)
				ctx.Set(s, cur) // set value from token of configured scope to the context, which you can use in your application.
			}
		}
		return len(scopesFromToken) > 0
	}
}

// ScopeAndCheck does an AND check of scopes given from token of the
// request to all provided scopes. Only if all of provided scopes are found in the
// Scopes of the token it grants access to the resource.
func ScopeAndCheck(name string, scopes ...string) func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	glog.Infof("ScopeCheck %s configured to grant access only if scopes: %v are present", name, scopes)
	configuredScopes := scopes
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		scopesFromToken := make([]string, 0)
		for _, s := range configuredScopes {
			if cur, ok := tc.Scopes[s]; ok {
				glog.V(2).Infof("Found configured scope %s", s)
				scopesFromToken = append(scopesFromToken, s)
				ctx.Set(s, cur) // set value from token of configured scope to the context, which you can use in your application.
			} else {
				return false
			}
		}
		return true
	}
}

// NoAuthorization sets "team" and "uid" in the context without
// checking if the user/team is authorized.
func NoAuthorization() func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
	return func(tc *ginoauth2.TokenContainer, ctx *gin.Context) bool {
		blob, err := RequestTeamInfo(tc, TeamAPI)
		var data []TeamInfo
		err = json.Unmarshal(blob, &data)
		if err != nil {
			glog.Errorf("[Gin-OAuth] JSON.Unmarshal failed, caused by: %s", err)
		}
		for _, teamInfo := range data {
			if teamInfo.Type == "official" {
				ctx.Set("uid", tc.Scopes["uid"].(string))
				ctx.Set("team", teamInfo.Id)
				return true
			}
		}
		return true
	}
}
<|endoftext|>


<commit_before>package zclwrite

import (
	"bytes"

	"github.com/zclconf/go-zcl/zcl"
)

// Parse interprets the given source bytes into a writer AST. The resulting AST
// can be used to perform surgical edits on the source code before turning
// it back into bytes again.
func Parse(src []byte, filename string, start zcl.Pos) (*File, zcl.Diagnostics) {
	return parse(src, filename, start)
}

// Format takes source code and performs simple whitespace changes to transform
// it to a canonical layout style.
//
// Format skips constructing an AST and works directly with tokens, so it
// is less expensive than formatting via the AST for situations where no other
// changes will be made. It also ignores syntax errors and can thus be applied
// to partial source code, although the result in that case may not be
// desirable.
func Format(src []byte) []byte {
	tokens := lexConfig(src)
	format(tokens)
	buf := &bytes.Buffer{}
	(&TokenSeq{tokens}).WriteTo(buf)
	return buf.Bytes()
}
<commit_msg>zclwrite: Rename "Parse" to "ParseConfig"<commit_after>package zclwrite

import (
	"bytes"

	"github.com/zclconf/go-zcl/zcl"
)

// ParseConfig interprets the given source bytes into a *zclwrite.File. The
// resulting AST can be used to perform surgical edits on the source code
// before turning it back into bytes again.
func ParseConfig(src []byte, filename string, start zcl.Pos) (*File, zcl.Diagnostics) {
	return parse(src, filename, start)
}

// Format takes source code and performs simple whitespace changes to transform
// it to a canonical layout style.
//
// Format skips constructing an AST and works directly with tokens, so it
// is less expensive than formatting via the AST for situations where no other
// changes will be made. It also ignores syntax errors and can thus be applied
// to partial source code, although the result in that case may not be
// desirable.
func Format(src []byte) []byte {
	tokens := lexConfig(src)
	format(tokens)
	buf := &bytes.Buffer{}
	(&TokenSeq{tokens}).WriteTo(buf)
	return buf.Bytes()
}
<|endoftext|>


<commit_before>package upgrade

import (
	"fmt"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libcompose/project"
	rancherClient "github.com/rancher/go-rancher/client"
	"github.com/rancher/rancher-compose/rancher"
)

type UpgradeOpts struct {
	BatchSize      int
	IntervalMillis int
	FinalScale     int
	UpdateLinks    bool
	Wait           bool
	CleanUp        bool
}

func Upgrade(p *project.Project, from, to string, opts UpgradeOpts) error {
	fromService, err := p.CreateService(from)
	if err != nil {
		return err
	}

	toService, err := p.CreateService(to)
	if err != nil {
		return err
	}

	rFromService, ok := fromService.(*rancher.RancherService)
	if !ok {
		return fmt.Errorf("%s is not a Rancher service", from)
	}

	rToService, ok := toService.(*rancher.RancherService)
	if !ok {
		return fmt.Errorf("%s is not a Rancher service", to)
	}

	if service, err := rToService.RancherService(); err != nil {
		return err
	} else if service == nil {
		if err := rToService.Create(); err != nil {
			return err
		}

		if err := rToService.Scale(0); err != nil {
			return err
		}
	}

	if err := rToService.Up(); err != nil {
		return err
	}

	source, err := rFromService.RancherService()
	if err != nil {
		return err
	}

	dest, err := rToService.RancherService()
	if err != nil {
		return err
	}

	if source == nil {
		return fmt.Errorf("Failed to find service %s", from)
	}

	if dest == nil {
		return fmt.Errorf("Failed to find service %s", to)
	}

	upgradeOpts := &rancherClient.ServiceUpgrade{
		UpdateLinks:    opts.UpdateLinks,
		FinalScale:     int64(opts.FinalScale),
		BatchSize:      int64(opts.BatchSize),
		IntervalMillis: int64(opts.IntervalMillis),
		ToServiceId:    dest.Id,
	}
	if upgradeOpts.FinalScale == -1 {
		upgradeOpts.FinalScale = source.Scale
	}

	client := rFromService.Client()

	logrus.Infof("Upgrading %s to %s, scale=%d", from, to, upgradeOpts.FinalScale)
	service, err := client.Service.ActionUpgrade(source, upgradeOpts)
	if err != nil {
		return err
	}

	if opts.Wait || opts.CleanUp {
		if err := rFromService.Wait(service); err != nil {
			return err
		}
	}

	if opts.CleanUp {
		if err := rFromService.Delete(); err != nil {
			return err
		}
	}

	return nil
}

func upgradeInfo(up bool, p *project.Project, from, to string, opts UpgradeOpts) (*rancherClient.Service, *rancherClient.Service, *rancherClient.RancherClient, error) {
	fromService, err := p.CreateService(from)
	if err != nil {
		return nil, nil, nil, err
	}

	toService, err := p.CreateService(to)
	if err != nil {
		return nil, nil, nil, err
	}

	rFromService, ok := fromService.(*rancher.RancherService)
	if !ok {
		return nil, nil, nil, fmt.Errorf("%s is not a Rancher service", from)
	}

	rToService, ok := toService.(*rancher.RancherService)
	if !ok {
		return nil, nil, nil, fmt.Errorf("%s is not a Rancher service", to)
	}

	if up {
		if err := rToService.Up(); err != nil {
			return nil, nil, nil, err
		}
	}

	source, err := rFromService.RancherService()
	if err != nil {
		return nil, nil, nil, err
	}

	dest, err := rToService.RancherService()
	if err != nil {
		return nil, nil, nil, err
	}

	return source, dest, rFromService.Client(), nil
}
<commit_msg>Don't delete source service if scale is not 0<commit_after>package upgrade

import (
	"fmt"

	"github.com/Sirupsen/logrus"
	"github.com/docker/libcompose/project"
	rancherClient "github.com/rancher/go-rancher/client"
	"github.com/rancher/rancher-compose/rancher"
)

type UpgradeOpts struct {
	BatchSize      int
	IntervalMillis int
	FinalScale     int
	UpdateLinks    bool
	Wait           bool
	CleanUp        bool
}

func Upgrade(p *project.Project, from, to string, opts UpgradeOpts) error {
	fromService, err := p.CreateService(from)
	if err != nil {
		return err
	}

	toService, err := p.CreateService(to)
	if err != nil {
		return err
	}

	rFromService, ok := fromService.(*rancher.RancherService)
	if !ok {
		return fmt.Errorf("%s is not a Rancher service", from)
	}

	rToService, ok := toService.(*rancher.RancherService)
	if !ok {
		return fmt.Errorf("%s is not a Rancher service", to)
	}

	if service, err := rToService.RancherService(); err != nil {
		return err
	} else if service == nil {
		if err := rToService.Create(); err != nil {
			return err
		}

		if err := rToService.Scale(0); err != nil {
			return err
		}
	}

	if err := rToService.Up(); err != nil {
		return err
	}

	source, err := rFromService.RancherService()
	if err != nil {
		return err
	}

	dest, err := rToService.RancherService()
	if err != nil {
		return err
	}

	if source == nil {
		return fmt.Errorf("Failed to find service %s", from)
	}

	if dest == nil {
		return fmt.Errorf("Failed to find service %s", to)
	}

	upgradeOpts := &rancherClient.ServiceUpgrade{
		UpdateLinks:    opts.UpdateLinks,
		FinalScale:     int64(opts.FinalScale),
		BatchSize:      int64(opts.BatchSize),
		IntervalMillis: int64(opts.IntervalMillis),
		ToServiceId:    dest.Id,
	}
	if upgradeOpts.FinalScale == -1 {
		upgradeOpts.FinalScale = source.Scale
	}

	client := rFromService.Client()

	logrus.Infof("Upgrading %s to %s, scale=%d", from, to, upgradeOpts.FinalScale)
	service, err := client.Service.ActionUpgrade(source, upgradeOpts)
	if err != nil {
		return err
	}

	if opts.Wait || opts.CleanUp {
		if err := rFromService.Wait(service); err != nil {
			return err
		}
	}

	if opts.CleanUp {
		// Reload source to check scale
		source, err = rFromService.RancherService()
		if err != nil {
			return err
		}

		if source.Scale == 0 {
			if err := rFromService.Delete(); err != nil {
				return err
			}
		} else {
			logrus.Warnf("Not deleting service %s, scale is not 0 but %d", source.Name, source.Scale)
		}
	}

	return nil
}

func upgradeInfo(up bool, p *project.Project, from, to string, opts UpgradeOpts) (*rancherClient.Service, *rancherClient.Service, *rancherClient.RancherClient, error) {
	fromService, err := p.CreateService(from)
	if err != nil {
		return nil, nil, nil, err
	}

	toService, err := p.CreateService(to)
	if err != nil {
		return nil, nil, nil, err
	}

	rFromService, ok := fromService.(*rancher.RancherService)
	if !ok {
		return nil, nil, nil, fmt.Errorf("%s is not a Rancher service", from)
	}

	rToService, ok := toService.(*rancher.RancherService)
	if !ok {
		return nil, nil, nil, fmt.Errorf("%s is not a Rancher service", to)
	}

	if up {
		if err := rToService.Up(); err != nil {
			return nil, nil, nil, err
		}
	}

	source, err := rFromService.RancherService()
	if err != nil {
		return nil, nil, nil, err
	}

	dest, err := rToService.RancherService()
	if err != nil {
		return nil, nil, nil, err
	}

	return source, dest, rFromService.Client(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func TestCheckHostInWhiteListWithEmptyConfiguration(t *testing.T) {
	config := new(Configuration)
	validator := Validator{config}

	if err := validator.CheckHostInWhiteList("doesnt exists"); err == nil {
		t.Errorf("Missing error returning!")
	}
}

func TestCheckHostInWhiteListWithSomeHostsInWhieList(t *testing.T) {
	config := new(Configuration)
	config.HostWhiteList = []string{"http://www.google.com", "two hosts"}
	validator := Validator{config}

	// Check for one that doesn't exists
	err := validator.CheckHostInWhiteList("http://www.sergiosola.com/")
	if err == nil {
		t.Errorf("Should return an error!!!")
	}
}

func TestCheckHostInWhiteListWithValidHost(t *testing.T) {
	config := new(Configuration)
	config.HostWhiteList = []string{"one host", "sergiosola.com"}
	validator := Validator{config}

	err := validator.CheckHostInWhiteList("https://sergiosola.com/images?withParams=dsada")
	if err != nil {
		t.Errorf("Should not to return an error!!!")
	}
}

func TestCheckHostInWhiteListWithValidPattern(t *testing.T) {
	config := new(Configuration)
	config.HostWhiteList = []string{"www.google.com", "([a-z]+).cdn.google.com"}
	validator := Validator{config}

	err := validator.CheckHostInWhiteList("https://dsadsaasds.cdn.google.com/images?withParams=dsada")
	if err != nil {
		t.Errorf("Should not to return an error!!!")
	}
}
<commit_msg>rename tests<commit_after>package main

import (
	"testing"
)

func Test_CheckHostInWhiteListWithEmptyConfiguration(t *testing.T) {
	config := new(Configuration)
	validator := Validator{config}

	if err := validator.CheckHostInWhiteList("doesnt exists"); err == nil {
		t.Errorf("Missing error returning!")
	}
}

func Test_CheckHostInWhiteListWithSomeHostsInWhieList(t *testing.T) {
	config := new(Configuration)
	config.HostWhiteList = []string{"http://www.google.com", "two hosts"}
	validator := Validator{config}

	// Check for one that doesn't exists
	err := validator.CheckHostInWhiteList("http://www.sergiosola.com/")
	if err == nil {
		t.Errorf("Should return an error!!!")
	}
}

func Test_CheckHostInWhiteListWithValidHost(t *testing.T) {
	config := new(Configuration)
	config.HostWhiteList = []string{"one host", "sergiosola.com"}
	validator := Validator{config}

	err := validator.CheckHostInWhiteList("https://sergiosola.com/images?withParams=dsada")
	if err != nil {
		t.Errorf("Should not to return an error!!!")
	}
}

func Test_CheckHostInWhiteListWithValidPattern(t *testing.T) {
	config := new(Configuration)
	config.HostWhiteList = []string{"www.google.com", "([a-z]+).cdn.google.com"}
	validator := Validator{config}

	err := validator.CheckHostInWhiteList("https://dsadsaasds.cdn.google.com/images?withParams=dsada")
	if err != nil {
		t.Errorf("Should not to return an error!!!")
	}
}
<|endoftext|>