<commit_before>// Package zfs provides wrappers around the ZFS command line tools.
package zfs

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

// ZFS dataset types, which can indicate if a dataset is a filesystem,
// snapshot, or volume.
const (
	DatasetFilesystem = "filesystem"
	DatasetSnapshot   = "snapshot"
	DatasetVolume     = "volume"
)

// Dataset is a ZFS dataset.  A dataset could be a clone, filesystem, snapshot,
// or volume.  The Type struct member can be used to determine a dataset's type.
//
// The field definitions can be found in the ZFS manual:
// http://www.freebsd.org/cgi/man.cgi?zfs(8).
type Dataset struct {
	Name          string
	Origin        string
	Used          uint64
	Avail         uint64
	Mountpoint    string
	Compression   string
	Type          string
	Written       uint64
	Volsize       uint64
	Usedbydataset uint64
	Logicalused   uint64
	Quota         uint64
}

// InodeType is the type of inode as reported by Diff
type InodeType int

// Types of Inodes
const (
	_                     = iota // 0 == unknown type
	BlockDevice InodeType = iota
	CharacterDevice
	Directory
	Door
	NamedPipe
	SymbolicLink
	EventPort
	Socket
	File
)

// ChangeType is the type of inode change as reported by Diff
type ChangeType int

// Types of Changes
const (
	_                  = iota // 0 == unknown type
	Removed ChangeType = iota
	Created
	Modified
	Renamed
)

// DestroyFlag is the options flag passed to Destroy
type DestroyFlag int

// Valid destroy options
const (
	DestroyDefault         DestroyFlag = 1 << iota
	DestroyRecursive                   = 1 << iota
	DestroyRecursiveClones             = 1 << iota
	DestroyDeferDeletion               = 1 << iota
	DestroyForceUmount                 = 1 << iota
)

// InodeChange represents a change as reported by Diff
type InodeChange struct {
	Change               ChangeType
	Type                 InodeType
	Path                 string
	NewPath              string
	ReferenceCountChange int
}

// Logger can be used to log commands/actions
type Logger interface {
	Log(cmd []string)
}

type defaultLogger struct{}

func (*defaultLogger) Log(cmd []string) {
	return
}

var logger Logger = &defaultLogger{}

// SetLogger set a log handler to log all commands including arguments before
// they are executed
func SetLogger(l Logger) {
	if l != nil {
		logger = l
	}
}

// zfs is a helper function to wrap typical calls to zfs.
func zfs(arg ...string) ([][]string, error) {
	c := command{Command: "zfs"}
	return c.Run(arg...)
}

// Datasets returns a slice of ZFS datasets, regardless of type.
// A filter argument may be passed to select a dataset with the matching name,
// or empty string ("") may be used to select all datasets.
func Datasets(filter string) ([]*Dataset, error) {
	return listByType("all", filter)
}

// Snapshots returns a slice of ZFS snapshots.
// A filter argument may be passed to select a snapshot with the matching name,
// or empty string ("") may be used to select all snapshots.
func Snapshots(filter string) ([]*Dataset, error) {
	return listByType(DatasetSnapshot, filter)
}

// Filesystems returns a slice of ZFS filesystems.
// A filter argument may be passed to select a filesystem with the matching name,
// or empty string ("") may be used to select all filesystems.
func Filesystems(filter string) ([]*Dataset, error) {
	return listByType(DatasetFilesystem, filter)
}

// Volumes returns a slice of ZFS volumes.
// A filter argument may be passed to select a volume with the matching name,
// or empty string ("") may be used to select all volumes.
func Volumes(filter string) ([]*Dataset, error) {
	return listByType(DatasetVolume, filter)
}

// GetDataset retrieves a single ZFS dataset by name.  This dataset could be
// any valid ZFS dataset type, such as a clone, filesystem, snapshot, or volume.
func GetDataset(name string) (*Dataset, error) {
	out, err := zfs("get", "-Hp", "all", name)
	if err != nil {
		return nil, err
	}

	ds := &Dataset{Name: name}
	for _, line := range out {
		if err := ds.parseLine(line); err != nil {
			return nil, err
		}
	}

	return ds, nil
}

// Clone clones a ZFS snapshot and returns a clone dataset.
// An error will be returned if the input dataset is not of snapshot type.
func (d *Dataset) Clone(dest string, properties map[string]string) (*Dataset, error) {
	if d.Type != DatasetSnapshot {
		return nil, errors.New("can only clone snapshots")
	}
	args := make([]string, 2, 4)
	args[0] = "clone"
	args[1] = "-p"
	if properties != nil {
		args = append(args, propsSlice(properties)...)
	}
	args = append(args, []string{d.Name, dest}...)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(dest)
}

// ReceiveSnapshot receives a ZFS stream from the input io.Reader, creates a
// new snapshot with the specified name, and streams the input data into the
// newly-created snapshot.
func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) {
	c := command{Command: "zfs", Stdin: input}
	_, err := c.Run("receive", name)
	if err != nil {
		return nil, err
	}
	return GetDataset(name)
}

// SendSnapshot sends a ZFS stream of a snapshot to the input io.Writer.
// An error will be returned if the input dataset is not of snapshot type.
func (d *Dataset) SendSnapshot(output io.Writer) error {
	if d.Type != DatasetSnapshot {
		return errors.New("can only send snapshots")
	}

	c := command{Command: "zfs", Stdout: output}
	_, err := c.Run("send", d.Name)
	return err
}

// CreateVolume creates a new ZFS volume with the specified name, size, and
// properties.
// A full list of available ZFS properties may be found here:
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
func CreateVolume(name string, size uint64, properties map[string]string) (*Dataset, error) {
	args := make([]string, 4, 5)
	args[0] = "create"
	args[1] = "-p"
	args[2] = "-V"
	args[3] = strconv.FormatUint(size, 10)
	if properties != nil {
		args = append(args, propsSlice(properties)...)
	}
	args = append(args, name)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(name)
}

// Destroy destroys a ZFS dataset. If the destroy bit flag is set, any
// descendents of the dataset will be recursively destroyed, including snapshots.
// If the deferred bit flag is set, the snapshot is marked for deferred
// deletion.
func (d *Dataset) Destroy(flags DestroyFlag) error {
	args := make([]string, 1, 3)
	args[0] = "destroy"
	if flags&DestroyRecursive != 0 {
		args = append(args, "-r")
	}

	if flags&DestroyRecursiveClones != 0 {
		args = append(args, "-R")
	}

	if flags&DestroyDeferDeletion != 0 {
		args = append(args, "-d")
	}

	if flags&DestroyForceUmount != 0 {
		args = append(args, "-f")
	}

	args = append(args, d.Name)
	_, err := zfs(args...)
	return err
}

// SetProperty sets a ZFS property on the receiving dataset.
// A full list of available ZFS properties may be found here:
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
func (d *Dataset) SetProperty(key, val string) error {
	prop := strings.Join([]string{key, val}, "=")
	_, err := zfs("set", prop, d.Name)
	return err
}

// GetProperty returns the current value of a ZFS property from the
// receiving dataset.
// A full list of available ZFS properties may be found here:
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
func (d *Dataset) GetProperty(key string) (string, error) {
	out, err := zfs("get", key, d.Name)
	if err != nil {
		return "", err
	}

	return out[0][2], nil
}

// Rename renames a dataset.
func (d *Dataset) Rename(name string, createParent bool, recursiveRenameSnapshots bool) (*Dataset, error) {
	args := make([]string, 3, 5)
	args[0] = "rename"
	args[1] = d.Name
	args[2] = name
	if createParent {
		args = append(args, "-p")
	}
	if recursiveRenameSnapshots {
		args = append(args, "-r")
	}
	_, err := zfs(args...)
	if err != nil {
		return d, err
	}

	return GetDataset(name)
}

// Snapshots returns a slice of all ZFS snapshots of a given dataset.
func (d *Dataset) Snapshots() ([]*Dataset, error) {
	return Snapshots(d.Name)
}

// CreateFilesystem creates a new ZFS filesystem with the specified name and
// properties.
// A full list of available ZFS properties may be found here:
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
func CreateFilesystem(name string, properties map[string]string) (*Dataset, error) {
	args := make([]string, 1, 4)
	args[0] = "create"

	if properties != nil {
		args = append(args, propsSlice(properties)...)
	}

	args = append(args, name)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(name)
}

// Snapshot creates a new ZFS snapshot of the receiving dataset, using the
// specified name.  Optionally, the snapshot can be taken recursively, creating
// snapshots of all descendent filesystems in a single, atomic operation.
func (d *Dataset) Snapshot(name string, recursive bool) (*Dataset, error) {
	args := make([]string, 1, 4)
	args[0] = "snapshot"
	if recursive {
		args = append(args, "-r")
	}
	snapName := fmt.Sprintf("%s@%s", d.Name, name)
	args = append(args, snapName)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(snapName)
}

// Rollback rolls back the receiving ZFS dataset to a previous snapshot.
// Optionally, intermediate snapshots can be destroyed.  A ZFS snapshot
// rollback cannot be completed without this option, if more recent
// snapshots exist.
// An error will be returned if the input dataset is not of snapshot type.
func (d *Dataset) Rollback(destroyMoreRecent bool) error {
	if d.Type != DatasetSnapshot {
		return errors.New("can only rollback snapshots")
	}

	args := make([]string, 1, 3)
	args[0] = "rollback"
	if destroyMoreRecent {
		args = append(args, "-r")
	}
	args = append(args, d.Name)

	_, err := zfs(args...)
	return err
}

// Children returns a slice of children of the receiving ZFS dataset.
// A recursion depth may be specified, or a depth of 0 allows unlimited
// recursion.
func (d *Dataset) Children(depth uint64) ([]*Dataset, error) {
	args := []string{"get", "-t", "all", "-Hp", "all"}
	if depth > 0 {
		args = append(args, "-d")
		args = append(args, strconv.FormatUint(depth, 10))
	} else {
		args = append(args, "-r")
	}
	args = append(args, d.Name)

	out, err := zfs(args...)
	if err != nil {
		return nil, err
	}

	var datasets []*Dataset
	name := ""
	var ds *Dataset
	for _, line := range out {
		if name != line[0] {
			name = line[0]
			ds = &Dataset{Name: name}
			datasets = append(datasets, ds)
		}
		if err := ds.parseLine(line); err != nil {
			return nil, err
		}
	}
	return datasets[1:], nil
}

// Diff returns changes between a snapshot and the given ZFS dataset.
// The snapshot name must include the filesystem part as it is possible to
// compare clones with their origin snapshots.
func (d *Dataset) Diff(snapshot string) ([]*InodeChange, error) {
	args := []string{"diff", "-FH", snapshot, d.Name}[:]
	out, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	inodeChanges, err := parseInodeChanges(out)
	if err != nil {
		return nil, err
	}
	return inodeChanges, nil
}
<commit_msg>Added mount and umount methods<commit_after>// Package zfs provides wrappers around the ZFS command line tools.
package zfs

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

// ZFS dataset types, which can indicate if a dataset is a filesystem,
// snapshot, or volume.
const (
	DatasetFilesystem = "filesystem"
	DatasetSnapshot   = "snapshot"
	DatasetVolume     = "volume"
)

// Dataset is a ZFS dataset.  A dataset could be a clone, filesystem, snapshot,
// or volume.  The Type struct member can be used to determine a dataset's type.
//
// The field definitions can be found in the ZFS manual:
// http://www.freebsd.org/cgi/man.cgi?zfs(8).
type Dataset struct {
	Name          string
	Origin        string
	Used          uint64
	Avail         uint64
	Mountpoint    string
	Compression   string
	Type          string
	Written       uint64
	Volsize       uint64
	Usedbydataset uint64
	Logicalused   uint64
	Quota         uint64
}

// InodeType is the type of inode as reported by Diff
type InodeType int

// Types of Inodes
const (
	_                     = iota // 0 == unknown type
	BlockDevice InodeType = iota
	CharacterDevice
	Directory
	Door
	NamedPipe
	SymbolicLink
	EventPort
	Socket
	File
)

// ChangeType is the type of inode change as reported by Diff
type ChangeType int

