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

package params

import (
	"bytes"
	"encoding/json"
	"fmt"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/instance"
)

// Error holds the error result of a single operation.
type Error struct {
	Message string
	Code    string
}

// ErrorCode implements rpc.ErrorCoder interface.
func (e Error) ErrorCode() string {
	return e.Code
}

// Error implements the error interface.
func (e Error) Error() string {
	return e.Message
}

// GoString implements fmt.GoStringer.  It means that a *Error shows its
// contents correctly when printed with %#v.
func (e Error) GoString() string {
	return fmt.Sprintf("&params.Error{%q, %q}", e.Code, e.Message)
}

// ErrorResults holds the results of calling a bulk operation which
// mutates multiple entites, like Machiner.SetStatus. The order and
// number of elements matches the entities specified in the request.
type ErrorResults struct {
	// Errors contains errors occured while performing each operation (if any).
	Errors []*Error
}

// Machines holds the arguments for making an API call working on
// multiple machine entities.
type Machines struct {
	Ids []string
}

// MachineSetStatus holds a machine id, status and extra info.
type MachineSetStatus struct {
	Id     string
	Status Status
	Info   string
}

// MachinesSetStatus holds the parameters for making a Machiner.SetStatus call.
type MachinesSetStatus struct {
	Machines []MachineSetStatus
}

// AddRelation holds the parameters for making the AddRelation call.
// The endpoints specified are unordered.
type AddRelation struct {
	Endpoints []string
}

// AddRelationResults holds the results of a AddRelation call. The Endpoints
// field maps service names to the involved endpoints.
type AddRelationResults struct {
	Endpoints map[string]charm.Relation
}

// DestroyRelation holds the parameters for making the DestroyRelation call.
// The endpoints specified are unordered.
type DestroyRelation struct {
	Endpoints []string
}

// Life describes the lifecycle state of an entity ("alive", "dying"
// or "dead").
type Life string

// MachineLifeResult holds the result of Machiner.Life for a single machine.
type MachineLifeResult struct {
	Life  Life
	Error *Error
}

// MachinesLifeResults holds the results of a Machiner.Life call.
type MachinesLifeResults struct {
	Machines []MachineLifeResult
}

// MachineAgentGetMachinesResults holds the results of a
// machineagent.API.GetMachines call.
type MachineAgentGetMachinesResults struct {
	Machines []MachineAgentGetMachinesResult
}

// MachineJob values define responsibilities that machines may be
// expected to fulfil.
type MachineJob string

const (
	JobHostUnits     MachineJob = "JobHostUnits"
	JobManageEnviron MachineJob = "JobManageEnviron"
	JobManageState   MachineJob = "JobManageState"
)

// MachineAgentGetMachinesResult holds the results of a
// machineagent.API.GetMachines call for a single machine.
type MachineAgentGetMachinesResult struct {
	Life  Life
	Jobs  []MachineJob
	Error *Error
}

// ServiceDeploy holds the parameters for making the ServiceDeploy call.
type ServiceDeploy struct {
	ServiceName    string
	CharmUrl       string
	NumUnits       int
	Config         map[string]string
	ConfigYAML     string // Takes precedence over config if both are present.
	Constraints    constraints.Value
	ForceMachineId string
}

// ServiceSetCharm sets the charm for a given service.
type ServiceSetCharm struct {
	ServiceName string
	CharmUrl    string
	Force       bool
}

// ServiceExpose holds the parameters for making the ServiceExpose call.
type ServiceExpose struct {
	ServiceName string
}

// ServiceSet holds the parameters for a ServiceSet
// command. Options contains the configuration data.
type ServiceSet struct {
	ServiceName string
	Options     map[string]string
}

// ServiceSetYAML holds the parameters for
// a ServiceSetYAML command. Config contains the
// configuration data in YAML format.
type ServiceSetYAML struct {
	ServiceName string
	Config      string
}

// ServiceGet holds parameters for making the ServiceGet call.
type ServiceGet struct {
	ServiceName string
}

// ServiceGetResults holds results of the ServiceGet call.
type ServiceGetResults struct {
	Service     string
	Charm       string
	Config      map[string]interface{}
	Constraints constraints.Value
}

// ServiceUnexpose holds parameters for the ServiceUnexpose call.
type ServiceUnexpose struct {
	ServiceName string
}

// Resolved holds parameters for the Resolved call.
type Resolved struct {
	UnitName string
	Retry    bool
}

// ResolvedResults holds results of the Resolved call.
type ResolvedResults struct {
	Service  string
	Charm    string
	Settings map[string]interface{}
}

// AddServiceUnitsResults holds the names of the units added by the
// AddServiceUnits call.
type AddServiceUnitsResults struct {
	Units []string
}

// AddServiceUnits holds parameters for the AddUnits call.
type AddServiceUnits struct {
	ServiceName string
	NumUnits    int
}

// DestroyServiceUnits holds parameters for the DestroyUnits call.
type DestroyServiceUnits struct {
	UnitNames []string
}

// ServiceDestroy holds the parameters for making the ServiceDestroy call.
type ServiceDestroy struct {
	ServiceName string
}

// PasswordChanges holds the parameters for making a SetPasswords call.
type PasswordChanges struct {
	Changes []PasswordChange
}

// PasswordChange specifies a password change for the entity
// with the given tag.
type PasswordChange struct {
	Tag      string
	Password string
}

// Creds holds credentials for identifying an entity.
type Creds struct {
	AuthTag  string
	Password string
}

// EntityWatcherId holds the id of an EntityWatcher.
type EntityWatcherId struct {
	EntityWatcherId string
}

// LifecycleWatchResults holds the results of API calls
// that watch the lifecycle of a set of objects.
// It is used both for the initial Watch request
// and for subsequent Next requests.
type LifecycleWatchResults struct {
	// LifeCycleWatcherId holds the id of the newly
	// created watcher. It will be empty for a Next
	// request.
	LifecycleWatcherId string

	// Ids holds the list of entity ids.
	// For a Watch request, it holds all entity ids being
	// watched; for a Next request, it holds the ids of those
	// that have changed.
	Ids []string
}

// EnvironConfigWatchResults holds the result of
// State.WatchEnvironConfig(): id of the created EnvironConfigWatcher,
// along with the current environment configuration. It is also used
// for the result of EnvironConfigWatcher.Next(), when it contains the
// changed config (EnvironConfigWatcherId will be empty in this case).
type EnvironConfigWatchResults struct {
	EnvironConfigWatcherId string
	Config                 map[string]interface{}
}

// AllWatcherId holds the id of an AllWatcher.
type AllWatcherId struct {
	AllWatcherId string
}

// AllWatcherNextResults holds deltas returned from calling AllWatcher.Next().
type AllWatcherNextResults struct {
	Deltas []Delta
}

// Password holds a password.
type Password struct {
	Password string
}

// Unit holds details of a unit.
type Unit struct {
	DeployerTag string
	// TODO(rog) other unit attributes.
}

// User holds details of a user.
type User struct {
	// This is a placeholder for any information
	// that may be associated with a user in the
	// future.
}

// GetAnnotationsResults holds annotations associated with an entity.
type GetAnnotationsResults struct {
	Annotations map[string]string
}

// GetAnnotations stores parameters for making the GetAnnotations call.
type GetAnnotations struct {
	Tag string
}

// SetAnnotations stores parameters for making the SetAnnotations call.
type SetAnnotations struct {
	Tag   string
	Pairs map[string]string
}

// GetServiceConstraints stores parameters for making the GetServiceConstraints call.
type GetServiceConstraints struct {
	ServiceName string
}

// GetServiceConstraintsResults holds results of the GetServiceConstraints call.
type GetServiceConstraintsResults struct {
	Constraints constraints.Value
}

// SetServiceConstraints stores parameters for making the SetServiceConstraints call.
type SetServiceConstraints struct {
	ServiceName string
	Constraints constraints.Value
}

// CharmInfo stores parameters for a CharmInfo call.
type CharmInfo struct {
	CharmURL string
}

// Delta holds details of a change to the environment.
type Delta struct {
	// If Removed is true, the entity has been removed;
	// otherwise it has been created or changed.
	Removed bool
	// Entity holds data about the entity that has changed.
	Entity EntityInfo
}

// MarshalJSON implements json.Marshaler.
func (d *Delta) MarshalJSON() ([]byte, error) {
	b, err := json.Marshal(d.Entity)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	buf.WriteByte('[')
	c := "change"
	if d.Removed {
		c = "remove"
	}
	fmt.Fprintf(&buf, "%q,%q,", d.Entity.EntityId().Kind, c)
	buf.Write(b)
	buf.WriteByte(']')
	return buf.Bytes(), nil
}

// UnmarshalJSON implements json.Unmarshaler.
func (d *Delta) UnmarshalJSON(data []byte) error {
	var elements []json.RawMessage
	if err := json.Unmarshal(data, &elements); err != nil {
		return err
	}
	if len(elements) != 3 {
		return fmt.Errorf(
			"Expected 3 elements in top-level of JSON but got %d",
			len(elements))
	}
	var entityKind, operation string
	if err := json.Unmarshal(elements[0], &entityKind); err != nil {
		return err
	}
	if err := json.Unmarshal(elements[1], &operation); err != nil {
		return err
	}
	if operation == "remove" {
		d.Removed = true
	} else if operation != "change" {
		return fmt.Errorf("Unexpected operation %q", operation)
	}
	switch entityKind {
	case "machine":
		d.Entity = new(MachineInfo)
	case "service":
		d.Entity = new(ServiceInfo)
	case "unit":
		d.Entity = new(UnitInfo)
	case "relation":
		d.Entity = new(RelationInfo)
	case "annotation":
		d.Entity = new(AnnotationInfo)
	default:
		return fmt.Errorf("Unexpected entity name %q", entityKind)
	}
	if err := json.Unmarshal(elements[2], &d.Entity); err != nil {
		return err
	}
	return nil
}

// EntityInfo is implemented by all entity Info types.
type EntityInfo interface {
	// EntityId returns an identifier that will uniquely
	// identify the entity within its kind
	EntityId() EntityId
}

// IMPORTANT NOTE: the types below are direct subsets of the entity docs
// held in mongo, as defined in the state package (serviceDoc,
// machineDoc etc).
// In particular, the document marshalled into mongo
// must unmarshal correctly into these documents.
// If the format of a field in a document is changed in mongo, or
// a field is removed and it coincides with one of the
// fields below, a similar change must be made here.
//
// MachineInfo corresponds with state.machineDoc.
// ServiceInfo corresponds with state.serviceDoc.
// UnitInfo corresponds with state.unitDoc.
// RelationInfo corresponds with state.relationDoc.
// AnnotationInfo corresponds with state.annotatorDoc.

var (
	_ EntityInfo = (*MachineInfo)(nil)
	_ EntityInfo = (*ServiceInfo)(nil)
	_ EntityInfo = (*UnitInfo)(nil)
	_ EntityInfo = (*RelationInfo)(nil)
	_ EntityInfo = (*AnnotationInfo)(nil)
)

type EntityId struct {
	Kind string
	Id   interface{}
}

// MachineInfo holds the information about a Machine
// that is watched by StateWatcher.
type MachineInfo struct {
	Id         string `bson:"_id"`
	InstanceId string
	Status     Status
	StatusInfo string
	// Not exported - used internally to detect changes.
	nonce      string
}

func (i *MachineInfo) EntityId() EntityId {
	return EntityId{
		Kind: "machine",
		Id:   i.Id,
	}
}

type ServiceInfo struct {
	Name        string `bson:"_id"`
	Exposed     bool
	CharmURL    string
	Life        Life
	Constraints constraints.Value
	Config      map[string]interface{}
}

func (i *ServiceInfo) EntityId() EntityId {
	return EntityId{
		Kind: "service",
		Id:   i.Name,
	}
}

type UnitInfo struct {
	Name           string `bson:"_id"`
	Service        string
	Series         string
	CharmURL       string
	PublicAddress  string
	PrivateAddress string
	MachineId      string
	Ports          []instance.Port
	Status         Status
	StatusInfo     string
}

func (i *UnitInfo) EntityId() EntityId {
	return EntityId{
		Kind: "unit",
		Id:   i.Name,
	}
}

type Endpoint struct {
	ServiceName string
	Relation    charm.Relation
}

type RelationInfo struct {
	Key       string `bson:"_id"`
	Endpoints []Endpoint
}

func (i *RelationInfo) EntityId() EntityId {
	return EntityId{
		Kind: "relation",
		Id:   i.Key,
	}
}