// Types of Changes
const (
	_                  = iota // 0 == unknown type
	Removed ChangeType = iota
	Created
	Modified
	Renamed
)

// DestroyFlag is the options flag passed to Destroy
type DestroyFlag int

// Valid destroy options
const (
	DestroyDefault         DestroyFlag = 1 << iota
	DestroyRecursive                   = 1 << iota
	DestroyRecursiveClones             = 1 << iota
	DestroyDeferDeletion               = 1 << iota
	DestroyForceUmount                 = 1 << iota
)

// InodeChange represents a change as reported by Diff
type InodeChange struct {
	Change               ChangeType
	Type                 InodeType
	Path                 string
	NewPath              string
	ReferenceCountChange int
}

// Logger can be used to log commands/actions
type Logger interface {
	Log(cmd []string)
}

type defaultLogger struct{}

func (*defaultLogger) Log(cmd []string) {
	return
}

var logger Logger = &defaultLogger{}

// SetLogger set a log handler to log all commands including arguments before
// they are executed
func SetLogger(l Logger) {
	if l != nil {
		logger = l
	}
}

// zfs is a helper function to wrap typical calls to zfs.
func zfs(arg ...string) ([][]string, error) {
	c := command{Command: "zfs"}
	return c.Run(arg...)
}

// Datasets returns a slice of ZFS datasets, regardless of type.
// A filter argument may be passed to select a dataset with the matching name,
// or empty string ("") may be used to select all datasets.
func Datasets(filter string) ([]*Dataset, error) {
	return listByType("all", filter)
}

// Snapshots returns a slice of ZFS snapshots.
// A filter argument may be passed to select a snapshot with the matching name,
// or empty string ("") may be used to select all snapshots.
func Snapshots(filter string) ([]*Dataset, error) {
	return listByType(DatasetSnapshot, filter)
}

// Filesystems returns a slice of ZFS filesystems.
// A filter argument may be passed to select a filesystem with the matching name,
// or empty string ("") may be used to select all filesystems.
func Filesystems(filter string) ([]*Dataset, error) {
	return listByType(DatasetFilesystem, filter)
}

// Volumes returns a slice of ZFS volumes.
// A filter argument may be passed to select a volume with the matching name,
// or empty string ("") may be used to select all volumes.
func Volumes(filter string) ([]*Dataset, error) {
	return listByType(DatasetVolume, filter)
}

// GetDataset retrieves a single ZFS dataset by name.  This dataset could be
// any valid ZFS dataset type, such as a clone, filesystem, snapshot, or volume.
func GetDataset(name string) (*Dataset, error) {
	out, err := zfs("get", "-Hp", "all", name)
	if err != nil {
		return nil, err
	}

	ds := &Dataset{Name: name}
	for _, line := range out {
		if err := ds.parseLine(line); err != nil {
			return nil, err
		}
	}

	return ds, nil
}

// Clone clones a ZFS snapshot and returns a clone dataset.
// An error will be returned if the input dataset is not of snapshot type.
func (d *Dataset) Clone(dest string, properties map[string]string) (*Dataset, error) {
	if d.Type != DatasetSnapshot {
		return nil, errors.New("can only clone snapshots")
	}
	args := make([]string, 2, 4)
	args[0] = "clone"
	args[1] = "-p"
	if properties != nil {
		args = append(args, propsSlice(properties)...)
	}
	args = append(args, []string{d.Name, dest}...)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(dest)
}

func (d *Dataset) Umount(force bool) (*Dataset, error) {
	if d.Type == DatasetSnapshot {
		return nil, errors.New("cannot umount snapshots")
	}
	args := make([]string, 1, 3)
	args[0] = "umount"
	if force {
		args = append(args, "-f")
	}
	args = append(args, d.Name)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(d.Name)
}

func (d *Dataset) Mount(overlay bool, options []string) (*Dataset, error) {
	if d.Type == DatasetSnapshot {
		return nil, errors.New("cannot mount snapshots")
	}
	args := make([]string, 1, 3)
	args[0] = "mount"
	if overlay {
		args = append(args, "-O")
	}
	if options != nil {
		args = append(args, "-o")
		args = append(args, strings.Join(options, ","))
	}
	args = append(args, d.Name)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(d.Name)
}

// ReceiveSnapshot receives a ZFS stream from the input io.Reader, creates a
// new snapshot with the specified name, and streams the input data into the
// newly-created snapshot.
func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) {
	c := command{Command: "zfs", Stdin: input}
	_, err := c.Run("receive", name)
	if err != nil {
		return nil, err
	}
	return GetDataset(name)
}

// SendSnapshot sends a ZFS stream of a snapshot to the input io.Writer.
// An error will be returned if the input dataset is not of snapshot type.
func (d *Dataset) SendSnapshot(output io.Writer) error {
	if d.Type != DatasetSnapshot {
		return errors.New("can only send snapshots")
	}

	c := command{Command: "zfs", Stdout: output}
	_, err := c.Run("send", d.Name)
	return err
}

// CreateVolume creates a new ZFS volume with the specified name, size, and
// properties.
// A full list of available ZFS properties may be found here:
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
func CreateVolume(name string, size uint64, properties map[string]string) (*Dataset, error) {
	args := make([]string, 4, 5)
	args[0] = "create"
	args[1] = "-p"
	args[2] = "-V"
	args[3] = strconv.FormatUint(size, 10)
	if properties != nil {
		args = append(args, propsSlice(properties)...)
	}
	args = append(args, name)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(name)
}

// Destroy destroys a ZFS dataset. If the destroy bit flag is set, any
// descendents of the dataset will be recursively destroyed, including snapshots.
// If the deferred bit flag is set, the snapshot is marked for deferred
// deletion.
func (d *Dataset) Destroy(flags DestroyFlag) error {
	args := make([]string, 1, 3)
	args[0] = "destroy"
	if flags&DestroyRecursive != 0 {
		args = append(args, "-r")
	}

	if flags&DestroyRecursiveClones != 0 {
		args = append(args, "-R")
	}

	if flags&DestroyDeferDeletion != 0 {
		args = append(args, "-d")
	}

	if flags&DestroyForceUmount != 0 {
		args = append(args, "-f")
	}

	args = append(args, d.Name)
	_, err := zfs(args...)
	return err
}

// SetProperty sets a ZFS property on the receiving dataset.
// A full list of available ZFS properties may be found here:
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
func (d *Dataset) SetProperty(key, val string) error {
	prop := strings.Join([]string{key, val}, "=")
	_, err := zfs("set", prop, d.Name)
	return err
}

// GetProperty returns the current value of a ZFS property from the
// receiving dataset.
// A full list of available ZFS properties may be found here:
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
func (d *Dataset) GetProperty(key string) (string, error) {
	out, err := zfs("get", key, d.Name)
	if err != nil {
		return "", err
	}

	return out[0][2], nil
}

// Rename renames a dataset.
func (d *Dataset) Rename(name string, createParent bool, recursiveRenameSnapshots bool) (*Dataset, error) {
	args := make([]string, 3, 5)
	args[0] = "rename"
	args[1] = d.Name
	args[2] = name
	if createParent {
		args = append(args, "-p")
	}
	if recursiveRenameSnapshots {
		args = append(args, "-r")
	}
	_, err := zfs(args...)
	if err != nil {
		return d, err
	}

	return GetDataset(name)
}

// Snapshots returns a slice of all ZFS snapshots of a given dataset.
func (d *Dataset) Snapshots() ([]*Dataset, error) {
	return Snapshots(d.Name)
}

// CreateFilesystem creates a new ZFS filesystem with the specified name and
// properties.
// A full list of available ZFS properties may be found here:
// https://www.freebsd.org/cgi/man.cgi?zfs(8).
func CreateFilesystem(name string, properties map[string]string) (*Dataset, error) {
	args := make([]string, 1, 4)
	args[0] = "create"

	if properties != nil {
		args = append(args, propsSlice(properties)...)
	}

	args = append(args, name)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(name)
}

// Snapshot creates a new ZFS snapshot of the receiving dataset, using the
// specified name.  Optionally, the snapshot can be taken recursively, creating
// snapshots of all descendent filesystems in a single, atomic operation.
func (d *Dataset) Snapshot(name string, recursive bool) (*Dataset, error) {
	args := make([]string, 1, 4)
	args[0] = "snapshot"
	if recursive {
		args = append(args, "-r")
	}
	snapName := fmt.Sprintf("%s@%s", d.Name, name)
	args = append(args, snapName)
	_, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	return GetDataset(snapName)
}

// Rollback rolls back the receiving ZFS dataset to a previous snapshot.
// Optionally, intermediate snapshots can be destroyed.  A ZFS snapshot
// rollback cannot be completed without this option, if more recent
// snapshots exist.
// An error will be returned if the input dataset is not of snapshot type.
func (d *Dataset) Rollback(destroyMoreRecent bool) error {
	if d.Type != DatasetSnapshot {
		return errors.New("can only rollback snapshots")
	}

	args := make([]string, 1, 3)
	args[0] = "rollback"
	if destroyMoreRecent {
		args = append(args, "-r")
	}
	args = append(args, d.Name)

	_, err := zfs(args...)
	return err
}

// Children returns a slice of children of the receiving ZFS dataset.
// A recursion depth may be specified, or a depth of 0 allows unlimited
// recursion.
func (d *Dataset) Children(depth uint64) ([]*Dataset, error) {
	args := []string{"get", "-t", "all", "-Hp", "all"}
	if depth > 0 {
		args = append(args, "-d")
		args = append(args, strconv.FormatUint(depth, 10))
	} else {
		args = append(args, "-r")
	}
	args = append(args, d.Name)

	out, err := zfs(args...)
	if err != nil {
		return nil, err
	}

	var datasets []*Dataset
	name := ""
	var ds *Dataset
	for _, line := range out {
		if name != line[0] {
			name = line[0]
			ds = &Dataset{Name: name}
			datasets = append(datasets, ds)
		}
		if err := ds.parseLine(line); err != nil {
			return nil, err
		}
	}
	return datasets[1:], nil
}

// Diff returns changes between a snapshot and the given ZFS dataset.
// The snapshot name must include the filesystem part as it is possible to
// compare clones with their origin snapshots.
func (d *Dataset) Diff(snapshot string) ([]*InodeChange, error) {
	args := []string{"diff", "-FH", snapshot, d.Name}[:]
	out, err := zfs(args...)
	if err != nil {
		return nil, err
	}
	inodeChanges, err := parseInodeChanges(out)
	if err != nil {
		return nil, err
	}
	return inodeChanges, nil
}
<|endoftext|>


<commit_before>package route

import (
	"fmt"
	"hash/adler32"
	"io"
	"log"
	"net/http"
	"path"
	"strings"

	"github.com/pachyderm-io/pfs/lib/etcache"
)

func HashResource(resource string) uint64 {
	return uint64(adler32.Checksum([]byte(resource)))
}

func hashRequest(r *http.Request) uint64 {
	return HashResource(r.URL.Path)
}

func Route(r *http.Request, etcdKey string, modulos uint64) (io.ReadCloser, error) {
	bucket := hashRequest(r) % modulos
	shard := fmt.Sprint(bucket, "-", fmt.Sprint(modulos))

	_master, err := etcache.Get(path.Join(etcdKey, shard), false, false)
	if err != nil {
		return nil, err
	}
	master := _master.Node.Value

	httpClient := &http.Client{}
	// `Do` will complain if r.RequestURI is set so we unset it
	r.RequestURI = ""
	r.URL.Scheme = "http"
	r.URL.Host = master
	resp, err := httpClient.Do(r)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Failed request (%s) to %s.", resp.Status, r.URL.String())
	}
	return resp.Body, nil
}

func RouteHttp(w http.ResponseWriter, r *http.Request, etcdKey string, modulos uint64) {
	reader, err := Route(r, etcdKey, modulos)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
	_, err = io.Copy(w, reader)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
}

type multiReadCloser struct {
	readers []io.ReadCloser
}

func (mr *multiReadCloser) Read(p []byte) (n int, err error) {
	for len(mr.readers) > 0 {
		n, err = mr.readers[0].Read(p)
		if n > 0 || err != io.EOF {
			if err == io.EOF {
				// Don't return EOF yet. There may be more bytes
				// in the remaining readers.
				err = nil
			}
			return
		}
		err = mr.readers[0].Close()
		if err != nil {
			return
		}
		mr.readers = mr.readers[1:]
	}
	return 0, io.EOF
}

func (mr *multiReadCloser) Close() error {
	for len(mr.readers) > 0 {
		err := mr.readers[0].Close()
		if err != nil {
			return err
		}
		mr.readers = mr.readers[1:]
	}
	return nil
}

// MultiReadCloser returns a ReaderCloser that's the logical concatenation of
// the provided input readers. They're read sequentially.  Once all inputs have
// returned EOF, Read will return EOF. If any of the readers return a non-nil,
// non-EOF error, Read will return that error.  MultiReadCloser closes all of
// the input readers when it is closed. It also closes readers when they finish.
func MultiReadCloser(readers ...io.ReadCloser) io.ReadCloser {
	r := make([]io.ReadCloser, len(readers))
	copy(r, readers)
	return &multiReadCloser{r}
}

// Multicast enables the Ogre Magi to rapidly cast his spells, giving them
// greater potency.
func Multicast(r *http.Request, etcdKey string) (io.ReadCloser, error) {
	_endpoints, err := etcache.Get(etcdKey, false, true)
	if err != nil {
		log.Fatal(err)
	}
	endpoints := _endpoints.Node.Nodes

	var readers []io.ReadCloser
	for i, node := range endpoints {
		httpClient := &http.Client{}
		// `Do` will complain if r.RequestURI is set so we unset it
		r.RequestURI = ""
		r.URL.Scheme = "http"
		r.URL.Host = node.Value
		resp, err := httpClient.Do(r)
		if err != nil {
			return nil, err
		}
		if resp.StatusCode != 200 {
			return nil, fmt.Errorf("Failed request (%s) to %s.", resp.Status, r.URL.String())
		}
		// paths with * are multigets so we want all of the responses
		if i == 0 || strings.Contains(r.URL.Path, "*") {
			readers = append(readers, resp.Body)
		}
	}

	return MultiReadCloser(readers...), nil
}

func MulticastHttp(w http.ResponseWriter, r *http.Request, etcdKey string) {
	reader, err := Multicast(r, etcdKey)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
	defer reader.Close()

	_, err = io.Copy(w, reader)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
}
<commit_msg>Maybe fixes double close problem.<commit_after>package route

import (
	"bytes"
	"fmt"
	"hash/adler32"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"path"
	"strings"

	"github.com/pachyderm-io/pfs/lib/etcache"
)

func HashResource(resource string) uint64 {
	return uint64(adler32.Checksum([]byte(resource)))
}

func hashRequest(r *http.Request) uint64 {
	return HashResource(r.URL.Path)
}

func Route(r *http.Request, etcdKey string, modulos uint64) (io.ReadCloser, error) {
	bucket := hashRequest(r) % modulos
	shard := fmt.Sprint(bucket, "-", fmt.Sprint(modulos))

	_master, err := etcache.Get(path.Join(etcdKey, shard), false, false)
	if err != nil {
		return nil, err
	}
	master := _master.Node.Value

	httpClient := &http.Client{}
	// `Do` will complain if r.RequestURI is set so we unset it
	r.RequestURI = ""
	r.URL.Scheme = "http"
	r.URL.Host = master
	resp, err := httpClient.Do(r)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("Failed request (%s) to %s.", resp.Status, r.URL.String())
	}
	return resp.Body, nil
}

func RouteHttp(w http.ResponseWriter, r *http.Request, etcdKey string, modulos uint64) {
	reader, err := Route(r, etcdKey, modulos)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
	_, err = io.Copy(w, reader)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
}

type multiReadCloser struct {
	readers []io.ReadCloser
}

func (mr *multiReadCloser) Read(p []byte) (n int, err error) {
	for len(mr.readers) > 0 {
		n, err = mr.readers[0].Read(p)
		if n > 0 || err != io.EOF {
			if err == io.EOF {
				// Don't return EOF yet. There may be more bytes
				// in the remaining readers.
				err = nil
			}
			return
		}
		err = mr.readers[0].Close()
		if err != nil {
			return
		}
		mr.readers = mr.readers[1:]
	}
	return 0, io.EOF
}

func (mr *multiReadCloser) Close() error {
	for len(mr.readers) > 0 {
		err := mr.readers[0].Close()
		if err != nil {
			return err
		}
		mr.readers = mr.readers[1:]
	}
	return nil
}

// MultiReadCloser returns a ReaderCloser that's the logical concatenation of
// the provided input readers. They're read sequentially.  Once all inputs have
// returned EOF, Read will return EOF. If any of the readers return a non-nil,
// non-EOF error, Read will return that error.  MultiReadCloser closes all of
// the input readers when it is closed. It also closes readers when they finish.
func MultiReadCloser(readers ...io.ReadCloser) io.ReadCloser {
	r := make([]io.ReadCloser, len(readers))
	copy(r, readers)
	return &multiReadCloser{r}
}

// Multicast enables the Ogre Magi to rapidly cast his spells, giving them
// greater potency.
func Multicast(r *http.Request, etcdKey string) (io.ReadCloser, error) {
	log.Print(r)
	_endpoints, err := etcache.Get(etcdKey, false, true)
	if err != nil {
		return nil, err
	}
	endpoints := _endpoints.Node.Nodes

	var body []byte
	if r.ContentLength != 0 {
		body, err = ioutil.ReadAll(r.Body)
		if err != nil {
			return nil, err
		}
	}

	var readers []io.ReadCloser
	for i, node := range endpoints {
		httpClient := &http.Client{}
		// `Do` will complain if r.RequestURI is set so we unset it
		r.RequestURI = ""
		r.URL.Scheme = "http"
		r.URL.Host = node.Value

		if r.ContentLength != 0 {
			r.Body = ioutil.NopCloser(bytes.NewReader(body))
		}

		resp, err := httpClient.Do(r)
		if err != nil {
			return nil, err
		}
		if resp.StatusCode != 200 {
			return nil, fmt.Errorf("Failed request (%s) to %s.", resp.Status, r.URL.String())
		}
		// paths with * are multigets so we want all of the responses
		if i == 0 || strings.Contains(r.URL.Path, "*") {
			readers = append(readers, resp.Body)
		}
	}

	return MultiReadCloser(readers...), nil
}

func MulticastHttp(w http.ResponseWriter, r *http.Request, etcdKey string) {
	reader, err := Multicast(r, etcdKey)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
	defer reader.Close()

	_, err = io.Copy(w, reader)
	if err != nil {
		http.Error(w, err.Error(), 500)
		log.Print(err)
		return
	}
}
<|endoftext|>


<commit_before>package srpc

import (
	"bufio"
	"crypto/tls"
	"errors"
	"io"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	libnet "github.com/Symantec/Dominator/lib/net"
	"github.com/Symantec/tricorder/go/tricorder"
	"github.com/Symantec/tricorder/go/tricorder/units"
)

var (
	clientMetricsDir          *tricorder.DirectorySpec
	clientMetricsMutex        sync.Mutex
	numInUseClientConnections uint64
	numOpenClientConnections  uint64
)

func init() {
	registerClientMetrics()
}

func registerClientMetrics() {
	var err error
	clientMetricsDir, err = tricorder.RegisterDirectory("srpc/client")
	if err != nil {
		panic(err)
	}
	err = clientMetricsDir.RegisterMetric("num-in-use-connections",
		&numInUseClientConnections, units.None,
		"number of connections in use")
	if err != nil {
		panic(err)
	}
	err = clientMetricsDir.RegisterMetric("num-open-connections",
		&numOpenClientConnections, units.None, "number of open connections")
	if err != nil {
		panic(err)
	}
}

func dial(network, address string, dialer Dialer) (net.Conn, error) {
	hostPort := strings.SplitN(address, ":", 2)
	address = strings.SplitN(hostPort[0], "*", 2)[0] + ":" + hostPort[1]
	conn, err := dialer.Dial(network, address)
	if err != nil {
		if strings.Contains(err.Error(), ErrorConnectionRefused.Error()) {
			return nil, ErrorConnectionRefused
		}
		if strings.Contains(err.Error(), ErrorNoRouteToHost.Error()) {
			return nil, ErrorNoRouteToHost
		}
		return nil, err
	}
	if tcpConn, ok := conn.(libnet.TCPConn); ok {
		if err := tcpConn.SetKeepAlive(true); err != nil {
			conn.Close()
			return nil, err
		}
		if err := tcpConn.SetKeepAlivePeriod(time.Minute * 5); err != nil {
			conn.Close()
			return nil, err
		}
	}
	return conn, nil
}

func dialHTTP(network, address string, tlsConfig *tls.Config,
	dialer Dialer) (*Client, error) {
	return dialHTTPWithCoder(network, address, tlsConfig, dialer, &gobCoder{})
}

func dialHTTPWithCoder(network, address string, tlsConfig *tls.Config,
	dialer Dialer, makeCoder coderMaker) (*Client, error) {
	unsecuredConn, err := dial(network, address, dialer)
	if err != nil {
		return nil, err
	}
	path := rpcPath
	if tlsConfig != nil {
		path = tlsRpcPath
	}
	if err := doHTTPConnect(unsecuredConn, path); err != nil {
		unsecuredConn.Close()
		if strings.Contains(err.Error(), "malformed HTTP response") {
			// The server may do TLS on all connections: try that.
			if tlsConfig == nil {
				return nil, err
			}
			unsecuredConn, err := dial(network, address, dialer)
			if err != nil {
				return nil, err
			}
			tlsConn := tls.Client(unsecuredConn, tlsConfig)
			if err := tlsConn.Handshake(); err != nil {
				unsecuredConn.Close()
				if strings.Contains(err.Error(), ErrorBadCertificate.Error()) {
					return nil, ErrorBadCertificate
				}
				return nil, err
			}
			if err := doHTTPConnect(tlsConn, tlsRpcPath); err != nil {
				tlsConn.Close()
				return nil, err
			}
			return newClient(unsecuredConn, tlsConn, true, makeCoder), nil
		} else if err == ErrorNoSrpcEndpoint &&
			tlsConfig != nil &&
			tlsConfig.InsecureSkipVerify {
			// Fall back to insecure connection.
			unsecuredConn, err := dial(network, address, dialer)
			if err != nil {
				return nil, err
			}
			if err := doHTTPConnect(unsecuredConn, rpcPath); err != nil {
				unsecuredConn.Close()
				return nil, err
			}
			return newClient(unsecuredConn, unsecuredConn, false, makeCoder),
				nil
		} else {
			return nil, err
		}
	}
	if tlsConfig == nil {
		return newClient(unsecuredConn, unsecuredConn, false, makeCoder), nil
	}
	tlsConn := tls.Client(unsecuredConn, tlsConfig)
	if err := tlsConn.Handshake(); err != nil {
		unsecuredConn.Close()
		if strings.Contains(err.Error(), ErrorBadCertificate.Error()) {
			return nil, ErrorBadCertificate
		}
		return nil, err
	}
	return newClient(unsecuredConn, tlsConn, true, makeCoder), nil
}

func doHTTPConnect(conn net.Conn, path string) error {
	io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
	// Require successful HTTP response before switching to SRPC protocol.
	resp, err := http.ReadResponse(bufio.NewReader(conn),
		&http.Request{Method: "CONNECT"})
	if err != nil {
		return err
	}
	if resp.StatusCode == http.StatusNotFound {
		return ErrorNoSrpcEndpoint
	}
	if resp.StatusCode == http.StatusUnauthorized {
		return ErrorBadCertificate
	}
	if resp.StatusCode == http.StatusMethodNotAllowed {
		return ErrorMissingCertificate
	}
	if resp.StatusCode != http.StatusOK || resp.Status != connectString {
		return errors.New("unexpected HTTP response: " + resp.Status)
	}
	return nil
}