type AnnotationInfo struct {
	Tag         string
	Annotations map[string]string
}

func (i *AnnotationInfo) EntityId() EntityId {
	return EntityId{
		Kind: "annotation",
		Id:   i.Tag,
	}
}
<commit_msg>go fmt<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package params

import (
	"bytes"
	"encoding/json"
	"fmt"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/instance"
)

// Error holds the error result of a single operation.
type Error struct {
	Message string
	Code    string
}

// ErrorCode implements rpc.ErrorCoder interface.
func (e Error) ErrorCode() string {
	return e.Code
}

// Error implements the error interface.
func (e Error) Error() string {
	return e.Message
}

// GoString implements fmt.GoStringer.  It means that a *Error shows its
// contents correctly when printed with %#v.
func (e Error) GoString() string {
	return fmt.Sprintf("&params.Error{%q, %q}", e.Code, e.Message)
}

// ErrorResults holds the results of calling a bulk operation which
// mutates multiple entites, like Machiner.SetStatus. The order and
// number of elements matches the entities specified in the request.
type ErrorResults struct {
	// Errors contains errors occured while performing each operation (if any).
	Errors []*Error
}

// Machines holds the arguments for making an API call working on
// multiple machine entities.
type Machines struct {
	Ids []string
}

// MachineSetStatus holds a machine id, status and extra info.
type MachineSetStatus struct {
	Id     string
	Status Status
	Info   string
}

// MachinesSetStatus holds the parameters for making a Machiner.SetStatus call.
type MachinesSetStatus struct {
	Machines []MachineSetStatus
}

// AddRelation holds the parameters for making the AddRelation call.
// The endpoints specified are unordered.
type AddRelation struct {
	Endpoints []string
}

// AddRelationResults holds the results of a AddRelation call. The Endpoints
// field maps service names to the involved endpoints.
type AddRelationResults struct {
	Endpoints map[string]charm.Relation
}

// DestroyRelation holds the parameters for making the DestroyRelation call.
// The endpoints specified are unordered.
type DestroyRelation struct {
	Endpoints []string
}

// Life describes the lifecycle state of an entity ("alive", "dying"
// or "dead").
type Life string

// MachineLifeResult holds the result of Machiner.Life for a single machine.
type MachineLifeResult struct {
	Life  Life
	Error *Error
}

// MachinesLifeResults holds the results of a Machiner.Life call.
type MachinesLifeResults struct {
	Machines []MachineLifeResult
}

// MachineAgentGetMachinesResults holds the results of a
// machineagent.API.GetMachines call.
type MachineAgentGetMachinesResults struct {
	Machines []MachineAgentGetMachinesResult
}

// MachineJob values define responsibilities that machines may be
// expected to fulfil.
type MachineJob string

const (
	JobHostUnits     MachineJob = "JobHostUnits"
	JobManageEnviron MachineJob = "JobManageEnviron"
	JobManageState   MachineJob = "JobManageState"
)

// MachineAgentGetMachinesResult holds the results of a
// machineagent.API.GetMachines call for a single machine.
type MachineAgentGetMachinesResult struct {
	Life  Life
	Jobs  []MachineJob
	Error *Error
}

// ServiceDeploy holds the parameters for making the ServiceDeploy call.
type ServiceDeploy struct {
	ServiceName    string
	CharmUrl       string
	NumUnits       int
	Config         map[string]string
	ConfigYAML     string // Takes precedence over config if both are present.
	Constraints    constraints.Value
	ForceMachineId string
}

// ServiceSetCharm sets the charm for a given service.
type ServiceSetCharm struct {
	ServiceName string
	CharmUrl    string
	Force       bool
}

// ServiceExpose holds the parameters for making the ServiceExpose call.
type ServiceExpose struct {
	ServiceName string
}

// ServiceSet holds the parameters for a ServiceSet
// command. Options contains the configuration data.
type ServiceSet struct {
	ServiceName string
	Options     map[string]string
}

// ServiceSetYAML holds the parameters for
// a ServiceSetYAML command. Config contains the
// configuration data in YAML format.
type ServiceSetYAML struct {
	ServiceName string
	Config      string
}

// ServiceGet holds parameters for making the ServiceGet call.
type ServiceGet struct {
	ServiceName string
}

// ServiceGetResults holds results of the ServiceGet call.
type ServiceGetResults struct {
	Service     string
	Charm       string
	Config      map[string]interface{}
	Constraints constraints.Value
}

// ServiceUnexpose holds parameters for the ServiceUnexpose call.
type ServiceUnexpose struct {
	ServiceName string
}

// Resolved holds parameters for the Resolved call.
type Resolved struct {
	UnitName string
	Retry    bool
}

// ResolvedResults holds results of the Resolved call.
type ResolvedResults struct {
	Service  string
	Charm    string
	Settings map[string]interface{}
}

// AddServiceUnitsResults holds the names of the units added by the
// AddServiceUnits call.
type AddServiceUnitsResults struct {
	Units []string
}

// AddServiceUnits holds parameters for the AddUnits call.
type AddServiceUnits struct {
	ServiceName string
	NumUnits    int
}

// DestroyServiceUnits holds parameters for the DestroyUnits call.
type DestroyServiceUnits struct {
	UnitNames []string
}

// ServiceDestroy holds the parameters for making the ServiceDestroy call.
type ServiceDestroy struct {
	ServiceName string
}

// PasswordChanges holds the parameters for making a SetPasswords call.
type PasswordChanges struct {
	Changes []PasswordChange
}

// PasswordChange specifies a password change for the entity
// with the given tag.
type PasswordChange struct {
	Tag      string
	Password string
}

// Creds holds credentials for identifying an entity.
type Creds struct {
	AuthTag  string
	Password string
}

// EntityWatcherId holds the id of an EntityWatcher.
type EntityWatcherId struct {
	EntityWatcherId string
}

// LifecycleWatchResults holds the results of API calls
// that watch the lifecycle of a set of objects.
// It is used both for the initial Watch request
// and for subsequent Next requests.
type LifecycleWatchResults struct {
	// LifeCycleWatcherId holds the id of the newly
	// created watcher. It will be empty for a Next
	// request.
	LifecycleWatcherId string

	// Ids holds the list of entity ids.
	// For a Watch request, it holds all entity ids being
	// watched; for a Next request, it holds the ids of those
	// that have changed.
	Ids []string
}

// EnvironConfigWatchResults holds the result of
// State.WatchEnvironConfig(): id of the created EnvironConfigWatcher,
// along with the current environment configuration. It is also used
// for the result of EnvironConfigWatcher.Next(), when it contains the
// changed config (EnvironConfigWatcherId will be empty in this case).
type EnvironConfigWatchResults struct {
	EnvironConfigWatcherId string
	Config                 map[string]interface{}
}

// AllWatcherId holds the id of an AllWatcher.
type AllWatcherId struct {
	AllWatcherId string
}

// AllWatcherNextResults holds deltas returned from calling AllWatcher.Next().
type AllWatcherNextResults struct {
	Deltas []Delta
}

// Password holds a password.
type Password struct {
	Password string
}

// Unit holds details of a unit.
type Unit struct {
	DeployerTag string
	// TODO(rog) other unit attributes.
}

// User holds details of a user.
type User struct {
	// This is a placeholder for any information
	// that may be associated with a user in the
	// future.
}

// GetAnnotationsResults holds annotations associated with an entity.
type GetAnnotationsResults struct {
	Annotations map[string]string
}

// GetAnnotations stores parameters for making the GetAnnotations call.
type GetAnnotations struct {
	Tag string
}

// SetAnnotations stores parameters for making the SetAnnotations call.
type SetAnnotations struct {
	Tag   string
	Pairs map[string]string
}

// GetServiceConstraints stores parameters for making the GetServiceConstraints call.
type GetServiceConstraints struct {
	ServiceName string
}

// GetServiceConstraintsResults holds results of the GetServiceConstraints call.
type GetServiceConstraintsResults struct {
	Constraints constraints.Value
}

// SetServiceConstraints stores parameters for making the SetServiceConstraints call.
type SetServiceConstraints struct {
	ServiceName string
	Constraints constraints.Value
}

// CharmInfo stores parameters for a CharmInfo call.
type CharmInfo struct {
	CharmURL string
}

// Delta holds details of a change to the environment.
type Delta struct {
	// If Removed is true, the entity has been removed;
	// otherwise it has been created or changed.
	Removed bool
	// Entity holds data about the entity that has changed.
	Entity EntityInfo
}

// MarshalJSON implements json.Marshaler.
func (d *Delta) MarshalJSON() ([]byte, error) {
	b, err := json.Marshal(d.Entity)
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	buf.WriteByte('[')
	c := "change"
	if d.Removed {
		c = "remove"
	}
	fmt.Fprintf(&buf, "%q,%q,", d.Entity.EntityId().Kind, c)
	buf.Write(b)
	buf.WriteByte(']')
	return buf.Bytes(), nil
}

// UnmarshalJSON implements json.Unmarshaler.
func (d *Delta) UnmarshalJSON(data []byte) error {
	var elements []json.RawMessage
	if err := json.Unmarshal(data, &elements); err != nil {
		return err
	}
	if len(elements) != 3 {
		return fmt.Errorf(
			"Expected 3 elements in top-level of JSON but got %d",
			len(elements))
	}
	var entityKind, operation string
	if err := json.Unmarshal(elements[0], &entityKind); err != nil {
		return err
	}
	if err := json.Unmarshal(elements[1], &operation); err != nil {
		return err
	}
	if operation == "remove" {
		d.Removed = true
	} else if operation != "change" {
		return fmt.Errorf("Unexpected operation %q", operation)
	}
	switch entityKind {
	case "machine":
		d.Entity = new(MachineInfo)
	case "service":
		d.Entity = new(ServiceInfo)
	case "unit":
		d.Entity = new(UnitInfo)
	case "relation":
		d.Entity = new(RelationInfo)
	case "annotation":
		d.Entity = new(AnnotationInfo)
	default:
		return fmt.Errorf("Unexpected entity name %q", entityKind)
	}
	if err := json.Unmarshal(elements[2], &d.Entity); err != nil {
		return err
	}
	return nil
}

// EntityInfo is implemented by all entity Info types.
type EntityInfo interface {
	// EntityId returns an identifier that will uniquely
	// identify the entity within its kind
	EntityId() EntityId
}

// IMPORTANT NOTE: the types below are direct subsets of the entity docs
// held in mongo, as defined in the state package (serviceDoc,
// machineDoc etc).
// In particular, the document marshalled into mongo
// must unmarshal correctly into these documents.
// If the format of a field in a document is changed in mongo, or
// a field is removed and it coincides with one of the
// fields below, a similar change must be made here.
//
// MachineInfo corresponds with state.machineDoc.
// ServiceInfo corresponds with state.serviceDoc.
// UnitInfo corresponds with state.unitDoc.
// RelationInfo corresponds with state.relationDoc.
// AnnotationInfo corresponds with state.annotatorDoc.

var (
	_ EntityInfo = (*MachineInfo)(nil)
	_ EntityInfo = (*ServiceInfo)(nil)
	_ EntityInfo = (*UnitInfo)(nil)
	_ EntityInfo = (*RelationInfo)(nil)
	_ EntityInfo = (*AnnotationInfo)(nil)
)

type EntityId struct {
	Kind string
	Id   interface{}
}

// MachineInfo holds the information about a Machine
// that is watched by StateWatcher.
type MachineInfo struct {
	Id         string `bson:"_id"`
	InstanceId string
	Status     Status
	StatusInfo string
}

func (i *MachineInfo) EntityId() EntityId {
	return EntityId{
		Kind: "machine",
		Id:   i.Id,
	}
}

type ServiceInfo struct {
	Name        string `bson:"_id"`
	Exposed     bool
	CharmURL    string
	Life        Life
	Constraints constraints.Value
	Config      map[string]interface{}
}

func (i *ServiceInfo) EntityId() EntityId {
	return EntityId{
		Kind: "service",
		Id:   i.Name,
	}
}

type UnitInfo struct {
	Name           string `bson:"_id"`
	Service        string
	Series         string
	CharmURL       string
	PublicAddress  string
	PrivateAddress string
	MachineId      string
	Ports          []instance.Port
	Status         Status
	StatusInfo     string
}

func (i *UnitInfo) EntityId() EntityId {
	return EntityId{
		Kind: "unit",
		Id:   i.Name,
	}
}

type Endpoint struct {
	ServiceName string
	Relation    charm.Relation
}

type RelationInfo struct {
	Key       string `bson:"_id"`
	Endpoints []Endpoint
}