func newClient(rawConn, dataConn net.Conn, isEncrypted bool,
	makeCoder coderMaker) *Client {
	clientMetricsMutex.Lock()
	numOpenClientConnections++
	clientMetricsMutex.Unlock()
	client := &Client{
		bufrw: bufio.NewReadWriter(bufio.NewReader(dataConn),
			bufio.NewWriter(dataConn)),
		conn:        dataConn,
		isEncrypted: isEncrypted,
		makeCoder:   makeCoder,
	}
	if tcpConn, ok := rawConn.(libnet.TCPConn); ok {
		client.tcpConn = tcpConn
	}
	return client
}

func (client *Client) call(serviceMethod string) (*Conn, error) {
	if client.conn == nil {
		panic("cannot call Client after Put()")
	}
	client.callLock.Lock()
	conn, err := client.callWithLock(serviceMethod)
	if err != nil {
		client.callLock.Unlock()
	}
	return conn, err
}

func (client *Client) callWithLock(serviceMethod string) (*Conn, error) {
	_, err := client.bufrw.WriteString(serviceMethod + "\n")
	if err != nil {
		return nil, err
	}
	if err = client.bufrw.Flush(); err != nil {
		return nil, err
	}
	resp, err := client.bufrw.ReadString('\n')
	if err != nil {
		return nil, err
	}
	if resp != "\n" {
		resp := resp[:len(resp)-1]
		if resp == ErrorAccessToMethodDenied.Error() {
			return nil, ErrorAccessToMethodDenied
		}
		return nil, errors.New(resp)
	}
	conn := &Conn{
		Decoder:     client.makeCoder.MakeDecoder(client.bufrw),
		Encoder:     client.makeCoder.MakeEncoder(client.bufrw),
		parent:      client,
		isEncrypted: client.isEncrypted,
		ReadWriter:  client.bufrw,
	}
	return conn, nil
}

func (client *Client) close() error {
	client.bufrw.Flush()
	if client.resource == nil {
		clientMetricsMutex.Lock()
		numOpenClientConnections--
		clientMetricsMutex.Unlock()
		return client.conn.Close()
	}
	client.resource.resource.Release()
	clientMetricsMutex.Lock()
	if client.resource.inUse {
		numInUseClientConnections--
		client.resource.inUse = false
	}
	numOpenClientConnections--
	clientMetricsMutex.Unlock()
	return client.resource.closeError
}

func (client *Client) ping() error {
	conn, err := client.call("")
	if err != nil {
		return err
	}
	conn.Close()
	return nil
}

func (client *Client) requestReply(serviceMethod string, request interface{},
	reply interface{}) error {
	conn, err := client.Call(serviceMethod)
	if err != nil {
		return err
	}
	defer conn.Close()
	return conn.requestReply(request, reply)
}

func (conn *Conn) requestReply(request interface{}, reply interface{}) error {
	if err := conn.Encode(request); err != nil {
		return err
	}
	if err := conn.Flush(); err != nil {
		return err
	}
	str, err := conn.ReadString('\n')
	if err != nil {
		return err
	}
	if str != "\n" {
		return errors.New(str[:len(str)-1])
	}
	return conn.Decode(reply)
}
<commit_msg>Support calling JSON-only SRPC servers in lib/srpc package.<commit_after>package srpc

import (
	"bufio"
	"crypto/tls"
	"errors"
	"io"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	libnet "github.com/Symantec/Dominator/lib/net"
	"github.com/Symantec/tricorder/go/tricorder"
	"github.com/Symantec/tricorder/go/tricorder/units"
)

type endpointType struct {
	coderMaker coderMaker
	path       string
	tls        bool
}

var (
	clientMetricsDir          *tricorder.DirectorySpec
	clientMetricsMutex        sync.Mutex
	numInUseClientConnections uint64
	numOpenClientConnections  uint64
)

func init() {
	registerClientMetrics()
}

func registerClientMetrics() {
	var err error
	clientMetricsDir, err = tricorder.RegisterDirectory("srpc/client")
	if err != nil {
		panic(err)
	}
	err = clientMetricsDir.RegisterMetric("num-in-use-connections",
		&numInUseClientConnections, units.None,
		"number of connections in use")
	if err != nil {
		panic(err)
	}
	err = clientMetricsDir.RegisterMetric("num-open-connections",
		&numOpenClientConnections, units.None, "number of open connections")
	if err != nil {
		panic(err)
	}
}

func dial(network, address string, dialer Dialer) (net.Conn, error) {
	hostPort := strings.SplitN(address, ":", 2)
	address = strings.SplitN(hostPort[0], "*", 2)[0] + ":" + hostPort[1]
	conn, err := dialer.Dial(network, address)
	if err != nil {
		if strings.Contains(err.Error(), ErrorConnectionRefused.Error()) {
			return nil, ErrorConnectionRefused
		}
		if strings.Contains(err.Error(), ErrorNoRouteToHost.Error()) {
			return nil, ErrorNoRouteToHost
		}
		return nil, err
	}
	if tcpConn, ok := conn.(libnet.TCPConn); ok {
		if err := tcpConn.SetKeepAlive(true); err != nil {
			conn.Close()
			return nil, err
		}
		if err := tcpConn.SetKeepAlivePeriod(time.Minute * 5); err != nil {
			conn.Close()
			return nil, err
		}
	}
	return conn, nil
}

func dialHTTP(network, address string, tlsConfig *tls.Config,
	dialer Dialer) (*Client, error) {
	insecureEndpoints := []endpointType{
		{&gobCoder{}, rpcPath, false},
		{&jsonCoder{}, jsonRpcPath, false},
	}
	secureEndpoints := []endpointType{
		{&gobCoder{}, tlsRpcPath, true},
		{&jsonCoder{}, jsonTlsRpcPath, true},
	}
	if tlsConfig == nil {
		return dialHTTPEndpoints(network, address, nil, false, dialer,
			insecureEndpoints)
	} else {
		var endpoints []endpointType
		endpoints = append(endpoints, secureEndpoints...)
		if tlsConfig.InsecureSkipVerify { // Don't have to trust server.
			endpoints = append(endpoints, insecureEndpoints...)
		}
		client, err := dialHTTPEndpoints(network, address, tlsConfig, false,
			dialer, endpoints)
		if err != nil &&
			strings.Contains(err.Error(), "malformed HTTP response") {
			// The server may do TLS on all connections: try that.
			return dialHTTPEndpoints(network, address, tlsConfig, true, dialer,
				secureEndpoints)
		}
		return client, err
	}
}

func dialHTTPEndpoint(network, address string, tlsConfig *tls.Config,
	fullTLS bool, dialer Dialer, endpoint endpointType) (*Client, error) {
	unsecuredConn, err := dial(network, address, dialer)
	if err != nil {
		return nil, err
	}
	dataConn := unsecuredConn
	doClose := true
	defer func() {
		if doClose {
			dataConn.Close()
		}
	}()
	if fullTLS {
		tlsConn := tls.Client(unsecuredConn, tlsConfig)
		if err := tlsConn.Handshake(); err != nil {
			if strings.Contains(err.Error(), ErrorBadCertificate.Error()) {
				return nil, ErrorBadCertificate
			}
			return nil, err
		}
		dataConn = tlsConn
	}
	if err := doHTTPConnect(dataConn, endpoint.path); err != nil {
		return nil, err
	}
	if endpoint.tls && !fullTLS {
		tlsConn := tls.Client(unsecuredConn, tlsConfig)
		if err := tlsConn.Handshake(); err != nil {
			if strings.Contains(err.Error(), ErrorBadCertificate.Error()) {
				return nil, ErrorBadCertificate
			}
			return nil, err
		}
		dataConn = tlsConn
	}
	doClose = false
	return newClient(unsecuredConn, dataConn, endpoint.tls,
		endpoint.coderMaker), nil
}

func dialHTTPEndpoints(network, address string, tlsConfig *tls.Config,
	fullTLS bool, dialer Dialer, endpoints []endpointType) (*Client, error) {
	for _, endpoint := range endpoints {
		client, err := dialHTTPEndpoint(network, address, tlsConfig, fullTLS,
			dialer, endpoint)
		if err == nil {
			return client, nil
		}
		if err != ErrorNoSrpcEndpoint {
			return nil, err
		}
	}
	return nil, ErrorNoSrpcEndpoint
}

func doHTTPConnect(conn net.Conn, path string) error {
	io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
	// Require successful HTTP response before switching to SRPC protocol.
	resp, err := http.ReadResponse(bufio.NewReader(conn),
		&http.Request{Method: "CONNECT"})
	if err != nil {
		return err
	}
	if resp.StatusCode == http.StatusNotFound {
		return ErrorNoSrpcEndpoint
	}
	if resp.StatusCode == http.StatusUnauthorized {
		return ErrorBadCertificate
	}
	if resp.StatusCode == http.StatusMethodNotAllowed {
		return ErrorMissingCertificate
	}
	if resp.StatusCode != http.StatusOK || resp.Status != connectString {
		return errors.New("unexpected HTTP response: " + resp.Status)
	}
	return nil
}

func newClient(rawConn, dataConn net.Conn, isEncrypted bool,
	makeCoder coderMaker) *Client {
	clientMetricsMutex.Lock()
	numOpenClientConnections++
	clientMetricsMutex.Unlock()
	client := &Client{
		bufrw: bufio.NewReadWriter(bufio.NewReader(dataConn),
			bufio.NewWriter(dataConn)),
		conn:        dataConn,
		isEncrypted: isEncrypted,
		makeCoder:   makeCoder,
	}
	if tcpConn, ok := rawConn.(libnet.TCPConn); ok {
		client.tcpConn = tcpConn
	}
	return client
}

func (client *Client) call(serviceMethod string) (*Conn, error) {
	if client.conn == nil {
		panic("cannot call Client after Put()")
	}
	client.callLock.Lock()
	conn, err := client.callWithLock(serviceMethod)
	if err != nil {
		client.callLock.Unlock()
	}
	return conn, err
}

func (client *Client) callWithLock(serviceMethod string) (*Conn, error) {
	_, err := client.bufrw.WriteString(serviceMethod + "\n")
	if err != nil {
		return nil, err
	}
	if err = client.bufrw.Flush(); err != nil {
		return nil, err
	}
	resp, err := client.bufrw.ReadString('\n')
	if err != nil {
		return nil, err
	}
	if resp != "\n" {
		resp := resp[:len(resp)-1]
		if resp == ErrorAccessToMethodDenied.Error() {
			return nil, ErrorAccessToMethodDenied
		}
		return nil, errors.New(resp)
	}
	conn := &Conn{
		Decoder:     client.makeCoder.MakeDecoder(client.bufrw),
		Encoder:     client.makeCoder.MakeEncoder(client.bufrw),
		parent:      client,
		isEncrypted: client.isEncrypted,
		ReadWriter:  client.bufrw,
	}
	return conn, nil
}

func (client *Client) close() error {
	client.bufrw.Flush()
	if client.resource == nil {
		clientMetricsMutex.Lock()
		numOpenClientConnections--
		clientMetricsMutex.Unlock()
		return client.conn.Close()
	}
	client.resource.resource.Release()
	clientMetricsMutex.Lock()
	if client.resource.inUse {
		numInUseClientConnections--
		client.resource.inUse = false
	}
	numOpenClientConnections--
	clientMetricsMutex.Unlock()
	return client.resource.closeError
}

func (client *Client) ping() error {
	conn, err := client.call("")
	if err != nil {
		return err
	}
	conn.Close()
	return nil
}

func (client *Client) requestReply(serviceMethod string, request interface{},
	reply interface{}) error {
	conn, err := client.Call(serviceMethod)
	if err != nil {
		return err
	}
	defer conn.Close()
	return conn.requestReply(request, reply)
}

func (conn *Conn) requestReply(request interface{}, reply interface{}) error {
	if err := conn.Encode(request); err != nil {
		return err
	}
	if err := conn.Flush(); err != nil {
		return err
	}
	str, err := conn.ReadString('\n')
	if err != nil {
		return err
	}
	if str != "\n" {
		return errors.New(str[:len(str)-1])
	}
	return conn.Decode(reply)
}
<|endoftext|>


<commit_before>package libstns

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os/exec"
	"path"
	"regexp"
	"strings"
	"time"

	"golang.org/x/net/context"

	stns_settings "github.com/STNS/STNS/settings"
	"github.com/STNS/STNS/stns"
	"github.com/STNS/libnss_stns/cache"
	"github.com/STNS/libnss_stns/settings"
)