func (i *RelationInfo) EntityId() EntityId {
	return EntityId{
		Kind: "relation",
		Id:   i.Key,
	}
}

type AnnotationInfo struct {
	Tag         string
	Annotations map[string]string
}

func (i *AnnotationInfo) EntityId() EntityId {
	return EntityId{
		Kind: "annotation",
		Id:   i.Tag,
	}
}
<|endoftext|>


<commit_before>package presence

import (
	"fmt"
	zk "launchpad.net/gozk/zookeeper"
	"time"
)

// changeNode wraps a zookeeper node and can induce watches on that node to fire.
type changeNode struct {
	conn    *zk.Conn
	path    string
	content string
}

// change sets the zookeeper node's content (creating it if it doesn't exist) and
// returns the node's new MTime.
func (n *changeNode) change() (mtime time.Time, err error) {
	stat, err := n.conn.Set(n.path, n.content, -1)
	if err == zk.ZNONODE {
		_, err = n.conn.Create(n.path, n.content, 0, zk.WorldACL(zk.PERM_ALL))
		if err == nil || err == zk.ZNODEEXISTS {
			// *Someone* created the node anyway; just try again.
			return n.change()
		}
	}
	if err != nil {
		return
	}
	return stat.MTime(), nil
}

// Pinger periodically updates a node in zookeeper while it is running.
type Pinger struct {
	conn     *zk.Conn
	target   changeNode
	period   time.Duration
	stopping chan bool
}

// run calls change on p.target every p.period nanoseconds until p is stopped.
func (p *Pinger) run() {
	for {
		select {
		case <-p.stopping:
			return
		case <-time.After(p.period):
			_, err := p.target.change()
			if err != nil {
				<-p.stopping
				return
			}
		}
	}
}

// Stop stops updating the node; AliveW watches will not notice any change
// until they time out. A final write to the node is triggered to ensure
// watchers time out as late as possible.
func (p *Pinger) Stop() {
	p.stopping <- true
	p.target.change()
}

// Kill stops updating and deletes the node, causing any AliveW watches
// to observe its departure (almost) immediately.
func (p *Pinger) Kill() {
	p.stopping <- true
	p.conn.Delete(p.target.path, -1)
}

// StartPinger returns a new Pinger that refreshes the content of path periodically.
func StartPinger(conn *zk.Conn, path string, period time.Duration) (*Pinger, error) {
	target := changeNode{conn, path, period.String()}
	_, err := target.change()
	if err != nil {
		return nil, err
	}
	p := &Pinger{conn, target, period, make(chan bool)}
	go p.run()
	return p, nil
}

// node represents the state of a presence node from a watcher's perspective.
type node struct {
	conn    *zk.Conn
	path    string
	alive   bool
	timeout time.Duration
}

// setAlive sets the current values of n.alive and n.timeout, given the
// content and stat of the zookeeper node (which must exist).
func (n *node) setAlive(content string, stat *zk.Stat) error {
	// Always update the timeout (it could change) but check whether this is
	// the first run of this method.
	timeoutWasSet := n.timeout != 0
	period, err := time.ParseDuration(content)
	if err != nil {
		return fmt.Errorf("%s is not a valid presence node: %s", n.path, err)
	}
	n.timeout = period * 2
	if timeoutWasSet {
		// If timeout was already set, we've already run this method; and if
		// this function is being run for the not-first time, we can be
		// confident that the node is either still alive, or newly alive;
		// that is, there's no way it can be dead, and there's no need to
		// check for staleness with the clock node.
		n.alive = true
	} else {
		clock := changeNode{n.conn, "/clock", ""}
		now, err := clock.change()
		if err != nil {
			return err
		}
		delay := now.Sub(stat.MTime())
		n.alive = delay < n.timeout
	}
	return nil
}

// update reads from ZooKeeper the current values of n.alive and n.timeout.
func (n *node) update() error {
	content, stat, err := n.conn.Get(n.path)
	if err == zk.ZNONODE {
		n.alive = false
		n.timeout = time.Duration(0)
		return nil
	} else if err != nil {
		return err
	}
	return n.setAlive(content, stat)
}

// updateW reads from ZooKeeper the current values of n.alive and n.timeout,
// and returns a ZooKeeper watch that will be notified of data or existence
// changes.
func (n *node) updateW() (<-chan zk.Event, error) {
	content, stat, watch, err := n.conn.GetW(n.path)
	if err == zk.ZNONODE {
		n.alive = false
		n.timeout = 0
		stat, watch, err = n.conn.ExistsW(n.path)
		if err != nil {
			return nil, err
		}
		if stat != nil {
			// Someone *just* created the node; try again.
			return n.updateW()
		}
		return watch, nil
	} else if err != nil {
		return nil, err
	}
	return watch, n.setAlive(content, stat)
}

// waitDead sends false to watch when the node is deleted, or when it has
// not been updated recently enough to still qualify as alive. zkWatch must
// be observing changes on the existent node at n.path.
func (n *node) waitDead(zkWatch <-chan zk.Event, watch chan bool) {
	for n.alive {
		select {
		case <-time.After(n.timeout):
			n.alive = false
		case event := <-zkWatch:
			if !event.Ok() {
				close(watch)
				return
			}
			switch event.Type {
			case zk.EVENT_DELETED:
				n.alive = false
			case zk.EVENT_CHANGED:
				var err error
				zkWatch, err = n.updateW()
				if err != nil {
					close(watch)
					return
				}
			default:
				panic(fmt.Errorf("Unexpected event: %v", event))
			}
		}
	}
	watch <- false
}

// waitAlive sends true to watch when the node is changed or created. zkWatch
// must be observing changes on the nonexistent node at n.path.
func (n *node) waitAlive(zkWatch <-chan zk.Event, watch chan bool) {
	for !n.alive {
		event := <-zkWatch
		if !event.Ok() {
			close(watch)
			return
		}
		switch event.Type {
		case zk.EVENT_CREATED, zk.EVENT_CHANGED:
			n.alive = true
		case zk.EVENT_DELETED:
			// The pinger is still dead (just differently dead); start a new watch.
			var err error
			zkWatch, err = n.updateW()
			if err != nil {
				close(watch)
				return
			}
		default:
			panic(fmt.Errorf("Unexpected event: %v", event))
		}
	}
	watch <- true
}

// Alive returns whether the Pinger at the given path seems to be alive.
func Alive(conn *zk.Conn, path string) (bool, error) {
	n := &node{conn: conn, path: path}
	err := n.update()
	return n.alive, err
}

// AliveW returns whether the Pinger at the given path seems to be alive.
// It also returns a channel that will receive the new status when it changes.
// If an error is encountered after AliveW returns, the channel will be closed.
func AliveW(conn *zk.Conn, path string) (bool, <-chan bool, error) {
	n := &node{conn: conn, path: path}
	zkWatch, err := n.updateW()
	if err != nil {
		return false, nil, err
	}
	alive := n.alive
	watch := make(chan bool)
	if alive {
		go n.waitDead(zkWatch, watch)
	} else {
		go n.waitAlive(zkWatch, watch)
	}
	return alive, watch, nil
}
<commit_msg>differently infer first calls to setState<commit_after>package presence

import (
	"fmt"
	zk "launchpad.net/gozk/zookeeper"
	"time"
)

// changeNode wraps a zookeeper node and can induce watches on that node to fire.
type changeNode struct {
	conn    *zk.Conn
	path    string
	content string
}

// change sets the zookeeper node's content (creating it if it doesn't exist) and
// returns the node's new MTime.
func (n *changeNode) change() (mtime time.Time, err error) {
	stat, err := n.conn.Set(n.path, n.content, -1)
	if err == zk.ZNONODE {
		_, err = n.conn.Create(n.path, n.content, 0, zk.WorldACL(zk.PERM_ALL))
		if err == nil || err == zk.ZNODEEXISTS {
			// *Someone* created the node anyway; just try again.
			return n.change()
		}
	}
	if err != nil {
		return
	}
	return stat.MTime(), nil
}

// Pinger periodically updates a node in zookeeper while it is running.
type Pinger struct {
	conn     *zk.Conn
	target   changeNode
	period   time.Duration
	stopping chan bool
}

// run calls change on p.target every p.period nanoseconds until p is stopped.
func (p *Pinger) run() {
	for {
		select {
		case <-p.stopping:
			return
		case <-time.After(p.period):
			_, err := p.target.change()
			if err != nil {
				<-p.stopping
				return
			}
		}
	}
}

// Stop stops updating the node; AliveW watches will not notice any change
// until they time out. A final write to the node is triggered to ensure
// watchers time out as late as possible.
func (p *Pinger) Stop() {
	p.stopping <- true
	p.target.change()
}

// Kill stops updating and deletes the node, causing any AliveW watches
// to observe its departure (almost) immediately.
func (p *Pinger) Kill() {
	p.stopping <- true
	p.conn.Delete(p.target.path, -1)
}

// StartPinger returns a new Pinger that refreshes the content of path periodically.
func StartPinger(conn *zk.Conn, path string, period time.Duration) (*Pinger, error) {
	target := changeNode{conn, path, period.String()}
	_, err := target.change()
	if err != nil {
		return nil, err
	}
	p := &Pinger{conn, target, period, make(chan bool)}
	go p.run()
	return p, nil
}

// node represents the state of a presence node from a watcher's perspective.
type node struct {
	conn    *zk.Conn
	path    string
	alive   bool
	timeout time.Duration
}

// setState sets the current values of n.alive and n.timeout, given the
// content and stat of the zookeeper node (which must exist).
func (n *node) setState(content string, stat *zk.Stat, firstTime bool) error {
	// Always check and reset timeout; it could change.
	period, err := time.ParseDuration(content)
	if err != nil {
		return fmt.Errorf("%s is not a valid presence node: %s", n.path, err)
	}
	n.timeout = period * 2
	if firstTime {
		clock := changeNode{n.conn, "/clock", ""}
		now, err := clock.change()
		if err != nil {
			return err
		}
		delay := now.Sub(stat.MTime())
		n.alive = delay < n.timeout
	} else {
		// If this method is being run for the not-first time, we can be
		// confident that the node is either still alive, or newly alive;
		// that is, there's no way it can be dead, and there's no need to
		// check for staleness with the clock node.
		n.alive = true
	}
	return nil
}

// update reads from ZooKeeper the current values of n.alive and n.timeout.
func (n *node) update() error {
	content, stat, err := n.conn.Get(n.path)
	if err == zk.ZNONODE {
		n.alive = false
		n.timeout = time.Duration(0)
		return nil
	} else if err != nil {
		return err
	}
	return n.setState(content, stat, true)
}

// updateW reads from ZooKeeper the current values of n.alive and n.timeout,
// and returns a ZooKeeper watch that will be notified of data or existence
// changes.
func (n *node) updateW(firstTime bool) (<-chan zk.Event, error) {
	content, stat, watch, err := n.conn.GetW(n.path)
	if err == zk.ZNONODE {
		n.alive = false
		n.timeout = 0
		stat, watch, err = n.conn.ExistsW(n.path)
		if err != nil {
			return nil, err
		}
		if stat != nil {
			// Someone *just* created the node; try again.
			return n.updateW(firstTime)
		}
		return watch, nil
	} else if err != nil {
		return nil, err
	}
	return watch, n.setState(content, stat, firstTime)
}

// waitDead sends false to watch when the node is deleted, or when it has
// not been updated recently enough to still qualify as alive. zkWatch must
// be observing changes on the existent node at n.path.
func (n *node) waitDead(zkWatch <-chan zk.Event, watch chan bool) {
	for n.alive {
		select {
		case <-time.After(n.timeout):
			n.alive = false
		case event := <-zkWatch:
			if !event.Ok() {
				close(watch)
				return
			}
			switch event.Type {
			case zk.EVENT_DELETED:
				n.alive = false
			case zk.EVENT_CHANGED:
				var err error
				zkWatch, err = n.updateW(false)
				if err != nil {
					close(watch)
					return
				}
			default:
				panic(fmt.Errorf("Unexpected event: %v", event))
			}
		}
	}
	watch <- false
}

// waitAlive sends true to watch when the node is changed or created. zkWatch
// must be observing changes on the nonexistent node at n.path.
func (n *node) waitAlive(zkWatch <-chan zk.Event, watch chan bool) {
	for !n.alive {
		event := <-zkWatch
		if !event.Ok() {
			close(watch)
			return
		}
		switch event.Type {
		case zk.EVENT_CREATED, zk.EVENT_CHANGED:
			n.alive = true
		case zk.EVENT_DELETED:
			// The pinger is still dead (just differently dead); start a new watch.
			var err error
			zkWatch, err = n.updateW(false)
			if err != nil {
				close(watch)
				return
			}
		default:
			panic(fmt.Errorf("Unexpected event: %v", event))
		}
	}
	watch <- true
}

// Alive returns whether the Pinger at the given path seems to be alive.
func Alive(conn *zk.Conn, path string) (bool, error) {
	n := &node{conn: conn, path: path}
	err := n.update()
	return n.alive, err
}

// AliveW returns whether the Pinger at the given path seems to be alive.
// It also returns a channel that will receive the new status when it changes.
// If an error is encountered after AliveW returns, the channel will be closed.
func AliveW(conn *zk.Conn, path string) (bool, <-chan bool, error) {
	n := &node{conn: conn, path: path}
	zkWatch, err := n.updateW(true)
	if err != nil {
		return false, nil, err
	}
	alive := n.alive
	watch := make(chan bool)
	if alive {
		go n.waitDead(zkWatch, watch)
	} else {
		go n.waitAlive(zkWatch, watch)
	}
	return alive, watch, nil
}
<|endoftext|>


<commit_before>package http

import (
	"encoding/json"
	"net/http"
	"reflect"

	"github.com/tonto/kit/http/middleware"

	"github.com/tonto/kit/http/respond"
)

type validator interface {
	Validate() error
}

// BaseService represents base http service
type BaseService struct {
	endpoints Endpoints
	mw        []middleware.Adapter
}

// Prefix returns service routing prefix (implements Service interface)
func (b *BaseService) Prefix() string {
	return "/"
}

// Endpoints returns all registered endpoints (implements Service interface)
func (b *BaseService) Endpoints() Endpoints {
	for _, e := range b.endpoints {
		e.Handler = middleware.Adapt(e.Handler, b.mw...)
	}
	return b.endpoints
}

// RegisterEndpoint is a helper method that registers service endpoint
func (b *BaseService) RegisterEndpoint(path string, h http.Handler, methods ...string) {
	if b.endpoints == nil {
		b.endpoints = make(map[string]*Endpoint)
	}
	b.endpoints[path] = &Endpoint{
		Methods: methods,
		Handler: h,
	}
}

// RegisterMiddleware is a helper method that registers provided middlewares
// for service wide usage, ie. provided middlewares are applied to all endpoints
func (b *BaseService) RegisterMiddleware(mw ...middleware.Adapter) {
	b.mw = mw
}

// HandlerFromMethod creates new handler from a given service method.
// Required request struct will be recognised and request body will be
// correctly unmarshaled to it.
func (b *BaseService) HandlerFromMethod(m interface{}) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		v := reflect.ValueOf(m)
		reqParamType := v.Type().In(2).Elem()
		req := reflect.New(reqParamType).Interface()

		err := json.NewDecoder(r.Body).Decode(req)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		defer r.Body.Close()

		if validator, ok := interface{}(req).(validator); ok {
			err = validator.Validate()
			if err != nil {
				respond.With(w, r, http.StatusBadRequest, err)
				return
			}
		}

		v.Call([]reflect.Value{
			reflect.ValueOf(w),
			reflect.ValueOf(r),
			reflect.ValueOf(req),
		})
	})
}
<commit_msg>Service middleware bug<commit_after>package http

import (
	"encoding/json"
	"net/http"
	"reflect"

	"github.com/tonto/kit/http/middleware"

	"github.com/tonto/kit/http/respond"
)

type validator interface {
	Validate() error
}

// BaseService represents base http service
type BaseService struct {
	endpoints Endpoints
	mw        []middleware.Adapter
}

// Prefix returns service routing prefix (implements Service interface)
func (b *BaseService) Prefix() string {
	return "/"
}

// Endpoints returns all registered endpoints (implements Service interface)
func (b *BaseService) Endpoints() Endpoints {
	for _, e := range b.endpoints {
		if b.mw != nil {
			e.Handler = middleware.Adapt(e.Handler, b.mw...)
		}
	}
	return b.endpoints
}

// RegisterEndpoint is a helper method that registers service endpoint
func (b *BaseService) RegisterEndpoint(path string, h http.Handler, methods ...string) {
	if b.endpoints == nil {
		b.endpoints = make(map[string]*Endpoint)
	}
	b.endpoints[path] = &Endpoint{
		Methods: methods,
		Handler: h,
	}
}

// RegisterMiddleware is a helper method that registers provided middlewares
// for service wide usage, ie. provided middlewares are applied to all endpoints
func (b *BaseService) RegisterMiddleware(mw ...middleware.Adapter) {
	b.mw = mw
}

// HandlerFromMethod creates new handler from a given service method.
// Required request struct will be recognised and request body will be
// correctly unmarshaled to it.
func (b *BaseService) HandlerFromMethod(m interface{}) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		v := reflect.ValueOf(m)
		reqParamType := v.Type().In(2).Elem()
		req := reflect.New(reqParamType).Interface()

		err := json.NewDecoder(r.Body).Decode(req)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		defer r.Body.Close()

		if validator, ok := interface{}(req).(validator); ok {
			err = validator.Validate()
			if err != nil {
				respond.With(w, r, http.StatusBadRequest, err)
				return
			}
		}

		v.Call([]reflect.Value{
			reflect.ValueOf(w),
			reflect.ValueOf(r),
			reflect.ValueOf(req),
		})
	})
}
<|endoftext|>


<commit_before>package httpcli

import (
	"bytes"
	"errors"
	"io"
	"net/http"

	"github.com/mesos/mesos-go/encoding"
)

var (
	// ErrNotLeader is returned by Do calls that are sent to a non leading Mesos master.
	ErrNotLeader = errors.New("scheduler: call sent to a non-leading master")
	// ErrAuth is returned by Do calls that are not successfully authenticated.
	ErrAuth = errors.New("scheduler: call not authenticated")
	// ErrUnsubscribed is returned by Do calls that are sent before a subscription is established.
	ErrUnsubscribed = errors.New("scheduler: no subscription established")
	// ErrVersion is returned by Do calls that are sent to an	incompatible API version.
	ErrVersion = errors.New("scheduler: incompatible API version")
	// ErrMalformed is returned by Do calls that are malformed.
	ErrMalformed = errors.New("scheduler: malformed request")
	// ErrMediaType is returned by Do calls that are sent with an unsupported media type.
	ErrMediaType = errors.New("scheduler: unsupported media type")
	// ErrRateLimit is returned by Do calls that are rate limited.
	ErrRateLimit = errors.New("scheduler: rate limited")

	// codeErrors maps HTTP response codes to their respective errors.
	codeErrors = map[int]error{
		http.StatusOK:                nil,
		http.StatusAccepted:          nil,
		http.StatusTemporaryRedirect: ErrNotLeader,
		http.StatusBadRequest:        ErrMalformed,
		http.StatusConflict:          ErrVersion,
		http.StatusForbidden:         ErrUnsubscribed,
		http.StatusUnauthorized:      ErrAuth,
		http.StatusNotAcceptable:     ErrMediaType,
		429: ErrRateLimit,
	}
)

// DoFunc sends an HTTP request and returns an HTTP response.
//
// An error is returned if caused by client policy (such as
// http.Client.CheckRedirect), or if there was an HTTP protocol error. A
// non-2xx response doesn't cause an error.
//
// When err is nil, resp always contains a non-nil resp.Body.
//
// Callers should close resp.Body when done reading from it. If resp.Body is
// not closed, an underlying RoundTripper (typically Transport) may not be able
// to re-use a persistent TCP connection to the server for a subsequent
// "keep-alive" request.
//
// The request Body, if non-nil, will be closed by an underlying Transport,
// even on errors.
type DoFunc func(*http.Request) (*http.Response, error)

// A Client is a Mesos HTTP APIs client.
type Client struct {
	url   string
	do    DoFunc
	hdr   http.Header
	codec *encoding.Codec
}

// New returns a new Client with the given Opts applied.
// Callers are expected to configure the URL, Doer, and Codec options prior to
// invoking Do.
func New(opts ...Opt) *Client { return new(Client).With(opts...) }

// Opt defines a functional option for the HTTP client type.
type Opt func(*Client)

// With applies the given Opts to a Client and returns itself.
func (c *Client) With(opts ...Opt) *Client {
	for _, opt := range opts {
		opt(c)
	}
	return c
}

// Do sends a Call and returns a streaming Decoder from which callers can read
// Events from, an io.Closer to close the event stream on graceful termination
// and an error in case of failure. Callers are expected to *always* close a
// non-nil io.Closer if one is returned.
func (c *Client) Do(m encoding.Marshaler) (encoding.Decoder, io.Closer, error) {
	var body bytes.Buffer
	if err := c.codec.NewEncoder(&body).Encode(m); err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest("POST", c.url, &body)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Set("Content-Type", c.codec.MediaTypes[0])
	req.Header.Set("Accept", c.codec.MediaTypes[1])

	res, err := c.do(req)
	if err != nil {
		if res != nil && res.Body != nil {
			res.Body.Close()
		}
		return nil, nil, err
	}

	return c.codec.NewDecoder(res.Body), res.Body, codeErrors[res.StatusCode]
}

// URL returns an Opt that sets a Client's URL.
func URL(rawurl string) Opt { return func(c *Client) { c.url = rawurl } }

// Doer returns an Opt that sets a Client's DoFunc
func Doer(do DoFunc) Opt { return func(c *Client) { c.do = do } }

// Codec returns an Opt that sets a Client's Codec.
func Codec(codec *encoding.Codec) Opt { return func(c *Client) { c.codec = codec } }

// Header returns an Opt that adds a header to an Client's headers.
func Header(k, v string) Opt { return func(c *Client) { c.hdr.Add(k, v) } }
<commit_msg>add support for per-request options<commit_after>package httpcli

import (
	"bytes"
	"errors"
	"io"
	"net/http"

	"github.com/mesos/mesos-go/encoding"
)

var (
	// ErrNotLeader is returned by Do calls that are sent to a non leading Mesos master.
	ErrNotLeader = errors.New("scheduler: call sent to a non-leading master")
	// ErrAuth is returned by Do calls that are not successfully authenticated.
	ErrAuth = errors.New("scheduler: call not authenticated")
	// ErrUnsubscribed is returned by Do calls that are sent before a subscription is established.
	ErrUnsubscribed = errors.New("scheduler: no subscription established")
	// ErrVersion is returned by Do calls that are sent to an	incompatible API version.
	ErrVersion = errors.New("scheduler: incompatible API version")
	// ErrMalformed is returned by Do calls that are malformed.
	ErrMalformed = errors.New("scheduler: malformed request")
	// ErrMediaType is returned by Do calls that are sent with an unsupported media type.
	ErrMediaType = errors.New("scheduler: unsupported media type")
	// ErrRateLimit is returned by Do calls that are rate limited.
	ErrRateLimit = errors.New("scheduler: rate limited")

	// codeErrors maps HTTP response codes to their respective errors.
	codeErrors = map[int]error{
		http.StatusOK:                nil,
		http.StatusAccepted:          nil,
		http.StatusTemporaryRedirect: ErrNotLeader,
		http.StatusBadRequest:        ErrMalformed,
		http.StatusConflict:          ErrVersion,
		http.StatusForbidden:         ErrUnsubscribed,
		http.StatusUnauthorized:      ErrAuth,
		http.StatusNotAcceptable:     ErrMediaType,
		429: ErrRateLimit,
	}
)

// DoFunc sends an HTTP request and returns an HTTP response.
//
// An error is returned if caused by client policy (such as
// http.Client.CheckRedirect), or if there was an HTTP protocol error. A
// non-2xx response doesn't cause an error.
//
// When err is nil, resp always contains a non-nil resp.Body.
//
// Callers should close resp.Body when done reading from it. If resp.Body is
// not closed, an underlying RoundTripper (typically Transport) may not be able
// to re-use a persistent TCP connection to the server for a subsequent
// "keep-alive" request.
//
// The request Body, if non-nil, will be closed by an underlying Transport,
// even on errors.
type DoFunc func(*http.Request) (*http.Response, error)

// A Client is a Mesos HTTP APIs client.
type Client struct {
	url   string
	do    DoFunc
	hdr   http.Header
	codec *encoding.Codec
}

// New returns a new Client with the given Opts applied.
// Callers are expected to configure the URL, Doer, and Codec options prior to
// invoking Do.
func New(opts ...Opt) *Client { return new(Client).With(opts...) }