type Request struct {
	ApiPath string
	Config  *Config
}

func NewRequest(config *Config, paths ...string) (*Request, error) {
	r := Request{
		ApiPath: path.Clean(strings.Join(paths, "/")),
		Config:  config,
	}
	return &r, nil
}

// only use wrapper command
func (r *Request) GetRawData() ([]byte, error) {
	if len(r.Config.ApiEndPoint) == 0 {
		return nil, errors.New("endpoint not defined")
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	rch := make(chan []byte, len(r.Config.ApiEndPoint))
	ech := make(chan error, len(r.Config.ApiEndPoint))
	for _, e := range r.Config.ApiEndPoint {
		go func(endPoint string) {
			if cache.IsLockEndPoint(endPoint) {
				ech <- fmt.Errorf("endpoint %s is locked", endPoint)
				return
			}

			u := strings.TrimRight(endPoint, "/") + "/" + strings.TrimLeft(r.ApiPath, "/")
			req, err := http.NewRequest("GET", u, nil)

			for k, v := range r.Config.RequestHeader {
				req.Header.Add(k, v)
			}

			if err != nil {
				ech <- err
				return
			}

			if r.Config.User != "" && r.Config.Password != "" {
				req.SetBasicAuth(r.Config.User, r.Config.Password)
			}

			r.httpDo(
				ctx,
				req,
				func(res *http.Response, err error) {
					if err != nil {
						if _, ok := err.(*url.Error); ok {
							cache.LockEndPoint(endPoint)
						}
						ech <- err
						return
					}
					defer res.Body.Close()
					body, err := ioutil.ReadAll(res.Body)
					switch res.StatusCode {
					case http.StatusOK, http.StatusNotFound:
						reg := regexp.MustCompile(`/v2[/]?$`)
						switch {
						// version1
						case !reg.MatchString(endPoint):
							buffer, err := r.migrateV2Format(body)
							if err != nil {
								ech <- err
								return
							}
							rch <- buffer
							return
						default:
							rch <- body
							return
						}
					case http.StatusUnauthorized:
						ech <- fmt.Errorf("authenticate error: %s", u)
						return
					default:
						ech <- fmt.Errorf("error: %s", u)
						return
					}
				},
			)
		}(e)
	}

	var cnt int
	for {
		select {
		case r := <-rch:
			return r, nil
		case e := <-ech:
			cnt++
			if cnt == len(r.Config.ApiEndPoint) {
				return nil, e
			}
		}
	}
}

func (r *Request) httpDo(
	ctx context.Context,
	req *http.Request,
	f func(*http.Response, error),
) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: !r.Config.SslVerify},
		Dial: (&net.Dialer{
			Timeout:   time.Duration(r.Config.RequestTimeOut) * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
	}
	tr.Proxy = http.ProxyFromEnvironment
	if r.Config.HttpProxy != "" {
		proxyUrl, err := url.Parse(r.Config.HttpProxy)
		if err == nil {
			tr.Proxy = http.ProxyURL(proxyUrl)
		}
	}

	client := &http.Client{Transport: tr}

	go func() { f(client.Do(req)) }()
	select {
	case <-ctx.Done():
		tr.CancelRequest(req)
		return
	}
}

func (r *Request) migrateV2Format(body []byte) ([]byte, error) {
	var attr stns.Attributes
	err := json.Unmarshal(body, &attr)

	if err != nil {
		return nil, err
	}

	if attr == nil {
		return nil, errors.New(settings.V2_FORMAT_ERROR)
	}

	mig := stns.ResponseFormat{
		&stns.MetaData{
			1.0,
			false,
			0,
			"sha256",
			stns_settings.SUCCESS,
			0,
		},
		&attr,
	}

	j, err := json.Marshal(mig)
	if err != nil {
		return nil, err
	}

	return j, nil
}

func (r *Request) GetByWrapperCmd() (stns.ResponseFormat, error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	cmd := exec.Command(r.Config.WrapperCommand, r.ApiPath)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()

	if err != nil {
		return stns.ResponseFormat{}, err
	}

	if len(stderr.Bytes()) > 0 {
		return stns.ResponseFormat{}, fmt.Errorf("command error:%s", stderr.String())
	}

	var res stns.ResponseFormat
	err = json.Unmarshal(stdout.Bytes(), &res)
	if err != nil {
		return stns.ResponseFormat{}, err
	}
	return res, nil
}
<commit_msg>fix header<commit_after>package libstns

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os/exec"
	"path"
	"regexp"
	"strings"
	"time"

	"golang.org/x/net/context"

	stns_settings "github.com/STNS/STNS/settings"
	"github.com/STNS/STNS/stns"
	"github.com/STNS/libnss_stns/cache"
	"github.com/STNS/libnss_stns/settings"
)

type Request struct {
	ApiPath string
	Config  *Config
}

func NewRequest(config *Config, paths ...string) (*Request, error) {
	r := Request{
		ApiPath: path.Clean(strings.Join(paths, "/")),
		Config:  config,
	}
	return &r, nil
}

// only use wrapper command
func (r *Request) GetRawData() ([]byte, error) {
	if len(r.Config.ApiEndPoint) == 0 {
		return nil, errors.New("endpoint not defined")
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	rch := make(chan []byte, len(r.Config.ApiEndPoint))
	ech := make(chan error, len(r.Config.ApiEndPoint))
	for _, e := range r.Config.ApiEndPoint {
		go func(endPoint string) {
			if cache.IsLockEndPoint(endPoint) {
				ech <- fmt.Errorf("endpoint %s is locked", endPoint)
				return
			}

			u := strings.TrimRight(endPoint, "/") + "/" + strings.TrimLeft(r.ApiPath, "/")
			req, err := http.NewRequest("GET", u, nil)

			for k, v := range r.Config.RequestHeader {
				req.Header.Add(k, v)
			}

			if err != nil {
				ech <- err
				return
			}

			if r.Config.User != "" && r.Config.Password != "" {
				req.SetBasicAuth(r.Config.User, r.Config.Password)
			}

			r.httpDo(
				ctx,
				req,
				func(res *http.Response, err error) {
					if err != nil {
						if _, ok := err.(*url.Error); ok {
							cache.LockEndPoint(endPoint)
						}
						ech <- err
						return
					}
					defer res.Body.Close()
					body, err := ioutil.ReadAll(res.Body)
					switch res.StatusCode {
					case http.StatusOK, http.StatusNotFound:
						reg := regexp.MustCompile(`/v2[/]?$`)
						switch {
						// version1
						case !reg.MatchString(endPoint):
							buffer, err := r.migrateV2Format(body)
							if err != nil {
								ech <- err
								return
							}
							rch <- buffer
							return
						default:
							rch <- body
							return
						}
					case http.StatusUnauthorized:
						ech <- fmt.Errorf("authenticate error: %s", u)
						return
					default:
						ech <- fmt.Errorf("error: %s", u)
						return
					}
				},
			)
		}(e)
	}

	var cnt int
	for {
		select {
		case r := <-rch:
			return r, nil
		case e := <-ech:
			cnt++
			if cnt == len(r.Config.ApiEndPoint) {
				return nil, e
			}
		}
	}
}

func (r *Request) httpDo(
	ctx context.Context,
	req *http.Request,
	f func(*http.Response, error),
) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: !r.Config.SslVerify},
		Dial: (&net.Dialer{
			Timeout:   time.Duration(r.Config.RequestTimeOut) * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
	}
	tr.Proxy = http.ProxyFromEnvironment
	if r.Config.HttpProxy != "" {
		proxyUrl, err := url.Parse(r.Config.HttpProxy)
		if err == nil {
			tr.Proxy = http.ProxyURL(proxyUrl)
		}
	}

	client := &http.Client{Transport: tr}

	go func() { f(client.Do(req)) }()
	select {
	case <-ctx.Done():
		tr.CancelRequest(req)
		return
	}
}

func (r *Request) migrateV2Format(body []byte) ([]byte, error) {
	var attr stns.Attributes
	err := json.Unmarshal(body, &attr)

	if err != nil {
		return nil, err
	}

	if attr == nil {
		return nil, errors.New(settings.V2_FORMAT_ERROR)
	}

	mig := stns.ResponseFormat{
		&stns.MetaData{
			1.0,
			stns_settings.SUCCESS,
			0,
		},
		&attr,
	}

	j, err := json.Marshal(mig)
	if err != nil {
		return nil, err
	}

	return j, nil
}

func (r *Request) GetByWrapperCmd() (stns.ResponseFormat, error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	cmd := exec.Command(r.Config.WrapperCommand, r.ApiPath)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()

	if err != nil {
		return stns.ResponseFormat{}, err
	}

	if len(stderr.Bytes()) > 0 {
		return stns.ResponseFormat{}, fmt.Errorf("command error:%s", stderr.String())
	}

	var res stns.ResponseFormat
	err = json.Unmarshal(stdout.Bytes(), &res)
	if err != nil {
		return stns.ResponseFormat{}, err
	}
	return res, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Pikkpoiss
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"../lib/twodee"
	"io/ioutil"
	"time"
)

type GameLayer struct {
	gameRenderer         *GameRenderer
	spriteSheet          *twodee.Spritesheet
	spriteTexture        *twodee.Texture
	app                  *Application
	level                *Level
	uiState              UiState
	state                *State
	playerLostObserverId int
	playerWonObserverId  int
}

func NewGameLayer(state *State, app *Application) (layer *GameLayer, err error) {
	layer = &GameLayer{
		app:   app,
		state: state,
	}
	layer.playerLostObserverId = app.GameEventHandler.AddObserver(PlayerLost, layer.PlayerLost)
	layer.playerWonObserverId = app.GameEventHandler.AddObserver(PlayerWon, layer.PlayerWon)
	err = layer.Reset()
	return
}

func (l *GameLayer) Delete() {
	if l.playerLostObserverId != 0 {
		l.app.GameEventHandler.RemoveObserver(
			PlayerLost, l.playerLostObserverId)
		l.playerLostObserverId = 0
	}
	if l.playerWonObserverId != 0 {
		l.app.GameEventHandler.RemoveObserver(
			PlayerLost, l.playerLostObserverId)
		l.playerLostObserverId = 0
	}
	l.gameRenderer.Delete()
}

func (l *GameLayer) Render() {
	switch l.state.SplashState {
	case SplashWin:
		fallthrough
	case SplashLose:
		return
	}
	l.spriteTexture.Bind()
	l.gameRenderer.Draw(l.level)
	l.spriteTexture.Unbind()
}

func (l *GameLayer) SetUiState(state UiState) {
	l.uiState.Unregister(l.level)
	l.uiState = state
	l.uiState.Register(l.level)
}

func (l *GameLayer) UnsetHighlights() {
	l.level.UnsetHighlights()
}

func (l *GameLayer) HandleEvent(evt twodee.Event) bool {
	if newState := l.uiState.HandleEvent(l.level, evt); newState != nil {
		l.SetUiState(newState)
	}

	switch event := evt.(type) {
	case *twodee.KeyEvent:
		if event.Type == twodee.Release {
			break
		}
		switch event.Code {
		case twodee.KeyM:
			if twodee.MusicIsPaused() {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(ResumeMusic))
			} else {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PauseMusic))
			}
		}
	}
	return true
}

func (l *GameLayer) Reset() (err error) {
	if err = l.loadSpritesheet(); err != nil {
		return
	}
	if err = l.LoadLevel(); err != nil {
		return
	}
	l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PlayBackgroundMusic))
	return
}

func (l *GameLayer) LoadLevel() (err error) {
	if l.level, err = NewLevel(l.state, l.spriteSheet, l.app.GameEventHandler); err != nil {
		return
	}
	l.uiState = NewNormalUiState()
	l.uiState.Register(l.level)
	if l.gameRenderer != nil {
		l.gameRenderer.Delete()
	}
	if l.gameRenderer, err = NewGameRenderer(l.level, l.spriteSheet); err != nil {
		return
	}
	return
}

func (l *GameLayer) PlayerLost(e twodee.GETyper) {
	l.state.Reset()
	l.state.SplashState = SplashLose
	l.LoadLevel()
}