// Opt defines a functional option for the HTTP client type.
type Opt func(*Client)

// RequestOpt defines a functional option for an http.Request.
type RequestOpt func(*http.Request)

// With applies the given Opts to a Client and returns itself.
func (c *Client) With(opts ...Opt) *Client {
	for _, opt := range opts {
		opt(c)
	}
	return c
}

// Do sends a Call and returns a streaming Decoder from which callers can read
// Events from, an io.Closer to close the event stream on graceful termination
// and an error in case of failure. Callers are expected to *always* close a
// non-nil io.Closer if one is returned.
func (c *Client) Do(m encoding.Marshaler, opt ...RequestOpt) (encoding.Decoder, io.Closer, error) {
	var body bytes.Buffer
	if err := c.codec.NewEncoder(&body).Encode(m); err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest("POST", c.url, &body)
	if err != nil {
		return nil, nil, err
	}

	// default headers, applied to all requests
	for k, v := range c.hdr {
		req.Header[k] = v
	}

	// apply per-request options
	for _, o := range opt {
		o(req)
	}

	// these headers override anything that a caller may have tried to set
	req.Header.Set("Content-Type", c.codec.MediaTypes[0])
	req.Header.Set("Accept", c.codec.MediaTypes[1])

	res, err := c.do(req)
	if err != nil {
		if res != nil && res.Body != nil {
			res.Body.Close()
		}
		return nil, nil, err
	}

	return c.codec.NewDecoder(res.Body), res.Body, codeErrors[res.StatusCode]
}

// URL returns an Opt that sets a Client's URL.
func URL(rawurl string) Opt { return func(c *Client) { c.url = rawurl } }

// Doer returns an Opt that sets a Client's DoFunc
func Doer(do DoFunc) Opt { return func(c *Client) { c.do = do } }

// Codec returns an Opt that sets a Client's Codec.
func Codec(codec *encoding.Codec) Opt { return func(c *Client) { c.codec = codec } }

// DefaultHeader returns an Opt that adds a header to an Client's headers.
func DefaultHeader(k, v string) Opt { return func(c *Client) { c.hdr.Add(k, v) } }

// Header returns an RequestOpt that adds a header value to an HTTP requests's header.
func Header(k, v string) RequestOpt { return func(r *http.Request) { r.Header.Add(k, v) } }

// Close returns a RequestOpt that determines whether to close the underlying connection after sending the request.
func Close(b bool) RequestOpt { return func(r *http.Request) { r.Close = b } }
<|endoftext|>


<commit_before>// Package counters provides a simple counter, max and min functionalities.
// All counters are kept in CounterBox.
// Library is thread safe.
package counters

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"sync"
	"sync/atomic"
	"text/template"
)

// MaxMinValue is an interface for minima and maxima counters.
type MaxMinValue interface {
	// Set allows to update value if necessary.
	Set(int)
	// Name returns a name of counter.
	Name() string
	// Value returns a current value.
	Value() int64
}

// Counter is an interface for integer increase only counter.
type Counter interface {
	// Increment increases counter by one.
	Increment()
	// IncrementBy increases counter by given number.
	IncrementBy(num int)
	// Name returns a name of counter.
	Name() string
	// Value returns a current value of counter.
	Value() int64
}

// CounterBox is a main type, it keeps references to all counters
// requested from it.
type CounterBox struct {
	counters map[string]*counterImpl
	min      map[string]*minImpl
	max      map[string]*maxImpl
	m        *sync.RWMutex
}

// NewCounterBox creates a new object to keep all counters.
func NewCounterBox() *CounterBox {
	return &CounterBox{
		counters: make(map[string]*counterImpl),
		min:      make(map[string]*minImpl),
		max:      make(map[string]*maxImpl),
		m:        &sync.RWMutex{},
	}
}

// CreateHttpHandler creates a simple handler printing values of all counters.
func (c *CounterBox) CreateHttpHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		c.m.RLock()
		fmt.Fprintf(w, "Counters %d\n", len(c.counters))
		for k, v := range c.counters {
			fmt.Fprintf(w, "%s=%d\n", k, v.Value())
		}
		fmt.Fprintf(w, "\nMax values %d\n", len(c.max))
		for k, v := range c.max {
			fmt.Fprintf(w, "%s=%d\n", k, v.Value())
		}
		fmt.Fprintf(w, "\nMin values %d\n", len(c.min))
		for k, v := range c.min {
			fmt.Fprintf(w, "%s=%d\n", k, v.Value())
		}
		c.m.RUnlock()
	}
}

// GetCounter returns a counter of given name, if doesn't exist than create.
func (c *CounterBox) GetCounter(name string) Counter {
	c.m.RLock()
	v, ok := c.counters[name]
	c.m.RUnlock()
	if !ok {
		c.m.Lock()
		if v, ok = c.counters[name]; !ok {
			v = &counterImpl{name, 0}
			c.counters[name] = v
		}
		c.m.Unlock()
	}
	return v
}

// GetMin returns a minima counter of given name, if doesn't exist than create.
func (c *CounterBox) GetMin(name string) MaxMinValue {
	c.m.RLock()
	v, ok := c.min[name]
	c.m.RUnlock()
	if !ok {
		c.m.Lock()
		if v, ok = c.min[name]; !ok {
			v = &minImpl{name, 0}
			c.min[name] = v
		}
		c.m.Unlock()
	}
	return v
}

// GetMax returns a maxima counter of given name, if doesn't exist than create.
func (c *CounterBox) GetMax(name string) MaxMinValue {
	c.m.RLock()
	v, ok := c.max[name]
	c.m.RUnlock()
	if !ok {
		c.m.Lock()
		if v, ok = c.max[name]; !ok {
			v = &maxImpl{name, 0}
			c.max[name] = v
		}
		c.m.Unlock()
	}
	return v
}

var tmpl = template.Must(template.New("main").Parse(`== Counters ==
{{- range .Counters}}
  {{.Name}}: {{.Value}}
{{- end}}
== Min values ==
{{- range .Min}}
  {{.Name}}: {{.Value}}
{{- end}}
== Max values ==
{{- range .Max}}
  {{.Name}}: {{.Value}}
{{- end -}}
`))

func (c *CounterBox) WriteTo(w io.Writer) {
	c.m.RLock()
	data := &struct {
		Counters []Counter
		Min      []MaxMinValue
		Max      []MaxMinValue
	}{}
	for _, c := range c.counters {
		data.Counters = append(data.Counters, c)
	}
	for _, c := range c.min {
		data.Min = append(data.Min, c)
	}
	for _, c := range c.max {
		data.Max = append(data.Max, c)
	}
	tmpl.Execute(w, data)
	c.m.RUnlock()
}

func (c *CounterBox) String() string {
	buf := &bytes.Buffer{}
	c.WriteTo(buf)
	return buf.String()
}

type counterImpl struct {
	name  string
	value int64
}

func (c *counterImpl) Increment() {
	atomic.AddInt64(&c.value, 1)
}

func (c *counterImpl) IncrementBy(num int) {
	atomic.AddInt64(&c.value, int64(num))
}

func (c *counterImpl) Name() string {
	return c.name
}

func (c *counterImpl) Value() int64 {
	return atomic.LoadInt64(&c.value)
}

type maxImpl counterImpl

func (m *maxImpl) Set(v int) {
	done := false
	v64 := int64(v)
	for !done {
		if o := atomic.LoadInt64(&m.value); v64 > o {
			done = atomic.CompareAndSwapInt64(&m.value, o, v64)
		} else {
			done = true
		}
	}
}

func (m *maxImpl) Name() string {
	return m.name
}

func (m *maxImpl) Value() int64 {
	return atomic.LoadInt64(&m.value)
}

type minImpl counterImpl

func (m *minImpl) Set(v int) {
	done := false
	v64 := int64(v)
	for !done {
		if o := atomic.LoadInt64(&m.value); v64 < o {
			done = atomic.CompareAndSwapInt64(&m.value, o, v64)
		} else {
			done = true
		}
	}
}

func (m *minImpl) Name() string {
	return m.name
}

func (m *minImpl) Value() int64 {
	return atomic.LoadInt64(&m.value)
}
<commit_msg>sort counters when printing<commit_after>// Package counters provides a simple counter, max and min functionalities.
// All counters are kept in CounterBox.
// Library is thread safe.
package counters

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"sort"
	"strings"
	"sync"
	"sync/atomic"
	"text/template"
)

// MaxMinValue is an interface for minima and maxima counters.
type MaxMinValue interface {
	// Set allows to update value if necessary.
	Set(int)
	// Name returns a name of counter.
	Name() string
	// Value returns a current value.
	Value() int64
}

// Counter is an interface for integer increase only counter.
type Counter interface {
	// Increment increases counter by one.
	Increment()
	// IncrementBy increases counter by given number.
	IncrementBy(num int)
	// Name returns a name of counter.
	Name() string
	// Value returns a current value of counter.
	Value() int64
}

// CounterBox is a main type, it keeps references to all counters
// requested from it.
type CounterBox struct {
	counters map[string]*counterImpl
	min      map[string]*minImpl
	max      map[string]*maxImpl
	m        *sync.RWMutex
}

// NewCounterBox creates a new object to keep all counters.
func NewCounterBox() *CounterBox {
	return &CounterBox{
		counters: make(map[string]*counterImpl),
		min:      make(map[string]*minImpl),
		max:      make(map[string]*maxImpl),
		m:        &sync.RWMutex{},
	}
}

// CreateHttpHandler creates a simple handler printing values of all counters.
func (c *CounterBox) CreateHttpHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		c.m.RLock()
		fmt.Fprintf(w, "Counters %d\n", len(c.counters))
		for k, v := range c.counters {
			fmt.Fprintf(w, "%s=%d\n", k, v.Value())
		}
		fmt.Fprintf(w, "\nMax values %d\n", len(c.max))
		for k, v := range c.max {
			fmt.Fprintf(w, "%s=%d\n", k, v.Value())
		}
		fmt.Fprintf(w, "\nMin values %d\n", len(c.min))
		for k, v := range c.min {
			fmt.Fprintf(w, "%s=%d\n", k, v.Value())
		}
		c.m.RUnlock()
	}
}

// GetCounter returns a counter of given name, if doesn't exist than create.
func (c *CounterBox) GetCounter(name string) Counter {
	c.m.RLock()
	v, ok := c.counters[name]
	c.m.RUnlock()
	if !ok {
		c.m.Lock()
		if v, ok = c.counters[name]; !ok {
			v = &counterImpl{name, 0}
			c.counters[name] = v
		}
		c.m.Unlock()
	}
	return v
}

// GetMin returns a minima counter of given name, if doesn't exist than create.
func (c *CounterBox) GetMin(name string) MaxMinValue {
	c.m.RLock()
	v, ok := c.min[name]
	c.m.RUnlock()
	if !ok {
		c.m.Lock()
		if v, ok = c.min[name]; !ok {
			v = &minImpl{name, 0}
			c.min[name] = v
		}
		c.m.Unlock()
	}
	return v
}

// GetMax returns a maxima counter of given name, if doesn't exist than create.
func (c *CounterBox) GetMax(name string) MaxMinValue {
	c.m.RLock()
	v, ok := c.max[name]
	c.m.RUnlock()
	if !ok {
		c.m.Lock()
		if v, ok = c.max[name]; !ok {
			v = &maxImpl{name, 0}
			c.max[name] = v
		}
		c.m.Unlock()
	}
	return v
}

var tmpl = template.Must(template.New("main").Parse(`== Counters ==
{{- range .Counters}}
  {{.Name}}: {{.Value}}
{{- end}}
== Min values ==
{{- range .Min}}
  {{.Name}}: {{.Value}}
{{- end}}
== Max values ==
{{- range .Max}}
  {{.Name}}: {{.Value}}
{{- end -}}
`))

func (c *CounterBox) WriteTo(w io.Writer) {
	c.m.RLock()
	data := &struct {
		Counters []Counter
		Min      []MaxMinValue
		Max      []MaxMinValue
	}{}
	for _, c := range c.counters {
		data.Counters = append(data.Counters, c)
	}
	for _, c := range c.min {
		data.Min = append(data.Min, c)
	}
	for _, c := range c.max {
		data.Max = append(data.Max, c)
	}
	sort.Slice(data.Counters, func(i, j int) bool { return strings.Compare(data.Counters[i].Name(), data.Counters[j].Name()) < 0 })
	sort.Slice(data.Min, func(i, j int) bool { return strings.Compare(data.Min[i].Name(), data.Min[j].Name()) < 0 })
	sort.Slice(data.Max, func(i, j int) bool { return strings.Compare(data.Max[i].Name(), data.Max[j].Name()) < 0 })
	tmpl.Execute(w, data)
	c.m.RUnlock()
}

func (c *CounterBox) String() string {
	buf := &bytes.Buffer{}
	c.WriteTo(buf)
	return buf.String()
}

type counterImpl struct {
	name  string
	value int64
}

func (c *counterImpl) Increment() {
	atomic.AddInt64(&c.value, 1)
}

func (c *counterImpl) IncrementBy(num int) {
	atomic.AddInt64(&c.value, int64(num))
}

func (c *counterImpl) Name() string {
	return c.name
}

func (c *counterImpl) Value() int64 {
	return atomic.LoadInt64(&c.value)
}

type maxImpl counterImpl

func (m *maxImpl) Set(v int) {
	done := false
	v64 := int64(v)
	for !done {
		if o := atomic.LoadInt64(&m.value); v64 > o {
			done = atomic.CompareAndSwapInt64(&m.value, o, v64)
		} else {
			done = true
		}
	}
}

func (m *maxImpl) Name() string {
	return m.name
}

func (m *maxImpl) Value() int64 {
	return atomic.LoadInt64(&m.value)
}

type minImpl counterImpl

func (m *minImpl) Set(v int) {
	done := false
	v64 := int64(v)
	for !done {
		if o := atomic.LoadInt64(&m.value); v64 < o {
			done = atomic.CompareAndSwapInt64(&m.value, o, v64)
		} else {
			done = true
		}
	}
}

func (m *minImpl) Name() string {
	return m.name
}

func (m *minImpl) Value() int64 {
	return atomic.LoadInt64(&m.value)
}
<|endoftext|>


<commit_before>package sqlite

import (
	"database/sql"
	"os"
	"testing"
)

func TestOpen(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to open database:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("drop table foo")
	_, err = db.Exec("create table foo (id integer)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	if stat, err := os.Stat("./foo.db"); err != nil || stat.IsDir() {
		t.Errorf("Failed to create ./foo.db")
	}
}

func TestInsert(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to open database:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("drop table foo")
	_, err = db.Exec("create table foo (id integer)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	res, err := db.Exec("insert into foo(id) values(123)")
	if err != nil {
		t.Errorf("Failed to insert record:", err)
		return
	}
	affected, _ := res.RowsAffected()
	if affected != 1 {
		t.Errorf("Expected %d for affected rows, but %d:", 1, affected)
		return
	}

	rows, err := db.Query("select id from foo")
	if err != nil {
		t.Errorf("Failed to select records:", err)
		return
	}
	defer rows.Close()

	rows.Next()

	var result int
	rows.Scan(&result)
	if result != 123 {
		t.Errorf("Fetched %q; expected %q", 123, result)
	}
}

func TestUpdate(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to open database:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("drop table foo")
	_, err = db.Exec("create table foo (id integer)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	res, err := db.Exec("insert into foo(id) values(123)")
	if err != nil {
		t.Errorf("Failed to insert record:", err)
		return
	}
	expected, err := res.LastInsertId()
	if err != nil {
		t.Errorf("Failed to get LastInsertId:", err)
		return
	}
	affected, _ := res.RowsAffected()
	if err != nil {
		t.Errorf("Failed to get RowsAffected:", err)
		return
	}
	if affected != 1 {
		t.Errorf("Expected %d for affected rows, but %d:", 1, affected)
		return
	}

	res, err = db.Exec("update foo set id = 234")
	if err != nil {
		t.Errorf("Failed to update record:", err)
		return
	}
	lastId, err := res.LastInsertId()
	if err != nil {
		t.Errorf("Failed to get LastInsertId:", err)
		return
	}
	if expected != lastId {
		t.Errorf("Expected %q for last Id, but %q:", expected, lastId)
	}
	affected, _ = res.RowsAffected()
	if err != nil {
		t.Errorf("Failed to get RowsAffected:", err)
		return
	}
	if affected != 1 {
		t.Errorf("Expected %d for affected rows, but %d:", 1, affected)
		return
	}

	rows, err := db.Query("select id from foo")
	if err != nil {
		t.Errorf("Failed to select records:", err)
		return
	}
	defer rows.Close()

	rows.Next()

	var result int
	rows.Scan(&result)
	if result != 234 {
		t.Errorf("Fetched %q; expected %q", 234, result)
	}
}

func TestDelete(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to select records:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("drop table foo")
	_, err = db.Exec("create table foo (id integer)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	res, err := db.Exec("insert into foo(id) values(123)")
	if err != nil {
		t.Errorf("Failed to insert record:", err)
		return
	}
	expected, err := res.LastInsertId()
	if err != nil {
		t.Errorf("Failed to get LastInsertId:", err)
		return
	}
	affected, err := res.RowsAffected()
	if err != nil {
		t.Errorf("Failed to get RowsAffected:", err)
		return
	}
	if affected != 1 {
		t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected)
	}

	res, err = db.Exec("delete from foo where id = 123")
	if err != nil {
		t.Errorf("Failed to delete record:", err)
		return
	}
	lastId, err := res.LastInsertId()
	if err != nil {
		t.Errorf("Failed to get LastInsertId:", err)
		return
	}
	if expected != lastId {
		t.Errorf("Expected %q for last Id, but %q:", expected, lastId)
	}
	affected, err = res.RowsAffected()
	if err != nil {
		t.Errorf("Failed to get RowsAffected:", err)
		return
	}
	if affected != 1 {
		t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected)
	}

	rows, err := db.Query("select id from foo")
	if err != nil {
		t.Errorf("Failed to select records:", err)
		return
	}
	defer rows.Close()

	if rows.Next() {
		t.Errorf("Fetched row but expected not rows")
	}
}

func TestBooleanRoundtrip(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Tailed to open database:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("DROP TABLE foo")
	_, err = db.Exec("CREATE TABLE foo(id INTEGER, value BOOL)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	_, err = db.Exec("INSERT INTO foo(id, value) VALUES(1, ?)", true)
	if err != nil {
		t.Errorf("Failed to insert true value:", err)
		return
	}

	_, err = db.Exec("INSERT INTO foo(id, value) VALUES(2, ?)", false)
	if err != nil {
		t.Errorf("Failed to insert false value:", err)
		return
	}

	rows, err := db.Query("SELECT id, value FROM foo")
	if err != nil {
		t.Errorf("Unable to query foo table:", err)
		return
	}

	for rows.Next() {
		var id int
		var value bool

		if err := rows.Scan(&id, &value); err != nil {
			t.Errorf("Unable to scan results:", err)
			continue
		}

		if id == 1 && !value {
			t.Errorf("Value for id 1 should be true, not false")

		} else if id == 2 && value {
			t.Errorf("Value for id 2 should be false, not true")
		}
	}
}
<commit_msg>Fix typo (Tailed -> Failed).<commit_after>package sqlite

import (
	"database/sql"
	"os"
	"testing"
)

func TestOpen(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to open database:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("drop table foo")
	_, err = db.Exec("create table foo (id integer)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	if stat, err := os.Stat("./foo.db"); err != nil || stat.IsDir() {
		t.Errorf("Failed to create ./foo.db")
	}
}

func TestInsert(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to open database:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("drop table foo")
	_, err = db.Exec("create table foo (id integer)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	res, err := db.Exec("insert into foo(id) values(123)")
	if err != nil {
		t.Errorf("Failed to insert record:", err)
		return
	}
	affected, _ := res.RowsAffected()
	if affected != 1 {
		t.Errorf("Expected %d for affected rows, but %d:", 1, affected)
		return
	}

	rows, err := db.Query("select id from foo")
	if err != nil {
		t.Errorf("Failed to select records:", err)
		return
	}
	defer rows.Close()

	rows.Next()

	var result int
	rows.Scan(&result)
	if result != 123 {
		t.Errorf("Fetched %q; expected %q", 123, result)
	}
}

func TestUpdate(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to open database:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("drop table foo")
	_, err = db.Exec("create table foo (id integer)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	res, err := db.Exec("insert into foo(id) values(123)")
	if err != nil {
		t.Errorf("Failed to insert record:", err)
		return
	}
	expected, err := res.LastInsertId()
	if err != nil {
		t.Errorf("Failed to get LastInsertId:", err)
		return
	}
	affected, _ := res.RowsAffected()
	if err != nil {
		t.Errorf("Failed to get RowsAffected:", err)
		return
	}
	if affected != 1 {
		t.Errorf("Expected %d for affected rows, but %d:", 1, affected)
		return
	}

	res, err = db.Exec("update foo set id = 234")
	if err != nil {
		t.Errorf("Failed to update record:", err)
		return
	}
	lastId, err := res.LastInsertId()
	if err != nil {
		t.Errorf("Failed to get LastInsertId:", err)
		return
	}
	if expected != lastId {
		t.Errorf("Expected %q for last Id, but %q:", expected, lastId)
	}
	affected, _ = res.RowsAffected()
	if err != nil {
		t.Errorf("Failed to get RowsAffected:", err)
		return
	}
	if affected != 1 {
		t.Errorf("Expected %d for affected rows, but %d:", 1, affected)
		return
	}

	rows, err := db.Query("select id from foo")
	if err != nil {
		t.Errorf("Failed to select records:", err)
		return
	}
	defer rows.Close()

	rows.Next()

	var result int
	rows.Scan(&result)
	if result != 234 {
		t.Errorf("Fetched %q; expected %q", 234, result)
	}
}

func TestDelete(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to select records:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("drop table foo")
	_, err = db.Exec("create table foo (id integer)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	res, err := db.Exec("insert into foo(id) values(123)")
	if err != nil {
		t.Errorf("Failed to insert record:", err)
		return
	}
	expected, err := res.LastInsertId()
	if err != nil {
		t.Errorf("Failed to get LastInsertId:", err)
		return
	}
	affected, err := res.RowsAffected()
	if err != nil {
		t.Errorf("Failed to get RowsAffected:", err)
		return
	}
	if affected != 1 {
		t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected)
	}

	res, err = db.Exec("delete from foo where id = 123")
	if err != nil {
		t.Errorf("Failed to delete record:", err)
		return
	}
	lastId, err := res.LastInsertId()
	if err != nil {
		t.Errorf("Failed to get LastInsertId:", err)
		return
	}
	if expected != lastId {
		t.Errorf("Expected %q for last Id, but %q:", expected, lastId)
	}
	affected, err = res.RowsAffected()
	if err != nil {
		t.Errorf("Failed to get RowsAffected:", err)
		return
	}
	if affected != 1 {
		t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected)
	}

	rows, err := db.Query("select id from foo")
	if err != nil {
		t.Errorf("Failed to select records:", err)
		return
	}
	defer rows.Close()

	if rows.Next() {
		t.Errorf("Fetched row but expected not rows")
	}
}

func TestBooleanRoundtrip(t *testing.T) {
	db, err := sql.Open("sqlite3", "./foo.db")
	if err != nil {
		t.Errorf("Failed to open database:", err)
		return
	}
	defer os.Remove("./foo.db")

	_, err = db.Exec("DROP TABLE foo")
	_, err = db.Exec("CREATE TABLE foo(id INTEGER, value BOOL)")
	if err != nil {
		t.Errorf("Failed to create table:", err)
		return
	}

	_, err = db.Exec("INSERT INTO foo(id, value) VALUES(1, ?)", true)
	if err != nil {
		t.Errorf("Failed to insert true value:", err)
		return
	}

	_, err = db.Exec("INSERT INTO foo(id, value) VALUES(2, ?)", false)
	if err != nil {
		t.Errorf("Failed to insert false value:", err)
		return
	}

	rows, err := db.Query("SELECT id, value FROM foo")
	if err != nil {
		t.Errorf("Unable to query foo table:", err)
		return
	}

	for rows.Next() {
		var id int
		var value bool

		if err := rows.Scan(&id, &value); err != nil {
			t.Errorf("Unable to scan results:", err)
			continue
		}

		if id == 1 && !value {
			t.Errorf("Value for id 1 should be true, not false")

		} else if id == 2 && value {
			t.Errorf("Value for id 2 should be false, not true")
		}
	}
}
<|endoftext|>


<commit_before>package idxfile

import (
	"bufio"
	"bytes"
	"errors"
	"io"

	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/utils/binary"
)

var (
	// ErrUnsupportedVersion is returned by Decode when the idx file version
	// is not supported.
	ErrUnsupportedVersion = errors.New("Unsuported version")
	// ErrMalformedIdxFile is returned by Decode when the idx file is corrupted.
	ErrMalformedIdxFile = errors.New("Malformed IDX file")
)

// Decoder reads and decodes idx files from an input stream.
type Decoder struct {
	*bufio.Reader
}

// NewDecoder builds a new idx stream decoder, that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{bufio.NewReader(r)}
}

// Decode reads from the stream and decode the content into the Idxfile struct.
func (d *Decoder) Decode(idx *Idxfile) error {
	if err := validateHeader(d); err != nil {
		return err
	}

	flow := []func(*Idxfile, io.Reader) error{
		readVersion,
		readFanout,
		readObjectNames,
		readCRC32,
		readOffsets,
		readChecksums,
	}

	for _, f := range flow {
		if err := f(idx, d); err != nil {
			return err
		}
	}

	if !idx.isValid() {
		return ErrMalformedIdxFile
	}

	return nil
}

func validateHeader(r io.Reader) error {
	var h = make([]byte, 4)
	if _, err := io.ReadFull(r, h); err != nil {
		return err
	}

	if !bytes.Equal(h, idxHeader) {
		return ErrMalformedIdxFile
	}

	return nil
}

func readVersion(idx *Idxfile, r io.Reader) error {
	v, err := binary.ReadUint32(r)
	if err != nil {
		return err
	}

	if v > VersionSupported {
		return ErrUnsupportedVersion
	}

	idx.Version = v
	return nil
}

func readFanout(idx *Idxfile, r io.Reader) error {
	var err error
	for i := 0; i < 255; i++ {
		idx.Fanout[i], err = binary.ReadUint32(r)
		if err != nil {
			return err
		}
	}

	idx.ObjectCount, err = binary.ReadUint32(r)
	return err
}

func readObjectNames(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)
	for i := 0; i < c; i++ {
		var ref plumbing.Hash
		if _, err := io.ReadFull(r, ref[:]); err != nil {
			return err
		}

		idx.Entries = append(idx.Entries, &Entry{Hash: ref})
	}

	return nil
}

func readCRC32(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)
	for i := 0; i < c; i++ {
		if err := binary.Read(r, &idx.Entries[i].CRC32); err != nil {
			return err
		}
	}

	return nil
}