func (l *GameLayer) PlayerWon(e twodee.GETyper) {
	fmt.Println("Player won, horray!")
}

func (l *GameLayer) Update(elapsed time.Duration) {
	if l.state.SplashState != SplashDisabled {
		return
	}
	l.level.Update(elapsed)
}

func (l *GameLayer) loadSpritesheet() (err error) {
	var (
		data []byte
	)
	if data, err = ioutil.ReadFile("resources/spritesheet.json"); err != nil {
		return
	}
	if l.spriteSheet, err = twodee.ParseTexturePackerJSONArrayString(
		string(data),
		PxPerUnit,
	); err != nil {
		return
	}
	if l.spriteTexture, err = twodee.LoadTexture(
		"resources/"+l.spriteSheet.TexturePath,
		twodee.Nearest,
	); err != nil {
		return
	}
	return
}
<commit_msg>Fix issue<commit_after>// Copyright 2015 Pikkpoiss
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"../lib/twodee"
	"fmt"
	"io/ioutil"
	"time"
)

type GameLayer struct {
	gameRenderer         *GameRenderer
	spriteSheet          *twodee.Spritesheet
	spriteTexture        *twodee.Texture
	app                  *Application
	level                *Level
	uiState              UiState
	state                *State
	playerLostObserverId int
	playerWonObserverId  int
}

func NewGameLayer(state *State, app *Application) (layer *GameLayer, err error) {
	layer = &GameLayer{
		app:   app,
		state: state,
	}
	layer.playerLostObserverId = app.GameEventHandler.AddObserver(PlayerLost, layer.PlayerLost)
	layer.playerWonObserverId = app.GameEventHandler.AddObserver(PlayerWon, layer.PlayerWon)
	err = layer.Reset()
	return
}

func (l *GameLayer) Delete() {
	if l.playerLostObserverId != 0 {
		l.app.GameEventHandler.RemoveObserver(
			PlayerLost, l.playerLostObserverId)
		l.playerLostObserverId = 0
	}
	if l.playerWonObserverId != 0 {
		l.app.GameEventHandler.RemoveObserver(
			PlayerLost, l.playerLostObserverId)
		l.playerLostObserverId = 0
	}
	l.gameRenderer.Delete()
}

func (l *GameLayer) Render() {
	switch l.state.SplashState {
	case SplashWin:
		fallthrough
	case SplashLose:
		return
	}
	l.spriteTexture.Bind()
	l.gameRenderer.Draw(l.level)
	l.spriteTexture.Unbind()
}

func (l *GameLayer) SetUiState(state UiState) {
	l.uiState.Unregister(l.level)
	l.uiState = state
	l.uiState.Register(l.level)
}

func (l *GameLayer) UnsetHighlights() {
	l.level.UnsetHighlights()
}

func (l *GameLayer) HandleEvent(evt twodee.Event) bool {
	if newState := l.uiState.HandleEvent(l.level, evt); newState != nil {
		l.SetUiState(newState)
	}

	switch event := evt.(type) {
	case *twodee.KeyEvent:
		if event.Type == twodee.Release {
			break
		}
		switch event.Code {
		case twodee.KeyM:
			if twodee.MusicIsPaused() {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(ResumeMusic))
			} else {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PauseMusic))
			}
		}
	}
	return true
}

func (l *GameLayer) Reset() (err error) {
	if err = l.loadSpritesheet(); err != nil {
		return
	}
	if err = l.LoadLevel(); err != nil {
		return
	}
	l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PlayBackgroundMusic))
	return
}

func (l *GameLayer) LoadLevel() (err error) {
	if l.level, err = NewLevel(l.state, l.spriteSheet, l.app.GameEventHandler); err != nil {
		return
	}
	l.uiState = NewNormalUiState()
	l.uiState.Register(l.level)
	if l.gameRenderer != nil {
		l.gameRenderer.Delete()
	}
	if l.gameRenderer, err = NewGameRenderer(l.level, l.spriteSheet); err != nil {
		return
	}
	return
}

func (l *GameLayer) PlayerLost(e twodee.GETyper) {
	l.state.Reset()
	l.state.SplashState = SplashLose
	l.LoadLevel()
}

func (l *GameLayer) PlayerWon(e twodee.GETyper) {
	fmt.Println("Player won, horray!")
}

func (l *GameLayer) Update(elapsed time.Duration) {
	if l.state.SplashState != SplashDisabled {
		return
	}
	l.level.Update(elapsed)
}

func (l *GameLayer) loadSpritesheet() (err error) {
	var (
		data []byte
	)
	if data, err = ioutil.ReadFile("resources/spritesheet.json"); err != nil {
		return
	}
	if l.spriteSheet, err = twodee.ParseTexturePackerJSONArrayString(
		string(data),
		PxPerUnit,
	); err != nil {
		return
	}
	if l.spriteTexture, err = twodee.LoadTexture(
		"resources/"+l.spriteSheet.TexturePath,
		twodee.Nearest,
	); err != nil {
		return
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"time"

	"github.com/urfave/cli"
)

// Putting this in the global scope let's the other source
// files fill it in with their init() functions.

var (
	Commands   []cli.Command
	StartTime  int64
	StripColor bool
	ShowDebug  bool
)

// Starting things rolling down the hill
func main() {
	// start our outputter thread
	go Outputter()

	// Define our app container
	app := cli.NewApp()

	// And give all the metadata a nice sound setting.
	app.Name = "flustro"
	app.Usage = "A whisper file toolkit."
	app.Version = "0.1.0"
	app.Compiled = time.Now()
	app.Authors = []cli.Author{
		cli.Author{
			Name:  "Mike 'Fuzzy' Partin",
			Email: "fuzzy@fumanchu.org",
		},
	}
	app.Copyright = "(c) 2016 Mike 'Fuzzy' Partin"
	app.Commands = Commands
	app.Flags = []cli.Flag{
		cli.BoolFlag{Name: "D", Usage: "Show debugging messages", Destination: &ShowDebug},
		cli.BoolFlag{Name: "C", Usage: "Disable colors in output", Destination: &StripColor},
	}

	// Now let's do things
	StartTime = time.Now().Unix()
	app.Run(os.Args)
	time.Sleep(500 * time.Millisecond)
}
<commit_msg>argument convention respect for args.go<commit_after>package main

import (
	"os"
	"time"

	"github.com/urfave/cli"
)

// Putting this in the global scope let's the other source
// files fill it in with their init() functions.

var (
	Commands   []cli.Command
	StartTime  int64
	StripColor bool
	ShowDebug  bool
)

// Starting things rolling down the hill
func main() {
	// start our outputter thread
	go Outputter()

	// Define our app container
	app := cli.NewApp()

	// And give all the metadata a nice sound setting.
	app.Name = "flustro"
	app.Usage = "A whisper file toolkit."
	app.Version = "0.1.0"
	app.Compiled = time.Now()
	app.Authors = []cli.Author{
		cli.Author{
			Name:  "Mike 'Fuzzy' Partin",
			Email: "fuzzy@fumanchu.org",
		},
	}
	app.Copyright = "(c) 2016 Mike 'Fuzzy' Partin"
	app.Commands = Commands
	app.Flags = []cli.Flag{
		cli.BoolFlag{Name: "d", Usage: "Show debugging messages", Destination: &ShowDebug},
		cli.BoolFlag{Name: "c", Usage: "Disable colors in output", Destination: &StripColor},
	}

	// Now let's do things
	StartTime = time.Now().Unix()
	app.Run(os.Args)
	time.Sleep(500 * time.Millisecond)
}
<|endoftext|>


<commit_before>package s3gof3r

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
)

// Keys for an Amazon Web Services account.
// Used for signing http requests.
type Keys struct {
	AccessKey     string
	SecretKey     string
	SecurityToken string
}

type mdCreds struct {
	Code            string
	LastUpdated     string
	Type            string
	AccessKeyId     string
	SecretAccessKey string
	Token           string
	Expiration      string
}

// Requests the AWS keys from the instance-based metadata on EC2
// Assumes only one IAM role.
func InstanceKeys() (keys Keys, err error) {

	rolePath := "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
	var creds mdCreds

	// request the role name for the instance
	// assumes there is only one
	resp, err := http.Get(rolePath)
	if err != nil {
		return
	}
	defer checkClose(resp.Body, &err)
	if resp.StatusCode != 200 {
		err = newRespError(resp)
		return
	}
	role, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return
	}

	// request the credential metadata for the role
	resp, err = http.Get(rolePath + string(role))
	if err != nil {
		return
	}
	defer checkClose(resp.Body, &err)
	if resp.StatusCode != 200 {
		err = newRespError(resp)
		return
	}
	metadata, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	if err = json.Unmarshal([]byte(metadata), &creds); err != nil {
		return
	}
	keys = Keys{AccessKey: creds.AccessKeyId,
		SecretKey:     creds.SecretAccessKey,
		SecurityToken: creds.Token,
	}

	return
}

// Reads the AWS keys from the environment
func EnvKeys() (keys Keys, err error) {
	keys = Keys{AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
		SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
	}
	if keys.AccessKey == "" || keys.SecretKey == "" {
		err = errors.New("Keys not set in environment: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY")
	}
	return
}
<commit_msg>Reduce instance metadata request timeout to 2 seconds.<commit_after>package s3gof3r

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"time"
)

// Keys for an Amazon Web Services account.
// Used for signing http requests.
type Keys struct {
	AccessKey     string
	SecretKey     string
	SecurityToken string
}

type mdCreds struct {
	Code            string
	LastUpdated     string
	Type            string
	AccessKeyId     string
	SecretAccessKey string
	Token           string
	Expiration      string
}

// Requests the AWS keys from the instance-based metadata on EC2
// Assumes only one IAM role.
func InstanceKeys() (keys Keys, err error) {

	rolePath := "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
	var creds mdCreds

	// request the role name for the instance
	// assumes there is only one
	resp, err := ClientWithTimeout(2 * time.Second).Get(rolePath)
	if err != nil {
		return
	}
	defer checkClose(resp.Body, &err)
	if resp.StatusCode != 200 {
		err = newRespError(resp)
		return
	}
	role, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return
	}

	// request the credential metadata for the role
	resp, err = http.Get(rolePath + string(role))
	if err != nil {
		return
	}
	defer checkClose(resp.Body, &err)
	if resp.StatusCode != 200 {
		err = newRespError(resp)
		return
	}
	metadata, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	if err = json.Unmarshal([]byte(metadata), &creds); err != nil {
		return
	}
	keys = Keys{AccessKey: creds.AccessKeyId,
		SecretKey:     creds.SecretAccessKey,
		SecurityToken: creds.Token,
	}

	return
}

// Reads the AWS keys from the environment
func EnvKeys() (keys Keys, err error) {
	keys = Keys{AccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
		SecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
	}
	if keys.AccessKey == "" || keys.SecretKey == "" {
		err = errors.New("Keys not set in environment: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY")
	}
	return
}
<|endoftext|>


<commit_before>package cuckoo

import (
	"bytes"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"
)

var binP sync.Pool

func init() {
	binP.New = func() interface{} {
		return make([]int, MAX_HASHES)
	}
}

// offset64 is the fnv1a 64-bit offset
var offset64 uint64 = 14695981039346656037

// prime64 is the fnv1a 64-bit prime
var prime64 uint64 = 1099511628211

var intoffs []uint = []uint{0, 8, 16, 24}

// bin returns the nth hash of the given key
func (m *cmap) bin(n int, key keyt) int {
	s := offset64
	for _, c := range key {
		s ^= uint64(c)
		s *= prime64
	}
	for _, i := range intoffs {
		s ^= uint64(n >> i)
		s *= prime64
	}
	return int(s % uint64(len(m.bins)))
}

// kbins returns all hashes of the given key.
// as m.hashes increases, this function will return more hashes.
func (m *cmap) kbins(key keyt) []int {
	nb := uint64(len(m.bins))
	nh := int(m.hashes)
	bins := binP.Get().([]int)
	bins = bins[0:nh]

	// only hash the key once
	s := offset64
	for _, c := range key {
		s ^= uint64(c)
		s *= prime64
	}

	for i := 0; i < nh; i++ {
		// compute key for this i
		s_ := s
		for _, o := range intoffs {
			s_ ^= uint64(i >> o)
			s_ *= prime64
		}
		bins[i] = int(s_ % nb)
	}
	return bins
}