func readOffsets(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)

	for i := 0; i < c; i++ {
		o, err := binary.ReadUint32(r)
		if err != nil {
			return err
		}

		idx.Entries[i].Offset = uint64(o)
	}

	for i := 0; i < c; i++ {
		if idx.Entries[i].Offset <= offsetLimit {
			continue
		}

		o, err := binary.ReadUint64(r)
		if err != nil {
			return err
		}

		idx.Entries[i].Offset = o
	}

	return nil
}

func readChecksums(idx *Idxfile, r io.Reader) error {
	if _, err := io.ReadFull(r, idx.PackfileChecksum[:]); err != nil {
		return err
	}

	if _, err := io.ReadFull(r, idx.IdxChecksum[:]); err != nil {
		return err
	}

	return nil
}
<commit_msg>idxfile: optimise allocations in readObjectNames<commit_after>package idxfile

import (
	"bufio"
	"bytes"
	"errors"
	"io"

	"gopkg.in/src-d/go-git.v4/utils/binary"
)

var (
	// ErrUnsupportedVersion is returned by Decode when the idx file version
	// is not supported.
	ErrUnsupportedVersion = errors.New("Unsuported version")
	// ErrMalformedIdxFile is returned by Decode when the idx file is corrupted.
	ErrMalformedIdxFile = errors.New("Malformed IDX file")
)

// Decoder reads and decodes idx files from an input stream.
type Decoder struct {
	*bufio.Reader
}

// NewDecoder builds a new idx stream decoder, that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{bufio.NewReader(r)}
}

// Decode reads from the stream and decode the content into the Idxfile struct.
func (d *Decoder) Decode(idx *Idxfile) error {
	if err := validateHeader(d); err != nil {
		return err
	}

	flow := []func(*Idxfile, io.Reader) error{
		readVersion,
		readFanout,
		readObjectNames,
		readCRC32,
		readOffsets,
		readChecksums,
	}

	for _, f := range flow {
		if err := f(idx, d); err != nil {
			return err
		}
	}

	if !idx.isValid() {
		return ErrMalformedIdxFile
	}

	return nil
}

func validateHeader(r io.Reader) error {
	var h = make([]byte, 4)
	if _, err := io.ReadFull(r, h); err != nil {
		return err
	}

	if !bytes.Equal(h, idxHeader) {
		return ErrMalformedIdxFile
	}

	return nil
}

func readVersion(idx *Idxfile, r io.Reader) error {
	v, err := binary.ReadUint32(r)
	if err != nil {
		return err
	}

	if v > VersionSupported {
		return ErrUnsupportedVersion
	}

	idx.Version = v
	return nil
}

func readFanout(idx *Idxfile, r io.Reader) error {
	var err error
	for i := 0; i < 255; i++ {
		idx.Fanout[i], err = binary.ReadUint32(r)
		if err != nil {
			return err
		}
	}

	idx.ObjectCount, err = binary.ReadUint32(r)
	return err
}

func readObjectNames(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)
	new := make([]Entry, c)
	for i := 0; i < c; i++ {
		e := &new[i]
		if _, err := io.ReadFull(r, e.Hash[:]); err != nil {
			return err
		}

		idx.Entries = append(idx.Entries, e)
	}

	return nil
}

func readCRC32(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)
	for i := 0; i < c; i++ {
		if err := binary.Read(r, &idx.Entries[i].CRC32); err != nil {
			return err
		}
	}

	return nil
}

func readOffsets(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)

	for i := 0; i < c; i++ {
		o, err := binary.ReadUint32(r)
		if err != nil {
			return err
		}

		idx.Entries[i].Offset = uint64(o)
	}

	for i := 0; i < c; i++ {
		if idx.Entries[i].Offset <= offsetLimit {
			continue
		}

		o, err := binary.ReadUint64(r)
		if err != nil {
			return err
		}

		idx.Entries[i].Offset = o
	}

	return nil
}

func readChecksums(idx *Idxfile, r io.Reader) error {
	if _, err := io.ReadFull(r, idx.PackfileChecksum[:]); err != nil {
		return err
	}

	if _, err := io.ReadFull(r, idx.IdxChecksum[:]); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package registry

import (
	"encoding/json"
	"fmt"
	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution"
	"github.com/docker/distribution/registry/client"
	"github.com/docker/docker/reference"
	"github.com/docker/docker/registry"
	"github.com/docker/engine-api/types"
	imageParser "github.com/docker/engine-api/types/reference"
	"github.com/nhurel/dim/lib/utils"
	t "github.com/nhurel/dim/types"
	"golang.org/x/net/context"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
	"text/tabwriter"
	"text/template"
)

// Client defines method to interact with a registry
type Client interface {
	client.Registry
	NewRepository(parsedName reference.Named) (Repository, error)
	Search(query, advanced string) error
	WalkRepositories(repositories chan<- Repository) error
	PrintImageInfo(out io.Writer, parsedName reference.Named, tpl *template.Template) error
	DeleteImage(parsedName reference.Named) error
}

// RegistryClient implements Client interface
type registryClient struct {
	client.Registry
	transport   http.RoundTripper
	registryURL string
}

var ctx = context.Background()

// New creates a registry client. Handles getting right credentials from user
func New(registryAuth *types.AuthConfig, registryURL string) (Client, error) {
	var err error
	var reg client.Registry

	if registryURL == "" {
		return nil, fmt.Errorf("No registry URL given")
	}

	transport := http.DefaultTransport

	if registryAuth != nil {
		transport = registry.AuthTransport(transport, registryAuth, true)
	}

	if reg, err = client.NewRegistry(ctx, registryURL, transport); err != nil {
		return nil, err
	}

	repos := make([]string, 1)
	for _, err = reg.Repositories(ctx, repos, ""); err != nil && err != io.EOF; _, err = reg.Repositories(ctx, repos, "") {
		logrus.Debugln("Prompting for credentials")
		if registryAuth == nil {
			registryAuth = &types.AuthConfig{}
		}
		utils.ReadCredentials(registryAuth)
		transport = registry.AuthTransport(transport, registryAuth, true)
		if reg, err = client.NewRegistry(ctx, registryURL, transport); err != nil {
			return nil, err
		}
	}

	return &registryClient{reg, transport, registryURL}, nil
}

// NewRepository creates a Repository object to query the registry about a specific repository
func (c *registryClient) NewRepository(parsedName reference.Named) (Repository, error) {
	logrus.WithField("name", parsedName).Debugln("Creating new repository")

	var repo distribution.Repository
	var err error
	if repo, err = client.NewRepository(ctx, parsedName, c.registryURL, c.transport); err != nil {
		return &registryRepository{}, err
	}

	return &registryRepository{Repository: repo, client: c}, nil
}

// Search runs a search against the registry, handling dim advanced querying option
func (c *registryClient) Search(query, advanced string) error {
	q := strings.TrimSpace(query)
	a := strings.TrimSpace(advanced)
	var err error

	var resp *http.Response

	values := url.Values{}
	if a != "" {
		values.Set("a", a)
	}
	if q != "" {
		values.Set("q", q)
	}
	values.Set("f", "full")

	http.PostForm(strings.Join([]string{c.registryURL, "/v1/search"}, ""), values)

	//FIXME  : Use http.PostForm("url", url.Values{"q": query, "a":advanced}) instead

	httpClient := http.Client{Transport: c.transport}
	if resp, err = httpClient.PostForm(strings.Join([]string{c.registryURL, "/v1/search"}, ""), values); err != nil {
		return fmt.Errorf("Failed to send request : %v", err)
	}
	defer resp.Body.Close()
	var b []byte
	if resp.StatusCode >= 200 && resp.StatusCode < 400 {
		results := &t.SearchResults{}
		if err := json.NewDecoder(resp.Body).Decode(results); err != nil {
			return fmt.Errorf("Failed to parse response : %v", err)
		}

		funcMap := template.FuncMap{
			"flatMap": flatMap,
		}
		tpl, _ := template.New("search").Funcs(funcMap).Parse(searchResultTemplate)

		w := tabwriter.NewWriter(os.Stdout, 8, 8, 8, ' ', 0)
		if err = tpl.Execute(w, results); err != nil {
			logrus.WithError(err).Errorln("Failed to parse template")
		}
		w.Flush()

	} else {
		b, _ = ioutil.ReadAll(resp.Body)
		return fmt.Errorf("Server returned an error : %s", string(b))
	}
	return nil

}

func flatMap(m map[string]string) string {
	if m == nil || len(m) == 0 {
		return ""
	}
	entries := make([]string, 0, len(m))
	for k, v := range m {
		entries = append(entries, fmt.Sprintf("%s=%s", k, v))
	}
	sort.Sort(Alphabetical(entries))
	return strings.Join(entries, ", ")
}

// Alphabetical is a slice of string that can be sorted by alphabetical order
type Alphabetical []string

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

const searchResultTemplate = `
{{- if gt .NumResults 0 -}}
{{.NumResults}} Results found :
Name	Tag	Labels	Volumes
{{ range $i, $r := .Results}} {{- $r.Name}}	{{$r.Tag}}	{{$r.Label | flatMap}}	{{if $r.Volumes}}{{$r.Volumes}}{{end}}
{{end}}
{{else -}}No result found
{{end -}}
`

// WalkRepositories walks through all repositories and send them in the given channel
func (c *registryClient) WalkRepositories(repositories chan<- Repository) error {
	return WalkRepositories(c, repositories)
}

// WalkRepositories walks through all repositories and ssend them in the given channel
func WalkRepositories(c Client, repositories chan<- Repository) error {
	var err error

	var n int
	registries := make([]string, 20)
	defer close(repositories)
	last := ""
	for stop := false; !stop; {

		if n, err = c.Repositories(nil, registries, last); err != nil && err != io.EOF {
			logrus.WithField("n", n).WithError(err).Errorln("Failed to get repostories")
			return err
		}

		stop = (err == io.EOF)

		for i := 0; i < n; i++ {
			last = registries[i]

			var parsedName reference.Named

			l := logrus.WithField("repository", last)
			l.Infoln("Indexing repository")
			if parsedName, err = reference.ParseNamed(last); err != nil {
				logrus.WithError(err).WithField("name", last).Errorln("Failed to parse repository name")
				continue
			}

			var repository Repository

			if repository, err = c.NewRepository(parsedName); err != nil {
				logrus.WithError(err).WithField("name", last).Errorln("Failed to fetch repository info")
				continue
			}
			repositories <- repository
		}

	}
	return nil

}

// PrintImageInfo prints the info about an image available on the remote registry
func (c *registryClient) PrintImageInfo(w io.Writer, parsedName reference.Named, tpl *template.Template) error {
	var repository Repository
	var err error
	name, _ := reference.ParseNamed(parsedName.Name()[strings.Index(parsedName.Name(), "/")+1:])
	if repository, err = c.NewRepository(name); err != nil {
		logrus.WithError(err).Errorln("Failed to fetch repository info")
		return err
	}

	var tag string
	switch parsedName := parsedName.(type) {
	case reference.NamedTagged:
		tag = parsedName.Tag()
	default:
		tag = "latest"
	}

	var image *Image
	if image, err = repository.Image(tag); err != nil {
		logrus.WithError(err).Errorln("Failed to fetch image info")
		return err
	}

	info := &types.ImageInspect{
		RepoTags: []string{image.Tag},
		ID:       image.ImageID(),
		Config:   image.Config,
	}

	return tpl.Execute(w, info)
}

// DeleteImage deletes the image on the remote registry
func (c *registryClient) DeleteImage(parsedName reference.Named) error {
	logrus.WithField("parsedName", parsedName.String()).Debugln("Entering DeleteImage")
	var repo Repository
	var err error
	name, _ := reference.ParseNamed(parsedName.Name()[strings.Index(parsedName.Name(), "/")+1:])
	if repo, err = c.NewRepository(name); err != nil {
		return err
	}

	tag := imageParser.GetTagFromNamedRef(parsedName)

	if tag == "" {
		tag = "latest"
	}

	logrus.Debugln("Deleting image")
	if err = repo.DeleteImage(tag); err != nil {
		logrus.WithError(err).Errorln("Failed to delete image on the remote registry")
		return err
	}

	return nil
}
<commit_msg>code cleanup<commit_after>package registry

import (
	"encoding/json"
	"fmt"
	"github.com/Sirupsen/logrus"
	"github.com/docker/distribution"
	"github.com/docker/distribution/registry/client"
	"github.com/docker/docker/reference"
	"github.com/docker/docker/registry"
	"github.com/docker/engine-api/types"
	imageParser "github.com/docker/engine-api/types/reference"
	"github.com/nhurel/dim/lib/utils"
	t "github.com/nhurel/dim/types"
	"golang.org/x/net/context"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
	"text/tabwriter"
	"text/template"
)

// Client defines method to interact with a registry
type Client interface {
	client.Registry
	NewRepository(parsedName reference.Named) (Repository, error)
	Search(query, advanced string) error
	WalkRepositories(repositories chan<- Repository) error
	PrintImageInfo(out io.Writer, parsedName reference.Named, tpl *template.Template) error
	DeleteImage(parsedName reference.Named) error
}

// RegistryClient implements Client interface
type registryClient struct {
	client.Registry
	transport   http.RoundTripper
	registryURL string
}

var ctx = context.Background()

// New creates a registry client. Handles getting right credentials from user
func New(registryAuth *types.AuthConfig, registryURL string) (Client, error) {
	var err error
	var reg client.Registry

	if registryURL == "" {
		return nil, fmt.Errorf("No registry URL given")
	}

	transport := http.DefaultTransport

	if registryAuth != nil {
		transport = registry.AuthTransport(transport, registryAuth, true)
	}

	if reg, err = client.NewRegistry(ctx, registryURL, transport); err != nil {
		return nil, err
	}

	repos := make([]string, 1)
	for _, err = reg.Repositories(ctx, repos, ""); err != nil && err != io.EOF; _, err = reg.Repositories(ctx, repos, "") {
		logrus.Debugln("Prompting for credentials")
		if registryAuth == nil {
			registryAuth = &types.AuthConfig{}
		}
		utils.ReadCredentials(registryAuth)
		transport = registry.AuthTransport(transport, registryAuth, true)
		if reg, err = client.NewRegistry(ctx, registryURL, transport); err != nil {
			return nil, err
		}
	}

	return &registryClient{reg, transport, registryURL}, nil
}

// NewRepository creates a Repository object to query the registry about a specific repository
func (c *registryClient) NewRepository(parsedName reference.Named) (Repository, error) {
	logrus.WithField("name", parsedName).Debugln("Creating new repository")

	var repo distribution.Repository
	var err error
	if repo, err = client.NewRepository(ctx, parsedName, c.registryURL, c.transport); err != nil {
		return &registryRepository{}, err
	}

	return &registryRepository{Repository: repo, client: c}, nil
}

// Search runs a search against the registry, handling dim advanced querying option
func (c *registryClient) Search(query, advanced string) error {
	q := strings.TrimSpace(query)
	a := strings.TrimSpace(advanced)
	var err error

	var resp *http.Response

	values := url.Values{}
	if a != "" {
		values.Set("a", a)
	}
	if q != "" {
		values.Set("q", q)
	}
	values.Set("f", "full")

	httpClient := http.Client{Transport: c.transport}
	if resp, err = httpClient.PostForm(strings.Join([]string{c.registryURL, "/v1/search"}, ""), values); err != nil {
		return fmt.Errorf("Failed to send request : %v", err)
	}
	defer resp.Body.Close()
	var b []byte
	if resp.StatusCode >= 200 && resp.StatusCode < 400 {
		results := &t.SearchResults{}
		if err := json.NewDecoder(resp.Body).Decode(results); err != nil {
			return fmt.Errorf("Failed to parse response : %v", err)
		}

		funcMap := template.FuncMap{
			"flatMap": flatMap,
		}
		tpl, _ := template.New("search").Funcs(funcMap).Parse(searchResultTemplate)

		w := tabwriter.NewWriter(os.Stdout, 8, 8, 8, ' ', 0)
		if err = tpl.Execute(w, results); err != nil {
			logrus.WithError(err).Errorln("Failed to parse template")
		}
		w.Flush()

	} else {
		b, _ = ioutil.ReadAll(resp.Body)
		return fmt.Errorf("Server returned an error : %s", string(b))
	}
	return nil

}

func flatMap(m map[string]string) string {
	if m == nil || len(m) == 0 {
		return ""
	}
	entries := make([]string, 0, len(m))
	for k, v := range m {
		entries = append(entries, fmt.Sprintf("%s=%s", k, v))
	}
	sort.Sort(Alphabetical(entries))
	return strings.Join(entries, ", ")
}

// Alphabetical is a slice of string that can be sorted by alphabetical order
type Alphabetical []string

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

const searchResultTemplate = `
{{- if gt .NumResults 0 -}}
{{.NumResults}} Results found :
Name	Tag	Labels	Volumes
{{ range $i, $r := .Results}} {{- $r.Name}}	{{$r.Tag}}	{{$r.Label | flatMap}}	{{if $r.Volumes}}{{$r.Volumes}}{{end}}
{{end}}
{{else -}}No result found
{{end -}}
`

// WalkRepositories walks through all repositories and send them in the given channel
func (c *registryClient) WalkRepositories(repositories chan<- Repository) error {
	return WalkRepositories(c, repositories)
}

// WalkRepositories walks through all repositories and ssend them in the given channel
func WalkRepositories(c Client, repositories chan<- Repository) error {
	var err error

	var n int
	registries := make([]string, 20)
	defer close(repositories)
	last := ""
	for stop := false; !stop; {

		if n, err = c.Repositories(nil, registries, last); err != nil && err != io.EOF {
			logrus.WithField("n", n).WithError(err).Errorln("Failed to get repostories")
			return err
		}

		stop = (err == io.EOF)

		for i := 0; i < n; i++ {
			last = registries[i]

			var parsedName reference.Named

			l := logrus.WithField("repository", last)
			l.Infoln("Indexing repository")
			if parsedName, err = reference.ParseNamed(last); err != nil {
				logrus.WithError(err).WithField("name", last).Errorln("Failed to parse repository name")
				continue
			}

			var repository Repository

			if repository, err = c.NewRepository(parsedName); err != nil {
				logrus.WithError(err).WithField("name", last).Errorln("Failed to fetch repository info")
				continue
			}
			repositories <- repository
		}

	}
	return nil

}

// PrintImageInfo prints the info about an image available on the remote registry
func (c *registryClient) PrintImageInfo(w io.Writer, parsedName reference.Named, tpl *template.Template) error {
	var repository Repository
	var err error
	name, _ := reference.ParseNamed(parsedName.Name()[strings.Index(parsedName.Name(), "/")+1:])
	if repository, err = c.NewRepository(name); err != nil {
		logrus.WithError(err).Errorln("Failed to fetch repository info")
		return err
	}

	var tag string
	switch parsedName := parsedName.(type) {
	case reference.NamedTagged:
		tag = parsedName.Tag()
	default:
		tag = "latest"
	}

	var image *Image
	if image, err = repository.Image(tag); err != nil {
		logrus.WithError(err).Errorln("Failed to fetch image info")
		return err
	}

	info := &types.ImageInspect{
		RepoTags: []string{image.Tag},
		ID:       image.ImageID(),
		Config:   image.Config,
	}

	return tpl.Execute(w, info)
}

// DeleteImage deletes the image on the remote registry
func (c *registryClient) DeleteImage(parsedName reference.Named) error {
	logrus.WithField("parsedName", parsedName.String()).Debugln("Entering DeleteImage")
	var repo Repository
	var err error
	name, _ := reference.ParseNamed(parsedName.Name()[strings.Index(parsedName.Name(), "/")+1:])
	if repo, err = c.NewRepository(name); err != nil {
		return err
	}

	tag := imageParser.GetTagFromNamedRef(parsedName)

	if tag == "" {
		tag = "latest"
	}

	logrus.Debugln("Deleting image")
	if err = repo.DeleteImage(tag); err != nil {
		logrus.WithError(err).Errorln("Failed to delete image on the remote registry")
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package ctx

import (
	"fmt"
	"net"
	"os"
)

func Hostname() string {
	ensureLogLoaded()
	return conf.hostname
}

func LogLevel() string {
	ensureLogLoaded()
	return conf.logLevel
}

func Zones() map[string]string {
	ensureLogLoaded()
	return conf.zones
}

func Tunnels() map[string]string {
	ensureLogLoaded()
	return conf.tunnels
}

func KafkaHome() string {
	ensureLogLoaded()
	return conf.kafkaHome
}

func InfluxdbHost() string {
	ensureLogLoaded()
	return conf.influxdbHost
}

func SortedZones() []string {
	ensureLogLoaded()
	return conf.sortedZones()
}

func ZoneZkAddrs(zone string) (zkAddrs string) {
	ensureLogLoaded()
	var present bool
	if zkAddrs, present = conf.zones[zone]; present {
		return
	}

	// should never happen
	fmt.Printf("zone[%s] undefined", zone)
	os.Exit(1)
	return ""
}

// LocalIP tries to determine a non-loopback address for the local machine
func LocalIP() (net.IP, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}
	for _, addr := range addrs {
		if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() {
			if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil {
				return ipnet.IP, nil
			}
		}
	}
	return nil, nil
}
<commit_msg>a helper func to judge whether current user is root<commit_after>package ctx

import (
	"fmt"
	"net"
	"os"
	"os/user"
)

func Hostname() string {
	ensureLogLoaded()
	return conf.hostname
}

func LogLevel() string {
	ensureLogLoaded()
	return conf.logLevel
}

func Zones() map[string]string {
	ensureLogLoaded()
	return conf.zones
}

func Tunnels() map[string]string {
	ensureLogLoaded()
	return conf.tunnels
}

func KafkaHome() string {
	ensureLogLoaded()
	return conf.kafkaHome
}

func InfluxdbHost() string {
	ensureLogLoaded()
	return conf.influxdbHost
}

func SortedZones() []string {
	ensureLogLoaded()
	return conf.sortedZones()
}

func ZoneZkAddrs(zone string) (zkAddrs string) {
	ensureLogLoaded()
	var present bool
	if zkAddrs, present = conf.zones[zone]; present {
		return
	}

	// should never happen
	fmt.Printf("zone[%s] undefined", zone)
	os.Exit(1)
	return ""
}

// LocalIP tries to determine a non-loopback address for the local machine
func LocalIP() (net.IP, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return nil, err
	}
	for _, addr := range addrs {
		if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() {
			if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil {
				return ipnet.IP, nil
			}
		}
	}
	return nil, nil
}

func CurrentUserIsRoot() bool {
	user, err := user.Current()
	if err != nil {
		return false
	}

	return user.Uid == "0"
}
<|endoftext|>