// cbin is a single Cuckoo map bin holding up to ASSOCIATIVITY values.
// each bin has a lock that must be used for *writes*.
// values should never be accessed directly, but rather through v()
type cbin struct {
	vals [ASSOCIATIVITY]unsafe.Pointer
	mx   SpinLock
}

// v returns a pointer to the current key data for a given slot (if any).
// this function may return nil if no key data is set for the given slot.
// this function is safe in the face of concurrent updates, assuming writers
// use setv().
func (b *cbin) v(i int) *cval {
	return (*cval)(atomic.LoadPointer(&b.vals[i]))
}

// vpresent returns true if the given slot contains unexpired key data
func (b *cbin) vpresent(i int, now time.Time) bool {
	v := b.v(i)
	return v != nil && v.present(now)
}

// setv will atomically update the key data for the given slot
func (b *cbin) setv(i int, v *cval) {
	atomic.StorePointer(&b.vals[i], unsafe.Pointer(v))
}

// subin atomically replaces the first free slot in this bin with the given key
// data
func (b *cbin) subin(v *cval, now time.Time) {
	for i := 0; i < ASSOCIATIVITY; i++ {
		if !b.vpresent(i, now) {
			b.setv(i, v)
			return
		}
	}
}

// kill will immediately and atomically invalidate the given slot's key data
func (b *cbin) kill(i int) {
	b.setv(i, nil)
}

// available returns true if this bin has a slot that is currently unoccupied
// or expired
func (b *cbin) available(now time.Time) bool {
	for i := 0; i < ASSOCIATIVITY; i++ {
		if !b.vpresent(i, now) {
			return true
		}
	}
	return false
}

// add will atomically replace the first available slot in this bin with the
// given key data. this function may return an error if there are no free
// slots.
func (b *cbin) add(val *cval, upd Memop, now time.Time) (ret MemopRes) {
	b.mx.Lock()
	defer b.mx.Unlock()

	ret.T = SERVER_ERROR
	if b.available(now) {
		val.val, ret = upd(val.val, false)
		if ret.T == STORED {
			b.subin(val, now)
		}
		return
	}
	return
}

// has returns the slot holding the key data for the given key in this bin.
// if no slot has the relevant key data, -1 is returned.
func (b *cbin) has(key keyt, now time.Time) int {
	for i := 0; i < ASSOCIATIVITY; i++ {
		v := b.v(i)
		if v != nil && v.present(now) && bytes.Equal(v.key, key) {
			return i
		}
	}
	return -1
}
<commit_msg>New bins should have capacity, not length<commit_after>package cuckoo

import (
	"bytes"
	"sync"
	"sync/atomic"
	"time"
	"unsafe"
)

var binP sync.Pool

func init() {
	binP.New = func() interface{} {
		return make([]int, 0, MAX_HASHES)
	}
}

// offset64 is the fnv1a 64-bit offset
var offset64 uint64 = 14695981039346656037

// prime64 is the fnv1a 64-bit prime
var prime64 uint64 = 1099511628211

var intoffs []uint = []uint{0, 8, 16, 24}

// bin returns the nth hash of the given key
func (m *cmap) bin(n int, key keyt) int {
	s := offset64
	for _, c := range key {
		s ^= uint64(c)
		s *= prime64
	}
	for _, i := range intoffs {
		s ^= uint64(n >> i)
		s *= prime64
	}
	return int(s % uint64(len(m.bins)))
}

// kbins returns all hashes of the given key.
// as m.hashes increases, this function will return more hashes.
func (m *cmap) kbins(key keyt) []int {
	nb := uint64(len(m.bins))
	nh := int(m.hashes)
	bins := binP.Get().([]int)
	bins = bins[0:nh]

	// only hash the key once
	s := offset64
	for _, c := range key {
		s ^= uint64(c)
		s *= prime64
	}

	for i := 0; i < nh; i++ {
		// compute key for this i
		s_ := s
		for _, o := range intoffs {
			s_ ^= uint64(i >> o)
			s_ *= prime64
		}
		bins[i] = int(s_ % nb)
	}
	return bins
}

// cbin is a single Cuckoo map bin holding up to ASSOCIATIVITY values.
// each bin has a lock that must be used for *writes*.
// values should never be accessed directly, but rather through v()
type cbin struct {
	vals [ASSOCIATIVITY]unsafe.Pointer
	mx   SpinLock
}

// v returns a pointer to the current key data for a given slot (if any).
// this function may return nil if no key data is set for the given slot.
// this function is safe in the face of concurrent updates, assuming writers
// use setv().
func (b *cbin) v(i int) *cval {
	return (*cval)(atomic.LoadPointer(&b.vals[i]))
}

// vpresent returns true if the given slot contains unexpired key data
func (b *cbin) vpresent(i int, now time.Time) bool {
	v := b.v(i)
	return v != nil && v.present(now)
}

// setv will atomically update the key data for the given slot
func (b *cbin) setv(i int, v *cval) {
	atomic.StorePointer(&b.vals[i], unsafe.Pointer(v))
}

// subin atomically replaces the first free slot in this bin with the given key
// data
func (b *cbin) subin(v *cval, now time.Time) {
	for i := 0; i < ASSOCIATIVITY; i++ {
		if !b.vpresent(i, now) {
			b.setv(i, v)
			return
		}
	}
}

// kill will immediately and atomically invalidate the given slot's key data
func (b *cbin) kill(i int) {
	b.setv(i, nil)
}

// available returns true if this bin has a slot that is currently unoccupied
// or expired
func (b *cbin) available(now time.Time) bool {
	for i := 0; i < ASSOCIATIVITY; i++ {
		if !b.vpresent(i, now) {
			return true
		}
	}
	return false
}

// add will atomically replace the first available slot in this bin with the
// given key data. this function may return an error if there are no free
// slots.
func (b *cbin) add(val *cval, upd Memop, now time.Time) (ret MemopRes) {
	b.mx.Lock()
	defer b.mx.Unlock()

	ret.T = SERVER_ERROR
	if b.available(now) {
		val.val, ret = upd(val.val, false)
		if ret.T == STORED {
			b.subin(val, now)
		}
		return
	}
	return
}

// has returns the slot holding the key data for the given key in this bin.
// if no slot has the relevant key data, -1 is returned.
func (b *cbin) has(key keyt, now time.Time) int {
	for i := 0; i < ASSOCIATIVITY; i++ {
		v := b.v(i)
		if v != nil && v.present(now) && bytes.Equal(v.key, key) {
			return i
		}
	}
	return -1
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package discoverspaces

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/juju/api/discoverspaces"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/worker"
	"github.com/juju/loggo"
	"github.com/juju/names"
	"github.com/juju/utils/set"
	"launchpad.net/tomb"
)

var logger = loggo.GetLogger("juju.discoverspaces")

type discoverspacesWorker struct {
	api  *discoverspaces.API
	tomb tomb.Tomb
}

var invalidChars = regexp.MustCompile("[^0-9a-z-]")
var dashPrefix = regexp.MustCompile("^-*")

func convertSpaceName(name string, existing set.Strings) string {
	// First replace any character that isn't in the set "-", "a-z", "0-9".
	name = invalidChars.ReplaceAllString(name, "")
	// Next get rid of any dashes at the start as that isn't valid.
	name = dashPrefix.ReplaceAllString(name, "")
	// Special case of when the space name was only dashes!
	if name == "" {
		name = "empty"
	}
	// If this name is in use add a numerical suffix.
	if existing.Contains(name) {
		counter := 2
		for existing.Contains(name + fmt.Sprintf("-%d", counter)) {
			counter += 1
		}
		name = name + fmt.Sprintf("-%d", counter)
	}
	return name
}

// NewWorker returns a worker
func NewWorker(api *discoverspaces.API) worker.Worker {
	dw := &discoverspacesWorker{
		api: api,
	}
	go func() {
		defer dw.tomb.Done()
		dw.tomb.Kill(dw.loop())
	}()
	return dw
}

func (dw *discoverspacesWorker) Kill() {
	dw.tomb.Kill(nil)
}

func (dw *discoverspacesWorker) Wait() error {
	return dw.tomb.Wait()
}

func (dw *discoverspacesWorker) loop() (err error) {
	envCfg, err := dw.api.EnvironConfig()
	if err != nil {
		return err
	}
	environ, err := environs.New(envCfg)
	if err != nil {
		return err
	}
	networkingEnviron, ok := environs.SupportsNetworking(environ)

	if ok {
		// TODO: (mfoord) API should be switched off until this is
		// completed.
		err = dw.handleSubnets(networkingEnviron)
		if err != nil {
			return errors.Trace(err)
		}
	}

	// TODO(mfoord): we'll have a watcher here checking if we need to
	// update the spaces/subnets definition.
	dying := dw.tomb.Dying()
	for {
		select {
		case <-dying:
			return nil
		}
	}
	return nil
}

func (dw *discoverspacesWorker) handleSubnets(env environs.NetworkingEnviron) error {
	ok, err := env.SupportsSpaceDiscovery()
	if err != nil {
		return errors.Trace(err)
	}
	if !ok {
		// Nothing to do.
		return nil
	}
	providerSpaces, err := env.Spaces()
	if err != nil {
		return errors.Trace(err)
	}
	listSpacesResult, err := dw.api.ListSpaces()
	if err != nil {
		return errors.Trace(err)
	}

	stateSubnets, err := dw.api.ListSubnets(params.SubnetsFilters{})
	if err != nil {
		return errors.Trace(err)
	}
	stateSubnetIds := make(set.Strings)
	for _, subnet := range stateSubnets.Results {
		stateSubnetIds.Add(subnet.ProviderId)
	}
	stateSpaceMap := make(map[string]params.ProviderSpace)
	spaceNames := make(set.Strings)
	for _, space := range listSpacesResult.Results {
		stateSpaceMap[space.ProviderId] = space
		spaceNames.Add(space.Name)
	}

	// TODO(mfoord): we need to delete spaces and subnets that no longer
	// exist, so long as they're not in use.
	for _, space := range providerSpaces {
		// Check if the space is already in state, in which case we know
		// its name.
		stateSpace, ok := stateSpaceMap[string(space.ProviderId)]
		var spaceTag names.SpaceTag
		if ok {
			spaceName := stateSpace.Name
			if !names.IsValidSpace(spaceName) {
				// Really shouldn't happen.
				// TODO(mfoord): erroring out will cause the
				// worker to be restarted, and we will hit the
				// same problem again. Log and continue instead?
				return errors.Errorf("space %q has an invalid name", spaceName)

			}
			spaceTag = names.NewSpaceTag(spaceName)

		} else {
			// The space is new, we need to create a valid name for it
			// in state.
			spaceName := string(space.ProviderId)
			spaceName = strings.Replace(spaceName, " ", "-", -1)
			spaceName = strings.ToLower(spaceName)
			if !names.IsValidSpace(spaceName) {
				// Convert the name into a valid name that isn't already in
				// use.
				spaceName = convertSpaceName(spaceName, spaceNames)
				spaceNames.Add(spaceName)
			}
			spaceTag = names.NewSpaceTag(spaceName)
			// We need to create the space.
			args := params.CreateSpacesParams{
				Spaces: []params.CreateSpaceParams{{
					Public:   false,
					SpaceTag: spaceTag.String(),
				}}}
			result, err := dw.api.CreateSpaces(args)
			if err != nil {
				logger.Errorf("error creating space %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from CreateSpaces, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				return errors.Errorf("error from CreateSpaces: %v", result.Results[0].Error)
			}
		}
		// TODO(mfoord): currently no way of removing subnets, or
		// changing the space they're in, so we can only add ones we
		// don't already know about.
		logger.Debugf("Created space %v with %v subnets", spaceTag.String(), len(space.Subnets))
		for _, subnet := range space.Subnets {
			if stateSubnetIds.Contains(string(subnet.ProviderId)) {
				continue
			}
			zones := subnet.AvailabilityZones
			if len(zones) == 0 {
				zones = []string{"default"}
			}
			args := params.AddSubnetsParams{
				Subnets: []params.AddSubnetParams{{
					SubnetProviderId: string(subnet.ProviderId),
					SpaceTag:         spaceTag.String(),
					Zones:            zones,
				}}}
			logger.Tracef("Adding subnet %v", subnet.CIDR)
			result, err := dw.api.AddSubnets(args)
			if err != nil {
				logger.Errorf("invalid creating subnet %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from AddSubnets, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				logger.Errorf("error creating subnet %v", result.Results[0].Error)
				return errors.Errorf("error creating subnet %v", result.Results[0].Error)
			}
		}
	}
	return nil
}
<commit_msg>Comment update<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package discoverspaces

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/juju/api/discoverspaces"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/worker"
	"github.com/juju/loggo"
	"github.com/juju/names"
	"github.com/juju/utils/set"
	"launchpad.net/tomb"
)

var logger = loggo.GetLogger("juju.discoverspaces")

type discoverspacesWorker struct {
	api  *discoverspaces.API
	tomb tomb.Tomb
}

var invalidChars = regexp.MustCompile("[^0-9a-z-]")
var dashPrefix = regexp.MustCompile("^-*")

func convertSpaceName(name string, existing set.Strings) string {
	// First replace any character that isn't in the set "-", "a-z", "0-9".
	name = invalidChars.ReplaceAllString(name, "")
	// Next get rid of any dashes at the start as that isn't valid.
	name = dashPrefix.ReplaceAllString(name, "")
	// Special case of when the space name was only dashes or invalid
	// characters!
	if name == "" {
		name = "empty"
	}
	// If this name is in use add a numerical suffix.
	if existing.Contains(name) {
		counter := 2
		for existing.Contains(name + fmt.Sprintf("-%d", counter)) {
			counter += 1
		}
		name = name + fmt.Sprintf("-%d", counter)
	}
	return name
}

// NewWorker returns a worker
func NewWorker(api *discoverspaces.API) worker.Worker {
	dw := &discoverspacesWorker{
		api: api,
	}
	go func() {
		defer dw.tomb.Done()
		dw.tomb.Kill(dw.loop())
	}()
	return dw
}

func (dw *discoverspacesWorker) Kill() {
	dw.tomb.Kill(nil)
}

func (dw *discoverspacesWorker) Wait() error {
	return dw.tomb.Wait()
}

func (dw *discoverspacesWorker) loop() (err error) {
	envCfg, err := dw.api.EnvironConfig()
	if err != nil {
		return err
	}
	environ, err := environs.New(envCfg)
	if err != nil {
		return err
	}
	networkingEnviron, ok := environs.SupportsNetworking(environ)

	if ok {
		// TODO: (mfoord) API should be switched off until this is
		// completed.
		err = dw.handleSubnets(networkingEnviron)
		if err != nil {
			return errors.Trace(err)
		}
	}

	// TODO(mfoord): we'll have a watcher here checking if we need to
	// update the spaces/subnets definition.
	dying := dw.tomb.Dying()
	for {
		select {
		case <-dying:
			return nil
		}
	}
	return nil
}

func (dw *discoverspacesWorker) handleSubnets(env environs.NetworkingEnviron) error {
	ok, err := env.SupportsSpaceDiscovery()
	if err != nil {
		return errors.Trace(err)
	}
	if !ok {
		// Nothing to do.
		return nil
	}
	providerSpaces, err := env.Spaces()
	if err != nil {
		return errors.Trace(err)
	}
	listSpacesResult, err := dw.api.ListSpaces()
	if err != nil {
		return errors.Trace(err)
	}

	stateSubnets, err := dw.api.ListSubnets(params.SubnetsFilters{})
	if err != nil {
		return errors.Trace(err)
	}
	stateSubnetIds := make(set.Strings)
	for _, subnet := range stateSubnets.Results {
		stateSubnetIds.Add(subnet.ProviderId)
	}
	stateSpaceMap := make(map[string]params.ProviderSpace)
	spaceNames := make(set.Strings)
	for _, space := range listSpacesResult.Results {
		stateSpaceMap[space.ProviderId] = space
		spaceNames.Add(space.Name)
	}

	// TODO(mfoord): we need to delete spaces and subnets that no longer
	// exist, so long as they're not in use.
	for _, space := range providerSpaces {
		// Check if the space is already in state, in which case we know
		// its name.
		stateSpace, ok := stateSpaceMap[string(space.ProviderId)]
		var spaceTag names.SpaceTag
		if ok {
			spaceName := stateSpace.Name
			if !names.IsValidSpace(spaceName) {
				// Really shouldn't happen.
				// TODO(mfoord): erroring out will cause the
				// worker to be restarted, and we will hit the
				// same problem again. Log and continue instead?
				return errors.Errorf("space %q has an invalid name", spaceName)

			}
			spaceTag = names.NewSpaceTag(spaceName)

		} else {
			// The space is new, we need to create a valid name for it
			// in state.
			spaceName := string(space.ProviderId)
			spaceName = strings.Replace(spaceName, " ", "-", -1)
			spaceName = strings.ToLower(spaceName)
			if !names.IsValidSpace(spaceName) {
				// Convert the name into a valid name that isn't already in
				// use.
				spaceName = convertSpaceName(spaceName, spaceNames)
				spaceNames.Add(spaceName)
			}
			spaceTag = names.NewSpaceTag(spaceName)
			// We need to create the space.
			args := params.CreateSpacesParams{
				Spaces: []params.CreateSpaceParams{{
					Public:   false,
					SpaceTag: spaceTag.String(),
				}}}
			result, err := dw.api.CreateSpaces(args)
			if err != nil {
				logger.Errorf("error creating space %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from CreateSpaces, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				return errors.Errorf("error from CreateSpaces: %v", result.Results[0].Error)
			}
		}
		// TODO(mfoord): currently no way of removing subnets, or
		// changing the space they're in, so we can only add ones we
		// don't already know about.
		logger.Debugf("Created space %v with %v subnets", spaceTag.String(), len(space.Subnets))
		for _, subnet := range space.Subnets {
			if stateSubnetIds.Contains(string(subnet.ProviderId)) {
				continue
			}
			zones := subnet.AvailabilityZones
			if len(zones) == 0 {
				zones = []string{"default"}
			}
			args := params.AddSubnetsParams{
				Subnets: []params.AddSubnetParams{{
					SubnetProviderId: string(subnet.ProviderId),
					SpaceTag:         spaceTag.String(),
					Zones:            zones,
				}}}
			logger.Tracef("Adding subnet %v", subnet.CIDR)
			result, err := dw.api.AddSubnets(args)
			if err != nil {
				logger.Errorf("invalid creating subnet %v", err)
				return errors.Trace(err)
			}
			if len(result.Results) != 1 {
				return errors.Errorf("unexpected number of results from AddSubnets, should be 1: %v", result)
			}
			if result.Results[0].Error != nil {
				logger.Errorf("error creating subnet %v", result.Results[0].Error)
				return errors.Errorf("error creating subnet %v", result.Results[0].Error)
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package somaproto

import (
	"github.com/satori/go.uuid"
)

type ProtoRequestRepository struct {
	Repository ProtoRepository       `json:"repository,omitempty"`
	Filter     ProtoRepositoryFilter `json:"filter,omitempty"`
	Restore    bool                  `json:"restore,omitempty"`
	Purge      bool                  `json:"purge,omitempty"`
	Clear      bool                  `json:"clear,omitempty"`
	Activate   bool                  `json:"activate,omitempty"`
}

type ProtoResultRepository struct {
	Code         uint16            `json:"code,omitempty"`
	Status       string            `json:"status,omitempty"`
	Text         []string          `json:"text,omitempty"`
	Repositories []ProtoRepository `json:"repositories,omitempty"`
	JobId        uuid.UUID         `json:"jobid,omitempty"`
}

type ProtoRepository struct {
	Id         uuid.UUID                 `json:"id,omitempty"`
	Name       string                    `json:"name,omitempty"`
	Team       string                    `json:"team,omitempty"`
	IsDeleted  bool                      `json:"deleted,omitempty"`
	IsActive   bool                      `json:"active,omitempty"`
	Details    *ProtoRepositoryDetails   `json:"details,omitempty"`
	Properties []ProtoRepositoryProperty `json:"properties,omitempty"`
}

type ProtoRepositoryFilter struct {
	Name      string `json:"name,omitempty"`
	Team      string `json:"team,omitempty"`
	IsDeleted bool   `json:"deleted,omitempty"`
	IsActive  bool   `json:"active,omitempty"`
}

type ProtoRepositoryDetails struct {
	CreatedAt string `json:"createdat,omitempty"`
	CreatedBy string `json:"createdby,omitempty"`
}

type ProtoRepositoryProperty struct {
	Type         string `json:"type,omitempty"`
	View         string `json:"view,omitempty"`
	Property     string `json:"property,omitempty"`
	Value        string `json:"value,omitempty"`
	Inheritance  bool   `json:"inheritance,omitempty"`
	ChildrenOnly bool   `json:"children,omitempty"`
	Source       string `json:"source,omitempty"`
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Update repository structs<commit_after>package somaproto

type ProtoRequestRepository struct {
	Repository *ProtoRepository       `json:"repository,omitempty"`
	Filter     *ProtoRepositoryFilter `json:"filter,omitempty"`
	Restore    bool                   `json:"restore,omitempty"`
	Purge      bool                   `json:"purge,omitempty"`
	Clear      bool                   `json:"clear,omitempty"`
	Activate   bool                   `json:"activate,omitempty"`
}

type ProtoResultRepository struct {
	Code         uint16            `json:"code,omitempty"`
	Status       string            `json:"status,omitempty"`
	Text         []string          `json:"text,omitempty"`
	Repositories []ProtoRepository `json:"repositories,omitempty"`
	JobId        string            `json:"jobid,omitempty"`
}

type ProtoRepository struct {
	Id         string                    `json:"id,omitempty"`
	Name       string                    `json:"name,omitempty"`
	Team       string                    `json:"team,omitempty"`
	IsDeleted  bool                      `json:"deleted,omitempty"`
	IsActive   bool                      `json:"active,omitempty"`
	Details    *ProtoRepositoryDetails   `json:"details,omitempty"`
	Properties []ProtoRepositoryProperty `json:"properties,omitempty"`
}

type ProtoRepositoryFilter struct {
	Name      string `json:"name,omitempty"`
	Team      string `json:"team,omitempty"`
	IsDeleted bool   `json:"deleted,omitempty"`
	IsActive  bool   `json:"active,omitempty"`
}

type ProtoRepositoryDetails struct {
	CreatedAt string `json:"createdat,omitempty"`
	CreatedBy string `json:"createdby,omitempty"`
}

type ProtoRepositoryProperty struct {
	Type         string `json:"type,omitempty"`
	View         string `json:"view,omitempty"`
	Property     string `json:"property,omitempty"`
	Value        string `json:"value,omitempty"`
	Inheritance  bool   `json:"inheritance,omitempty"`
	ChildrenOnly bool   `json:"children,omitempty"`
	Source       string `json:"source,omitempty"`
}

//
func (p *ProtoResultRepository) ErrorMark(err error, imp bool, found bool,
	length int) bool {
	if p.markError(err) {
		return true
	}
	if p.markImplemented(imp) {
		return true
	}
	if p.markFound(found, length) {
		return true
	}
	return p.markOk()
}

func (p *ProtoResultRepository) markError(err error) bool {
	if err != nil {
		p.Code = 500
		p.Status = "ERROR"
		p.Text = []string{err.Error()}
		return true
	}
	return false
}

func (p *ProtoResultRepository) markImplemented(f bool) bool {
	if f {
		p.Code = 501
		p.Status = "NOT IMPLEMENTED"
		return true
	}
	return false
}

func (p *ProtoResultRepository) markFound(f bool, i int) bool {
	if f || i == 0 {
		p.Code = 404
		p.Status = "NOT FOUND"
		return true
	}
	return false
}

func (p *ProtoResultRepository) markOk() bool {
	p.Code = 200
	p.Status = "OK"
	return false
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>