<commit_before>package common

// App is the main top-level Resource within Supergiant, acting as a logical
// namespace for Components and all of their controlled assets -- along with
// provisioning an actual Kubernetes Namespace, it is used as a base name for
// app-specific cloud assets.
type App struct {
	Name ID `json:"name" validate:"nonzero,max=24,regexp=^[a-z]([-a-z0-9]*[a-z0-9])?$"`
	*Meta
}

type Component struct {
	Name ID `json:"name" validate:"nonzero,max=24,regexp=^[a-z]([-a-z0-9]*[a-z0-9])?$"`

	CustomDeployScript *CustomDeployScript `json:"custom_deploy_script"`

	CurrentReleaseTimestamp ID `json:"current_release_id" sg:"readonly"` // TODO this should be release_timestamp, not release_id
	TargetReleaseTimestamp  ID `json:"target_release_id" sg:"readonly"`

	Addresses *Addresses `json:"addresses,omitempty" sg:"readonly,nostore"`

	*Meta
}

// NOTE the word Blueprint is used for Volumes and Containers, since they are
// both "definitions" that create "instances" of the real thing
type Release struct {
	// NOTE Timestamp here does not use the Timestamp type.
	Timestamp ID `json:"timestamp"`

	// TODO
	// InstanceGroup is used as a labeling mechanism for instances. If nil,
	// InstanceGroup is set equal to the release's Timestamp. If a value is
	// supplied by the user, it MUST be the current (previous) Release's
	// Timestamp.
	//
	// The purpose of InstanceGroup is to prevent restarting between Releases.
	// I'm pretty sure the ONLY scenario in which this value makes sense is when
	// changing InstanceCount, and the value supplied in such a scenario must be
	// the previous Release's timestamp.
	//
	// It seems as though we need to break deploys up into:
	//   - config changes
	//	 - adding/removing instances
	//
	// It might still makes sense to have Release as a grouping mechanism, though,
	// because it could allow for grouping metrics recorded per-Release. However,
	// you may be able to separate the operations, but have every operation create
	// a new record that records the config / instance count at that time.
	InstanceGroup ID `json:"instance_group"`

	InstanceCount int `json:"instance_count" validate:"min=1" sg:"default=1"`

	// These attributes, when changed from last Release, indicate a restart is
	// needed (or just new instances through other means).
	Volumes                []*VolumeBlueprint    `json:"volumes"`
	Containers             []*ContainerBlueprint `json:"containers" validate:"min=1"`
	TerminationGracePeriod int                   `json:"termination_grace_period" validate:"min=0" sg:"default=10"`

	// Retired defines whether or not a Release still has active assets, like pods
	// or services. When retired is true, we skip attempting to delete assets.
	Retired bool `json:"retired" sg:"readonly"`

	// Committed defines whether or not a Release is being / has been deployed.
	Committed bool `json:"committed" sg:"readonly"`

	*Meta
}

// NOTE Instances are not stored in etcd, so the json tags here apply to HTTP
type Instance struct {
	ID ID `json:"id"` // actually just the number (starting w/ 1) of the instance order in the release

	// BaseName is the name of the instance without the Release ID appended. It is
	// used for naming volumes, which move between releases.
	BaseName string `json:"base_name"`
	Name     string `json:"name"`

	Status string `json:"status"`

	CPU *ResourceMetrics `json:"cpu"`
	RAM *ResourceMetrics `json:"ram"`

	Addresses *Addresses `json:"addresses,omitempty"`
}

type Entrypoint struct {
	// NOTE eventually the plan for Domain is to use it for DNS application.
	// That's not hooked up yet, and thus, we aren't doing any type of validation
	// on hostnames.
	Domain ID `json:"domain" validate:"nonzero,max=21"` // e.g. test.example.com

	// the ELB address
	Address string `json:"address" sg:"readonly"`

	// NOTE we actually don't need this -- we can always attach the policy, and enable per port
	// IPWhitelistEnabled bool   `json:"ip_whitelist_enabled"`

	*Meta
}

type Task struct {
	ID         ID     `json:"id"`
	ActionData string `json:"action_data" validate:"nonzero"`

	MaxAttempts int `json:"max_attempts" validate:"min=1" sg:"default=10"`

	Status   string `json:"status" sg:"readonly"`
	Attempts int    `json:"attempts" sg:"readonly"`
	Error    string `json:"error" sg:"readonly"`

	*Meta
}

type ImageRegistry struct {
	Name ID `json:"name"`

	// more to come soon... it's just Dockerhub for now

	*Meta
}

// TODO this should really be renamed to Org probably
type ImageRepo struct {
	Name       ID     `json:"name" validate:"nonzero"`
	Key        string `json:"key" validate:"nonzero" sg:"private"`
	SecretType string `json:"secret_type" validate:"regexp=^(dockercfg|dockerconfigjson)$" sg:"default=dockercfg"`

	*Meta
}

type Node struct {
	ID   ID     `json:"id"`
	Name string `json:"name" sg:"readonly"`
	// This is the only input for Node
	Class      string `json:"class" validate:"nonzero"`
	ExternalIP string `json:"external_ip" sg:"readonly"`

	// LaunchTime time.Time `json:"-"`
	// ServerUptime int              `json:"server_uptime" db:"-"`

	ProviderCreationTimestamp *Timestamp `json:"provider_creation_timestamp" sg:"readonly"`

	OutOfDisk bool             `json:"out_of_disk" sg:"readonly,nostore"`
	Status    string           `json:"status" sg:"readonly,nostore"`
	CPU       *ResourceMetrics `json:"cpu" sg:"readonly,nostore"`
	RAM       *ResourceMetrics `json:"ram" sg:"readonly,nostore"`

	*Meta
}

//==============================================================================

// ID is defined as a string pointer in order to check for nil in the context of
// relations. NOTE this may not be best practice.
type ID *string

// Tags is a meta field for holding unstructued key/val info.
type Tags map[string]string

// Meta is a set of fields on all Resources for holding metadata.
type Meta struct {
	Created *Timestamp `json:"created" sg:"readonly"`
	Updated *Timestamp `json:"updated" sg:"readonly"`
	Tags    Tags       `json:"tags"`
}

type PortAddress struct {
	Port    string `json:"port"` // TODO really this should be the name of the port, which currently is the string of the number
	Address string `json:"address"`
}

type Addresses struct {
	External []*PortAddress `json:"external"`
	Internal []*PortAddress `json:"internal"`
}

type CustomDeployScript struct {
	Image   string   `json:"image" validate:"nonzero,regexp=^[-\\w\\.\\/]+(:[-\\w\\.]+)?$"`
	Command []string `json:"command"` // TODO need validation here, I think we need to reqire command
	Timeout int      `json:"timeout" sg:"default=1800"`
}

type VolumeBlueprint struct {
	Name ID     `json:"name" validate:"nonzero,regexp=^\\w[-\\w\\.]*$/"` // TODO max length
	Type string `json:"type" validate:"regexp=^(gp2)$" sg:"default=gp2"` // TODO support other vol types
	Size int    `json:"size" validate:"min=1"`
}

type ContainerBlueprint struct {
	Image   string         `json:"image" validate:"nonzero,regexp=^[-\\w\\.\\/]+(:[-\\w\\.]+)?$"`
	Name    string         `json:"name,omitempty" validate:"regexp=^[-\\w\\.\\/]+(:[-\\w\\.]+)?$"`
	Command []string       `json:"command,omitempty"`
	Ports   []*Port        `json:"ports,omitempty"`
	Env     []*EnvVar      `json:"env,omitempty"`
	CPU     *CpuAllocation `json:"cpu" validate:"nonzero"`
	RAM     *RamAllocation `json:"ram" validate:"nonzero"`
	Mounts  []*Mount       `json:"mounts,omitempty"`
}

type EnvVar struct {
	Name  string `json:"name" validate:"nonzero"`
	Value string `json:"value" validate:"nonzero"` // this may be templated, "something_{{ instance_id }}"
}

type Mount struct {
	Volume ID     `json:"volume" validate:"nonzero"` // TODO should be VolumeName
	Path   string `json:"path" validate:"nonzero"`
}

type Port struct {
	// TODO Kube only accepts TCP|UDP for protocol values, but we accept values
	// like HTTP, which are used to display component addresses. We should either
	// build a map defining the accepted application protocols on top of TCP|UDP,
	// or make a sep. field.
	Protocol string `json:"protocol" validate:"nonzero" sg:"default=TCP"`

	// Number is the port number used by the container. If your application runs
	// on port 80, for example, use that.
	Number int `json:"number" validate:"nonzero,max=40000"`

	// Public determines whether the port can be accessed ONLY from other
	// Components within Supergiant (false), or from BOTH inside and outside of
	// Supergiant (true). When true, the port can be accessed from external Node
	// IPs. When true, and with an EntrypointDomain provided, the port will be
	// exposed on an external load balancer.
	Public bool `json:"public"`

	// PerInstance, when true, provides each Instance of a Component with its own
	// addressable endpoint (in addition to the normal Component-wide endpoints).
	// When false, Instances can not be reached directly, as traffic to the port
	// is load balanced randomly across all Instances.
	PerInstance bool `json:"per_instance"`

	// EntrypointDomain specifies which Entrypoint this Port is added to. Does not
	// apply when Public is false.
	EntrypointDomain ID `json:"entrypoint_domain,omitempty"`

	// ExternalNumber instructs the Entrypoint to set the actual Port number
	// specified as the external load balancer port.
	//
	// TODO validation needed just like on Number, but it can't be nonzero since
	// the value provided can be 0.
	//
	// NOTE Does not apply when EntrypointDomain is nil.
	//      Does not apply to PerInstance ports.
	ExternalNumber int `json:"external_number"`
}

type CpuAllocation struct {
	Min *CoresValue `json:"min"` // NOTE validations removed since they are not numerical values anymore
	Max *CoresValue `json:"max"`
}

type RamAllocation struct {
	Min *BytesValue `json:"min"`
	Max *BytesValue `json:"max"`
}

const (
	InstanceStatusStopped = "STOPPED"
	InstanceStatusStarted = "STARTED"
)

// NOTE this is not to be confused with our concept of Resources like Apps and
// Components -- this is for CPU / RAM / disk.
type ResourceMetrics struct {
	Usage int `json:"usage"`
	Limit int `json:"limit"`
}
<commit_msg>Change max len validation on Component name to 17<commit_after>package common

// App is the main top-level Resource within Supergiant, acting as a logical
// namespace for Components and all of their controlled assets -- along with
// provisioning an actual Kubernetes Namespace, it is used as a base name for
// app-specific cloud assets.
type App struct {
	Name ID `json:"name" validate:"nonzero,max=24,regexp=^[a-z]([-a-z0-9]*[a-z0-9])?$"`
	*Meta
}

type Component struct {
	Name ID `json:"name" validate:"nonzero,max=17,regexp=^[a-z]([-a-z0-9]*[a-z0-9])?$"`

	CustomDeployScript *CustomDeployScript `json:"custom_deploy_script"`

	CurrentReleaseTimestamp ID `json:"current_release_id" sg:"readonly"` // TODO this should be release_timestamp, not release_id
	TargetReleaseTimestamp  ID `json:"target_release_id" sg:"readonly"`

	Addresses *Addresses `json:"addresses,omitempty" sg:"readonly,nostore"`

	*Meta
}

// NOTE the word Blueprint is used for Volumes and Containers, since they are
// both "definitions" that create "instances" of the real thing
type Release struct {
	// NOTE Timestamp here does not use the Timestamp type.
	Timestamp ID `json:"timestamp"`

	// TODO
	// InstanceGroup is used as a labeling mechanism for instances. If nil,
	// InstanceGroup is set equal to the release's Timestamp. If a value is
	// supplied by the user, it MUST be the current (previous) Release's
	// Timestamp.
	//
	// The purpose of InstanceGroup is to prevent restarting between Releases.
	// I'm pretty sure the ONLY scenario in which this value makes sense is when
	// changing InstanceCount, and the value supplied in such a scenario must be
	// the previous Release's timestamp.
	//
	// It seems as though we need to break deploys up into:
	//   - config changes
	//	 - adding/removing instances
	//
	// It might still makes sense to have Release as a grouping mechanism, though,
	// because it could allow for grouping metrics recorded per-Release. However,
	// you may be able to separate the operations, but have every operation create
	// a new record that records the config / instance count at that time.
	InstanceGroup ID `json:"instance_group"`

	InstanceCount int `json:"instance_count" validate:"min=1" sg:"default=1"`

	// These attributes, when changed from last Release, indicate a restart is
	// needed (or just new instances through other means).
	Volumes                []*VolumeBlueprint    `json:"volumes"`
	Containers             []*ContainerBlueprint `json:"containers" validate:"min=1"`
	TerminationGracePeriod int                   `json:"termination_grace_period" validate:"min=0" sg:"default=10"`

	// Retired defines whether or not a Release still has active assets, like pods
	// or services. When retired is true, we skip attempting to delete assets.
	Retired bool `json:"retired" sg:"readonly"`

	// Committed defines whether or not a Release is being / has been deployed.
	Committed bool `json:"committed" sg:"readonly"`

	*Meta
}

// NOTE Instances are not stored in etcd, so the json tags here apply to HTTP
type Instance struct {
	ID ID `json:"id"` // actually just the number (starting w/ 1) of the instance order in the release

	// BaseName is the name of the instance without the Release ID appended. It is
	// used for naming volumes, which move between releases.
	BaseName string `json:"base_name"`
	Name     string `json:"name"`

	Status string `json:"status"`

	CPU *ResourceMetrics `json:"cpu"`
	RAM *ResourceMetrics `json:"ram"`

	Addresses *Addresses `json:"addresses,omitempty"`
}

type Entrypoint struct {
	// NOTE eventually the plan for Domain is to use it for DNS application.
	// That's not hooked up yet, and thus, we aren't doing any type of validation
	// on hostnames.
	Domain ID `json:"domain" validate:"nonzero,max=21"` // e.g. test.example.com

	// the ELB address
	Address string `json:"address" sg:"readonly"`

	// NOTE we actually don't need this -- we can always attach the policy, and enable per port
	// IPWhitelistEnabled bool   `json:"ip_whitelist_enabled"`

	*Meta
}

type Task struct {
	ID         ID     `json:"id"`
	ActionData string `json:"action_data" validate:"nonzero"`

	MaxAttempts int `json:"max_attempts" validate:"min=1" sg:"default=10"`

	Status   string `json:"status" sg:"readonly"`
	Attempts int    `json:"attempts" sg:"readonly"`
	Error    string `json:"error" sg:"readonly"`

	*Meta
}

type ImageRegistry struct {
	Name ID `json:"name"`

	// more to come soon... it's just Dockerhub for now

	*Meta
}

// TODO this should really be renamed to Org probably
type ImageRepo struct {
	Name       ID     `json:"name" validate:"nonzero"`
	Key        string `json:"key" validate:"nonzero" sg:"private"`
	SecretType string `json:"secret_type" validate:"regexp=^(dockercfg|dockerconfigjson)$" sg:"default=dockercfg"`

	*Meta
}

type Node struct {
	ID   ID     `json:"id"`
	Name string `json:"name" sg:"readonly"`
	// This is the only input for Node
	Class      string `json:"class" validate:"nonzero"`
	ExternalIP string `json:"external_ip" sg:"readonly"`

	// LaunchTime time.Time `json:"-"`
	// ServerUptime int              `json:"server_uptime" db:"-"`

	ProviderCreationTimestamp *Timestamp `json:"provider_creation_timestamp" sg:"readonly"`

	OutOfDisk bool             `json:"out_of_disk" sg:"readonly,nostore"`
	Status    string           `json:"status" sg:"readonly,nostore"`
	CPU       *ResourceMetrics `json:"cpu" sg:"readonly,nostore"`
	RAM       *ResourceMetrics `json:"ram" sg:"readonly,nostore"`

	*Meta
}

//==============================================================================

// ID is defined as a string pointer in order to check for nil in the context of
// relations. NOTE this may not be best practice.
type ID *string

// Tags is a meta field for holding unstructued key/val info.
type Tags map[string]string

// Meta is a set of fields on all Resources for holding metadata.
type Meta struct {
	Created *Timestamp `json:"created" sg:"readonly"`
	Updated *Timestamp `json:"updated" sg:"readonly"`
	Tags    Tags       `json:"tags"`
}

type PortAddress struct {
	Port    string `json:"port"` // TODO really this should be the name of the port, which currently is the string of the number
	Address string `json:"address"`
}

type Addresses struct {
	External []*PortAddress `json:"external"`
	Internal []*PortAddress `json:"internal"`
}

type CustomDeployScript struct {
	Image   string   `json:"image" validate:"nonzero,regexp=^[-\\w\\.\\/]+(:[-\\w\\.]+)?$"`
	Command []string `json:"command"` // TODO need validation here, I think we need to reqire command
	Timeout int      `json:"timeout" sg:"default=1800"`
}

type VolumeBlueprint struct {
	Name ID     `json:"name" validate:"nonzero,regexp=^\\w[-\\w\\.]*$/"` // TODO max length
	Type string `json:"type" validate:"regexp=^(gp2)$" sg:"default=gp2"` // TODO support other vol types
	Size int    `json:"size" validate:"min=1"`
}

type ContainerBlueprint struct {
	Image   string         `json:"image" validate:"nonzero,regexp=^[-\\w\\.\\/]+(:[-\\w\\.]+)?$"`
	Name    string         `json:"name,omitempty" validate:"regexp=^[-\\w\\.\\/]+(:[-\\w\\.]+)?$"`
	Command []string       `json:"command,omitempty"`
	Ports   []*Port        `json:"ports,omitempty"`
	Env     []*EnvVar      `json:"env,omitempty"`
	CPU     *CpuAllocation `json:"cpu" validate:"nonzero"`
	RAM     *RamAllocation `json:"ram" validate:"nonzero"`
	Mounts  []*Mount       `json:"mounts,omitempty"`
}

type EnvVar struct {
	Name  string `json:"name" validate:"nonzero"`
	Value string `json:"value" validate:"nonzero"` // this may be templated, "something_{{ instance_id }}"
}

type Mount struct {
	Volume ID     `json:"volume" validate:"nonzero"` // TODO should be VolumeName
	Path   string `json:"path" validate:"nonzero"`
}

type Port struct {
	// TODO Kube only accepts TCP|UDP for protocol values, but we accept values
	// like HTTP, which are used to display component addresses. We should either
	// build a map defining the accepted application protocols on top of TCP|UDP,
	// or make a sep. field.
	Protocol string `json:"protocol" validate:"nonzero" sg:"default=TCP"`

	// Number is the port number used by the container. If your application runs
	// on port 80, for example, use that.
	Number int `json:"number" validate:"nonzero,max=40000"`

	// Public determines whether the port can be accessed ONLY from other
	// Components within Supergiant (false), or from BOTH inside and outside of
	// Supergiant (true). When true, the port can be accessed from external Node
	// IPs. When true, and with an EntrypointDomain provided, the port will be
	// exposed on an external load balancer.
	Public bool `json:"public"`

	// PerInstance, when true, provides each Instance of a Component with its own
	// addressable endpoint (in addition to the normal Component-wide endpoints).
	// When false, Instances can not be reached directly, as traffic to the port
	// is load balanced randomly across all Instances.
	PerInstance bool `json:"per_instance"`

	// EntrypointDomain specifies which Entrypoint this Port is added to. Does not
	// apply when Public is false.
	EntrypointDomain ID `json:"entrypoint_domain,omitempty"`

	// ExternalNumber instructs the Entrypoint to set the actual Port number
	// specified as the external load balancer port.
	//
	// TODO validation needed just like on Number, but it can't be nonzero since
	// the value provided can be 0.
	//
	// NOTE Does not apply when EntrypointDomain is nil.
	//      Does not apply to PerInstance ports.
	ExternalNumber int `json:"external_number"`
}

type CpuAllocation struct {
	Min *CoresValue `json:"min"` // NOTE validations removed since they are not numerical values anymore
	Max *CoresValue `json:"max"`
}

type RamAllocation struct {
	Min *BytesValue `json:"min"`
	Max *BytesValue `json:"max"`
}

const (
	InstanceStatusStopped = "STOPPED"
	InstanceStatusStarted = "STARTED"
)

// NOTE this is not to be confused with our concept of Resources like Apps and
// Components -- this is for CPU / RAM / disk.
type ResourceMetrics struct {
	Usage int `json:"usage"`
	Limit int `json:"limit"`
}
<|endoftext|>


<commit_before>package fronted

import "crypto/x509"
import "fmt"
import "net/http"
import "time"

import "github.com/getlantern/eventual"

var (
	defaultContext = NewFrontingContext("default")
)

// Configure sets the masquerades to use, the trusted root CAs, and the
// cache file for caching masquerades to set up direct domain fronting
// in the default context.
//
// defaultProviderID is used when a masquerade without a provider is
// encountered (eg in a cache file)
func Configure(pool *x509.CertPool, providers map[string]*Provider, defaultProviderID string, cacheFile string) {
	if err := defaultContext.Configure(pool, providers, defaultProviderID, cacheFile); err != nil {
		log.Errorf("Error configuring fronting %s context: %s!!", defaultContext.name, err)
	}
}

// NewDirect creates a new http.RoundTripper that does direct domain fronting
// using the default context. If it can't obtain a working masquerade within
// the given timeout, it will return nil/false.
func NewDirect(timeout time.Duration) (http.RoundTripper, bool) {
	return defaultContext.NewDirect(timeout)
}

// CloseCache closes any existing cache file in the default context
func CloseCache() {
	defaultContext.CloseCache()
}

func NewFrontingContext(name string) *FrontingContext {
	return &FrontingContext{
		name:     name,
		instance: eventual.NewValue(),
	}
}

type FrontingContext struct {
	name     string
	instance eventual.Value
}

// Configure sets the masquerades to use, the trusted root CAs, and the
// cache file for caching masquerades to set up direct domain fronting.
// defaultProviderID is used when a masquerade without a provider is
// encountered (eg in a cache file)
func (fctx *FrontingContext) Configure(pool *x509.CertPool, providers map[string]*Provider, defaultProviderID string, cacheFile string) error {
	log.Tracef("Configuring fronted %s context", fctx.name)

	if providers == nil || len(providers) == 0 {
		return fmt.Errorf("No fronted providers for %s context.", fctx.name)
	}

	_existing, ok := fctx.instance.Get(0)
	if ok && _existing != nil {
		existing := _existing.(*direct)
		log.Debugf("Closing cache from existing instance for %s context", fctx.name)
		existing.closeCache()
	}

	size := 0
	for _, p := range providers {
		size += len(p.Masquerades)
	}

	if size == 0 {
		return fmt.Errorf("No masquerades for %s context.", fctx.name)
	}

	d := &direct{
		certPool:            pool,
		candidates:          make(chan masquerade, size),
		masquerades:         make(chan masquerade, size),
		maxAllowedCachedAge: defaultMaxAllowedCachedAge,
		maxCacheSize:        defaultMaxCacheSize,
		cacheSaveInterval:   defaultCacheSaveInterval,
		toCache:             make(chan masquerade, defaultMaxCacheSize),
		defaultProviderID:   defaultProviderID,
		providers:           make(map[string]*Provider),
		ready:               make(chan struct{}),
	}

	// copy providers
	for k, p := range providers {
		d.providers[k] = NewProvider(p.HostAliases, p.TestURL, p.Masquerades, p.Validator, p.PassthroughPatterns)
	}

	numberToVet := numberToVetInitially
	if cacheFile != "" {
		numberToVet -= d.initCaching(cacheFile)
	}

	d.loadCandidates(d.providers)
	if numberToVet > 0 {
		d.vet(numberToVet)
	} else {
		log.Debugf("Not vetting any masquerades for %s context because we have enough cached ones", fctx.name)
		d.signalReady()
	}
	fctx.instance.Set(d)
	return nil
}

// NewDirect creates a new http.RoundTripper that does direct domain fronting.
// If it can't obtain a working masquerade within the given timeout, it will
// return nil/false.
func (fctx *FrontingContext) NewDirect(timeout time.Duration) (http.RoundTripper, bool) {
	start := time.Now()
	instance, ok := fctx.instance.Get(timeout)
	if !ok {
		log.Errorf("No DirectHttpClient available within %v for context %s", timeout, fctx.name)
		return nil, false
	}
	remaining := timeout - time.Since(start)

	// Wait to be signalled that at least one masquerade has been vetted...
	select {
	case <-instance.(*direct).ready:
		return instance.(http.RoundTripper), true
	case <-time.After(remaining):
		log.Errorf("No DirectHttpClient available within %v", timeout)
		return nil, false
	}
}

// CloseCache closes any existing cache file in the default contexxt.
func (fctx *FrontingContext) CloseCache() {
	_existing, ok := fctx.instance.Get(0)
	if ok && _existing != nil {
		existing := _existing.(*direct)
		log.Debugf("Closing cache from existing instance in %s context", fctx.name)
		existing.closeCache()
	}
}
<commit_msg>brain stopped working<commit_after>package fronted

import (
	"crypto/x509"
	"fmt"
	"net/http"
	"time"

	"github.com/getlantern/eventual"
)

var (
	defaultContext = NewFrontingContext("default")
)

// Configure sets the masquerades to use, the trusted root CAs, and the
// cache file for caching masquerades to set up direct domain fronting
// in the default context.
//
// defaultProviderID is used when a masquerade without a provider is
// encountered (eg in a cache file)
func Configure(pool *x509.CertPool, providers map[string]*Provider, defaultProviderID string, cacheFile string) {
	if err := defaultContext.Configure(pool, providers, defaultProviderID, cacheFile); err != nil {
		log.Errorf("Error configuring fronting %s context: %s!!", defaultContext.name, err)
	}
}

// NewDirect creates a new http.RoundTripper that does direct domain fronting
// using the default context. If it can't obtain a working masquerade within
// the given timeout, it will return nil/false.
func NewDirect(timeout time.Duration) (http.RoundTripper, bool) {
	return defaultContext.NewDirect(timeout)
}

// CloseCache closes any existing cache file in the default context
func CloseCache() {
	defaultContext.CloseCache()
}

func NewFrontingContext(name string) *FrontingContext {
	return &FrontingContext{
		name:     name,
		instance: eventual.NewValue(),
	}
}

type FrontingContext struct {
	name     string
	instance eventual.Value
}

// Configure sets the masquerades to use, the trusted root CAs, and the
// cache file for caching masquerades to set up direct domain fronting.
// defaultProviderID is used when a masquerade without a provider is
// encountered (eg in a cache file)
func (fctx *FrontingContext) Configure(pool *x509.CertPool, providers map[string]*Provider, defaultProviderID string, cacheFile string) error {
	log.Tracef("Configuring fronted %s context", fctx.name)

	if providers == nil || len(providers) == 0 {
		return fmt.Errorf("No fronted providers for %s context.", fctx.name)
	}

	_existing, ok := fctx.instance.Get(0)
	if ok && _existing != nil {
		existing := _existing.(*direct)
		log.Debugf("Closing cache from existing instance for %s context", fctx.name)
		existing.closeCache()
	}

	size := 0
	for _, p := range providers {
		size += len(p.Masquerades)
	}

	if size == 0 {
		return fmt.Errorf("No masquerades for %s context.", fctx.name)
	}

	d := &direct{
		certPool:            pool,
		candidates:          make(chan masquerade, size),
		masquerades:         make(chan masquerade, size),
		maxAllowedCachedAge: defaultMaxAllowedCachedAge,
		maxCacheSize:        defaultMaxCacheSize,
		cacheSaveInterval:   defaultCacheSaveInterval,
		toCache:             make(chan masquerade, defaultMaxCacheSize),
		defaultProviderID:   defaultProviderID,
		providers:           make(map[string]*Provider),
		ready:               make(chan struct{}),
	}

	// copy providers
	for k, p := range providers {
		d.providers[k] = NewProvider(p.HostAliases, p.TestURL, p.Masquerades, p.Validator, p.PassthroughPatterns)
	}

	numberToVet := numberToVetInitially
	if cacheFile != "" {
		numberToVet -= d.initCaching(cacheFile)
	}

	d.loadCandidates(d.providers)
	if numberToVet > 0 {
		d.vet(numberToVet)
	} else {
		log.Debugf("Not vetting any masquerades for %s context because we have enough cached ones", fctx.name)
		d.signalReady()
	}
	fctx.instance.Set(d)
	return nil
}

// NewDirect creates a new http.RoundTripper that does direct domain fronting.
// If it can't obtain a working masquerade within the given timeout, it will
// return nil/false.
func (fctx *FrontingContext) NewDirect(timeout time.Duration) (http.RoundTripper, bool) {
	start := time.Now()
	instance, ok := fctx.instance.Get(timeout)
	if !ok {
		log.Errorf("No DirectHttpClient available within %v for context %s", timeout, fctx.name)
		return nil, false
	}
	remaining := timeout - time.Since(start)

	// Wait to be signalled that at least one masquerade has been vetted...
	select {
	case <-instance.(*direct).ready:
		return instance.(http.RoundTripper), true
	case <-time.After(remaining):
		log.Errorf("No DirectHttpClient available within %v", timeout)
		return nil, false
	}
}

// CloseCache closes any existing cache file in the default contexxt.
func (fctx *FrontingContext) CloseCache() {
	_existing, ok := fctx.instance.Get(0)
	if ok && _existing != nil {
		existing := _existing.(*direct)
		log.Debugf("Closing cache from existing instance in %s context", fctx.name)
		existing.closeCache()
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Jonathan Chappelow
// Under the ISC license.  See LICENSE.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"time"

	"github.com/decred/dcrd/chaincfg"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrrpcclient"
)

var host = flag.String("host", "127.0.0.1:9110", "wallet RPC host:port")
var user = flag.String("user", "dcrwallet", "wallet RPC username")
var pass = flag.String("pass", "bananas", "wallet RPC password")
var cert = flag.String("cert", "dcrwallet.cert", "wallet RPC TLS certificate (when notls=false)")
var notls = flag.Bool("notls", false, "Disable use of TLS for wallet connection")

var (
	activeChainParams = &chaincfg.MainNetParams
)

func main() {
	// Parse command line flags
	flag.Parse()

	// Connect to wallet RPC server
	wcl, err := ConnectRPC(*host, *user, *pass, *cert, *notls)
	if err != nil {
		log.Fatalf("Unable to connect to RPC server: %v", err)
	}

	walletInfo, err := wcl.WalletInfo()
	if err != nil {
		log.Fatalf("WalletInfo failed: %v", err)
	}

	log.Println("Wallet connected to node? ", walletInfo.DaemonConnected)

	log.Println("Listing all transaction inputs and outputs...")
	allTxnIOs, err := wcl.ListTransactionsCountFrom("*", 9999999, 0)
	if err != nil {
		log.Fatalf("ListTransactions failed: %v", err)
	}

	// Transactions may occur multiple times in the list, so gather the unique
	// transaction hashes with a map. We could just take the result that is
	// vout[0] of an stakegen, but this easy too.
	knownVotes := make(map[string]bool)
	for _, tx := range allTxnIOs {
		if *tx.TxType == "vote" {
			knownVotes[tx.TxID] = true
		}
	}
	log.Println("Number of votes: ", len(knownVotes))

	waitSeconds := make([]float64, 0, len(knownVotes))
	waitBlocks := make([]int64, 0, len(knownVotes))

	for txid := range knownVotes {
		// Get ticket address from previous outpoint of Vin[1] of SSGen
		voteHash, err := chainhash.NewHashFromStr(txid)
		if err != nil {
			log.Printf("Invalid tx hash %s: %v", txid, err)
			continue
		}
		txRaw, err := wcl.GetRawTransaction(voteHash)
		if err != nil {
			log.Printf("GetRawTransaction(vote) failed: %v", err)
			continue
		}

		// Vin[1] spends the stakesubmission of the ticket purchase
		prevout := txRaw.MsgTx().TxIn[1].PreviousOutPoint
		ticketHash := &prevout.Hash
		ticketTxOutIndex := prevout.Index

		// Get block height and time for the vote
		txRawVerbose, err := wcl.GetRawTransactionVerbose(voteHash)
		if err != nil {
			log.Fatalf("GetRawTransactionVerbose(vote) failed: %v", err)
		}
		voteHeight := txRawVerbose.BlockHeight
		voteTime := time.Unix(txRawVerbose.Blocktime, 0)

		// Get block height and time for the ticket
		prevTxRaw, err := wcl.GetRawTransactionVerbose(ticketHash)
		if err != nil {
			log.Fatalf("GetRawTransactionVerbose(ticket) failed: %v", err)
		}

		// Tickets mature 256 blocks after purchase
		ticketPurchaseHeight := prevTxRaw.BlockHeight
		//ticketTime := time.Unix(prevTxRaw.Blocktime, 0)
		ticketMaturityHeight := ticketPurchaseHeight + int64(activeChainParams.TicketMaturity)
		// Get time of block at this height
		ticketMaturityBlockHash, _ := wcl.GetBlockHash(ticketMaturityHeight)
		ticketMaturityBlock, _ := wcl.GetBlockHeaderVerbose(ticketMaturityBlockHash)
		ticketMaturityTime := time.Unix(ticketMaturityBlock.Time, 0)

		// Compute time from maturity to vote
		voteWaitBlocks := voteHeight - ticketMaturityHeight
		voteWaitSeconds := voteTime.Sub(ticketMaturityTime)
		voteWaitDays := voteWaitSeconds.Hours() / 24.0

		ticketPrice := prevTxRaw.Vout[ticketTxOutIndex].Value
		log.Printf("Ticket %s... (%f DCR) mined in block %d, voted %d blocks (%.2f days) after maturity.",
			prevTxRaw.Txid[:8], ticketPrice, ticketPurchaseHeight, voteWaitBlocks, voteWaitDays)

		waitBlocks = append(waitBlocks, voteWaitBlocks)
		waitSeconds = append(waitSeconds, voteWaitSeconds.Seconds())
	}

	// Compute mean wait time in blocks and seconds
	var avgBlockWait, avgSecondWait float64
	for iv := range waitBlocks {
		avgBlockWait += float64(waitBlocks[iv])
		avgSecondWait += waitSeconds[iv]
	}
	avgBlockWait /= float64(len(waitBlocks))
	avgSecondWait /= float64(len(waitBlocks))
	log.Printf("Mean wait for %d votes: %.1f blocks, %.2f days.", len(knownVotes),
		avgBlockWait, avgSecondWait/86400.0)
}

// ConnectRPC attempts to create a new websocket connection to a legacy RPC
// server with the given credentials.
func ConnectRPC(host, user, pass, cert string, disableTLS bool) (*dcrrpcclient.Client, error) {
	var rpcCerts []byte
	var err error
	if !disableTLS {
		rpcCerts, err = ioutil.ReadFile(cert)
		if err != nil {
			return nil, fmt.Errorf("Failed to read RPC cert file at %s: %s\n",
				cert, err.Error())
		}
		log.Printf("Attempting to connect to RPC server %s as user %s "+
			"using certificate located in %s",
			host, user, cert)
	} else {
		log.Printf("Attempting to connect to RPC server %s as user %s (no TLS)",
			host, user)
	}

	connCfgDaemon := &dcrrpcclient.ConnConfig{
		Host:         host,
		Endpoint:     "ws", // websocket
		User:         user,
		Pass:         pass,
		Certificates: rpcCerts,
		DisableTLS:   disableTLS,
	}

	rpcClient, err := dcrrpcclient.New(connCfgDaemon, nil)
	if err != nil {
		return nil, fmt.Errorf("Failed to start dcrwallet RPC client: %s", err.Error())
	}

	return rpcClient, nil
}
<commit_msg>Update for new rpcclient package location.<commit_after>// Copyright (c) 2017, Jonathan Chappelow
// Under the ISC license.  See LICENSE.
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"time"

	"github.com/decred/dcrd/chaincfg"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/rpcclient"
)

var host = flag.String("host", "127.0.0.1:9110", "wallet RPC host:port")
var user = flag.String("user", "dcrwallet", "wallet RPC username")
var pass = flag.String("pass", "bananas", "wallet RPC password")
var cert = flag.String("cert", "dcrwallet.cert", "wallet RPC TLS certificate (when notls=false)")
var notls = flag.Bool("notls", false, "Disable use of TLS for wallet connection")

var (
	activeChainParams = &chaincfg.MainNetParams
)

func main() {
	// Parse command line flags
	flag.Parse()

	// Connect to wallet RPC server
	wcl, err := ConnectRPC(*host, *user, *pass, *cert, *notls)
	if err != nil {
		log.Fatalf("Unable to connect to RPC server: %v", err)
	}

	walletInfo, err := wcl.WalletInfo()
	if err != nil {
		log.Fatalf("WalletInfo failed: %v", err)
	}

	log.Println("Wallet connected to node? ", walletInfo.DaemonConnected)

	log.Println("Listing all transaction inputs and outputs...")
	allTxnIOs, err := wcl.ListTransactionsCountFrom("*", 9999999, 0)
	if err != nil {
		log.Fatalf("ListTransactions failed: %v", err)
	}

	// Transactions may occur multiple times in the list, so gather the unique
	// transaction hashes with a map. We could just take the result that is
	// vout[0] of an stakegen, but this easy too.
	knownVotes := make(map[string]bool)
	for _, tx := range allTxnIOs {
		if *tx.TxType == "vote" {
			knownVotes[tx.TxID] = true
		}
	}
	log.Println("Number of votes: ", len(knownVotes))

	waitSeconds := make([]float64, 0, len(knownVotes))
	waitBlocks := make([]int64, 0, len(knownVotes))

	for txid := range knownVotes {
		// Get ticket address from previous outpoint of Vin[1] of SSGen
		voteHash, err := chainhash.NewHashFromStr(txid)
		if err != nil {
			log.Printf("Invalid tx hash %s: %v", txid, err)
			continue
		}
		txRaw, err := wcl.GetRawTransaction(voteHash)
		if err != nil {
			log.Printf("GetRawTransaction(vote) failed: %v", err)
			continue
		}

		// Vin[1] spends the stakesubmission of the ticket purchase
		prevout := txRaw.MsgTx().TxIn[1].PreviousOutPoint
		ticketHash := &prevout.Hash
		ticketTxOutIndex := prevout.Index

		// Get block height and time for the vote
		txRawVerbose, err := wcl.GetRawTransactionVerbose(voteHash)
		if err != nil {
			log.Fatalf("GetRawTransactionVerbose(vote) failed: %v", err)
		}
		voteHeight := txRawVerbose.BlockHeight
		voteTime := time.Unix(txRawVerbose.Blocktime, 0)

		// Get block height and time for the ticket
		prevTxRaw, err := wcl.GetRawTransactionVerbose(ticketHash)
		if err != nil {
			log.Fatalf("GetRawTransactionVerbose(ticket) failed: %v", err)
		}

		// Tickets mature 256 blocks after purchase
		ticketPurchaseHeight := prevTxRaw.BlockHeight
		//ticketTime := time.Unix(prevTxRaw.Blocktime, 0)
		ticketMaturityHeight := ticketPurchaseHeight + int64(activeChainParams.TicketMaturity)
		// Get time of block at this height
		ticketMaturityBlockHash, _ := wcl.GetBlockHash(ticketMaturityHeight)
		ticketMaturityBlock, _ := wcl.GetBlockHeaderVerbose(ticketMaturityBlockHash)
		ticketMaturityTime := time.Unix(ticketMaturityBlock.Time, 0)

		// Compute time from maturity to vote
		voteWaitBlocks := voteHeight - ticketMaturityHeight
		voteWaitSeconds := voteTime.Sub(ticketMaturityTime)
		voteWaitDays := voteWaitSeconds.Hours() / 24.0

		ticketPrice := prevTxRaw.Vout[ticketTxOutIndex].Value
		log.Printf("Ticket %s... (%f DCR) mined in block %d, voted %d blocks (%.2f days) after maturity.",
			prevTxRaw.Txid[:8], ticketPrice, ticketPurchaseHeight, voteWaitBlocks, voteWaitDays)

		waitBlocks = append(waitBlocks, voteWaitBlocks)
		waitSeconds = append(waitSeconds, voteWaitSeconds.Seconds())
	}

	// Compute mean wait time in blocks and seconds
	var avgBlockWait, avgSecondWait float64
	for iv := range waitBlocks {
		avgBlockWait += float64(waitBlocks[iv])
		avgSecondWait += waitSeconds[iv]
	}
	avgBlockWait /= float64(len(waitBlocks))
	avgSecondWait /= float64(len(waitBlocks))
	log.Printf("Mean wait for %d votes: %.1f blocks, %.2f days.", len(knownVotes),
		avgBlockWait, avgSecondWait/86400.0)
}

// ConnectRPC attempts to create a new websocket connection to a legacy RPC
// server with the given credentials.
func ConnectRPC(host, user, pass, cert string, disableTLS bool) (*rpcclient.Client, error) {
	var rpcCerts []byte
	var err error
	if !disableTLS {
		rpcCerts, err = ioutil.ReadFile(cert)
		if err != nil {
			return nil, fmt.Errorf("Failed to read RPC cert file at %s: %s\n",
				cert, err.Error())
		}
		log.Printf("Attempting to connect to RPC server %s as user %s "+
			"using certificate located in %s",
			host, user, cert)
	} else {
		log.Printf("Attempting to connect to RPC server %s as user %s (no TLS)",
			host, user)
	}

	connCfgDaemon := &rpcclient.ConnConfig{
		Host:         host,
		Endpoint:     "ws", // websocket
		User:         user,
		Pass:         pass,
		Certificates: rpcCerts,
		DisableTLS:   disableTLS,
	}

	rpcClient, err := rpcclient.New(connCfgDaemon, nil)
	if err != nil {
		return nil, fmt.Errorf("Failed to start dcrwallet RPC client: %s", err.Error())
	}

	return rpcClient, nil
}
<|endoftext|>


<commit_before>// +build !libstorage_storage_executor libstorage_storage_executor_scaleio

package executor

import (
	"io/ioutil"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	gofig "github.com/akutz/gofig/types"
	"github.com/akutz/goof"
	"github.com/akutz/gotil"

	"github.com/codedellemc/libstorage/api/registry"
	"github.com/codedellemc/libstorage/api/types"
	"github.com/codedellemc/libstorage/drivers/storage/scaleio"
)

// driver is the storage executor for the VFS storage driver.
type driver struct {
	guid   string
	drvCfg string
}

func init() {
	registry.RegisterStorageExecutor(scaleio.Name, newdriver)
}

func newdriver() types.StorageExecutor {
	return &driver{}
}

func (d *driver) Init(context types.Context, config gofig.Config) error {
	if d.guid = config.GetString("scaleio.guid"); d.guid == "" {
		d.drvCfg = config.GetString("scaleio.drvCfg")
	}
	return nil
}

func (d *driver) Name() string {
	return scaleio.Name
}

// Supported returns a flag indicating whether or not the platform
// implementing the executor is valid for the host on which the executor
// resides.
func (d *driver) Supported(
	ctx types.Context,
	opts types.Store) (bool, error) {

	if d.guid != "" {
		return true, nil
	}

	return gotil.FileExists(d.drvCfg), nil
}

// NextDevice returns the next available device.
func (d *driver) NextDevice(
	ctx types.Context,
	opts types.Store) (string, error) {

	return "", types.ErrNotImplemented
}

// LocalDevices returns a map of the system's local devices.
func (d *driver) LocalDevices(
	ctx types.Context,
	opts *types.LocalDevicesOpts) (*types.LocalDevices, error) {

	lvm, err := getLocalVolumeMap()
	if err != nil {
		return nil, err
	}

	return &types.LocalDevices{
		Driver:    scaleio.Name,
		DeviceMap: lvm,
	}, nil
}

const diskIDPath = "/dev/disk/by-id"

func getLocalVolumeMap() (map[string]string, error) {
	volMap := map[string]string{}
	files, err := ioutil.ReadDir(diskIDPath)
	if err != nil {
		return nil, err
	}
	diskIDRX := regexp.MustCompile(`(?i)emc-vol-[^-].+-(.+)$`)
	for _, f := range files {
		m := diskIDRX.FindStringSubmatch(f.Name())
		if len(m) == 0 {
			continue
		}
		devPath, err := filepath.EvalSymlinks(path.Join(diskIDPath, f.Name()))
		if err != nil {
			return nil, err
		}
		volMap[m[1]] = devPath
	}
	return volMap, nil
}

// InstanceID returns the local system's InstanceID.
func (d *driver) InstanceID(
	ctx types.Context,
	opts types.Store) (*types.InstanceID, error) {

	return d.getInstanceID()
}

// GetInstanceID returns the instance ID object
func GetInstanceID(
	ctx types.Context, config gofig.Config) (*types.InstanceID, error) {

	d := &driver{}
	d.Init(ctx, config)
	return d.getInstanceID()
}

func (d *driver) getInstanceID() (*types.InstanceID, error) {

	if d.guid != "" {
		iid := &types.InstanceID{Driver: scaleio.Name}
		if err := iid.MarshalMetadata(d.guid); err != nil {
			return nil, err
		}
		return iid, nil
	}

	out, err := exec.Command(d.drvCfg, "--query_guid").CombinedOutput()
	if err != nil {
		return nil, goof.WithError("error getting sdc guid", err)
	}

	sdcGUID := strings.Replace(string(out), "\n", "", -1)
	iid := &types.InstanceID{Driver: scaleio.Name}
	if err := iid.MarshalMetadata(sdcGUID); err != nil {
		return nil, err
	}
	return iid, nil
}
<commit_msg>return empty vol map if /dev/disk/by-id not there<commit_after>// +build !libstorage_storage_executor libstorage_storage_executor_scaleio

package executor

import (
	"io/ioutil"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	gofig "github.com/akutz/gofig/types"
	"github.com/akutz/goof"
	"github.com/akutz/gotil"

	"github.com/codedellemc/libstorage/api/registry"
	"github.com/codedellemc/libstorage/api/types"
	"github.com/codedellemc/libstorage/drivers/storage/scaleio"
)

// driver is the storage executor for the VFS storage driver.
type driver struct {
	guid   string
	drvCfg string
}

func init() {
	registry.RegisterStorageExecutor(scaleio.Name, newdriver)
}

func newdriver() types.StorageExecutor {
	return &driver{}
}

func (d *driver) Init(context types.Context, config gofig.Config) error {
	if d.guid = config.GetString("scaleio.guid"); d.guid == "" {
		d.drvCfg = config.GetString("scaleio.drvCfg")
	}
	return nil
}

func (d *driver) Name() string {
	return scaleio.Name
}

// Supported returns a flag indicating whether or not the platform
// implementing the executor is valid for the host on which the executor
// resides.
func (d *driver) Supported(
	ctx types.Context,
	opts types.Store) (bool, error) {

	if d.guid != "" {
		return true, nil
	}

	return gotil.FileExists(d.drvCfg), nil
}

// NextDevice returns the next available device.
func (d *driver) NextDevice(
	ctx types.Context,
	opts types.Store) (string, error) {

	return "", types.ErrNotImplemented
}

// LocalDevices returns a map of the system's local devices.
func (d *driver) LocalDevices(
	ctx types.Context,
	opts *types.LocalDevicesOpts) (*types.LocalDevices, error) {

	lvm, err := getLocalVolumeMap()
	if err != nil {
		return nil, err
	}

	return &types.LocalDevices{
		Driver:    scaleio.Name,
		DeviceMap: lvm,
	}, nil
}

const diskIDPath = "/dev/disk/by-id"

func getLocalVolumeMap() (map[string]string, error) {
	volMap := map[string]string{}

	if !gotil.FileExists(diskIDPath) {
		// the diskIDPath does not exist -- therefore no vols
		return volMap, nil
	}
	files, err := ioutil.ReadDir(diskIDPath)
	if err != nil {
		return nil, err
	}
	diskIDRX := regexp.MustCompile(`(?i)emc-vol-[^-].+-(.+)$`)
	for _, f := range files {
		m := diskIDRX.FindStringSubmatch(f.Name())
		if len(m) == 0 {
			continue
		}
		devPath, err := filepath.EvalSymlinks(path.Join(diskIDPath, f.Name()))
		if err != nil {
			return nil, err
		}
		volMap[m[1]] = devPath
	}
	return volMap, nil
}

// InstanceID returns the local system's InstanceID.
func (d *driver) InstanceID(
	ctx types.Context,
	opts types.Store) (*types.InstanceID, error) {

	return d.getInstanceID()
}

// GetInstanceID returns the instance ID object
func GetInstanceID(
	ctx types.Context, config gofig.Config) (*types.InstanceID, error) {

	d := &driver{}
	d.Init(ctx, config)
	return d.getInstanceID()
}

func (d *driver) getInstanceID() (*types.InstanceID, error) {

	if d.guid != "" {
		iid := &types.InstanceID{Driver: scaleio.Name}
		if err := iid.MarshalMetadata(d.guid); err != nil {
			return nil, err
		}
		return iid, nil
	}

	out, err := exec.Command(d.drvCfg, "--query_guid").CombinedOutput()
	if err != nil {
		return nil, goof.WithError("error getting sdc guid", err)
	}

	sdcGUID := strings.Replace(string(out), "\n", "", -1)
	iid := &types.InstanceID{Driver: scaleio.Name}
	if err := iid.MarshalMetadata(sdcGUID); err != nil {
		return nil, err
	}
	return iid, nil
}
<|endoftext|>


<commit_before>package wag

import (
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"testing"

	"github.com/tsavola/wag/internal/sexp"
)

const (
	magic   = 0x54fd3985
	idBase  = 556231
	objdump = true
)

type fun func() int32

type startFunc struct {
	f *fun
}

type startFuncPtr *startFunc

func TestI32(t *testing.T) { test(t, "testdata/i32.wast", "i32") }
func TestI64(t *testing.T) { test(t, "testdata/i64.wast", "i64") }

var (
	execCompiled bool
)

func test(t *testing.T, filename, typeHack string) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}

	module, data := sexp.ParsePanic(data)

	exports := make(map[string]string)

	for i := 1; i < len(module); {
		item := module[i].([]interface{})
		if item[0].(string) == "export" {
			exports[item[1].(string)] = item[2].(string)
			module = append(module[:i], module[i+1:]...)
		} else {
			i++
		}
	}

	testFunc := []interface{}{
		"func",
		"$test",
		[]interface{}{"result", "i32"},
	}

	testId := idBase

	for {
		var assert []interface{}
		assert, data = sexp.ParsePanic(data)
		if assert == nil {
			break
		}

		invoke2call(exports, assert[1:])

		name := assert[0].(string)

		var test []interface{}

		switch name {
		case "assert_return":
			test = []interface{}{
				"if",
				append([]interface{}{typeHack + ".ne"}, assert[1:]...),
				[]interface{}{
					[]interface{}{
						"return",
						[]interface{}{
							"i32.const",
							strconv.Itoa(testId),
						},
					},
				},
			}

		default:
			t.Logf("%s skipped", name)
			continue
		}

		testFunc = append(testFunc, test)
		testId++
	}

	testFunc = append(testFunc, []interface{}{
		"return",
		[]interface{}{
			"i32.const",
			strconv.Itoa(magic),
		},
	})

	module = append(module, testFunc)

	module = append(module, []interface{}{
		"start",
		"$test",
	})

	m := loadModule(module)
	binary := m.GenCode()

	f, err := os.Create("testdata/code")
	if err != nil {
		t.Fatal(err)
	}

	if _, err := f.Write(binary); err != nil {
		t.Fatal(err)
	}

	if objdump {
		dump := exec.Command("objdump", "-D", "-bbinary", "-mi386:x86-64", "testdata/code")
		dump.Stdout = os.Stdout
		dump.Stderr = os.Stderr

		if err := dump.Run(); err != nil {
			t.Fatal(err)
		}
	}

	if !execCompiled {
		cc := exec.Command("cc", "-g", "-o", "testdata/exec", "testdata/exec.c")
		cc.Stdout = os.Stdout
		cc.Stderr = os.Stderr

		if err := cc.Run(); err != nil {
			t.Fatal(err)
		}

		execCompiled = true
	}

	exec := exec.Command("testdata/exec", "testdata/code")
	exec.Stdin = f
	exec.Stdout = os.Stdout
	exec.Stderr = os.Stderr

	if err := exec.Run(); err != nil {
		t.Fatal(err)
	}
}

func invoke2call(exports map[string]string, x interface{}) {
	if item, ok := x.([]interface{}); ok {
		if s, ok := item[0].(string); ok && s == "invoke" {
			name1 := item[1].(string)
			name2, found := exports[name1]
			if !found {
				panic(name1)
			}

			item[0] = "call"
			item[1] = name2
		}

		for _, e := range item {
			invoke2call(exports, e)
		}
	}
}
<commit_msg>determine type of assert_return comparison automatically<commit_after>package wag

import (
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"testing"

	"github.com/tsavola/wag/internal/sexp"
)

const (
	magic   = 0x54fd3985
	idBase  = 556231
	objdump = true
)

type fun func() int32

type startFunc struct {
	f *fun
}

type startFuncPtr *startFunc

func TestI32(t *testing.T) { test(t, "testdata/i32.wast") }
func TestI64(t *testing.T) { test(t, "testdata/i64.wast") }

var (
	execCompiled bool
)

func test(t *testing.T, filename string) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}

	module, data := sexp.ParsePanic(data)

	exports := make(map[string]string)

	for i := 1; i < len(module); {
		item := module[i].([]interface{})
		if item[0].(string) == "export" {
			exports[item[1].(string)] = item[2].(string)
			module = append(module[:i], module[i+1:]...)
		} else {
			i++
		}
	}

	testFunc := []interface{}{
		"func",
		"$test",
		[]interface{}{"result", "i32"},
	}

	testId := idBase

	for {
		var assert []interface{}
		assert, data = sexp.ParsePanic(data)
		if assert == nil {
			break
		}

		assertName := assert[0].(string)

		var exprType string

		for _, x := range assert[1:] {
			expr := x.([]interface{})
			exprName := expr[0].(string)
			if strings.Contains(exprName, ".") {
				exprType = strings.SplitN(exprName, ".", 2)[0]
				break
			}
		}

		if exprType == "" {
			t.Fatalf("can't figure out type of %s", assertName)
		}

		invoke2call(exports, assert[1:])

		var test []interface{}

		switch assertName {
		case "assert_return":
			test = []interface{}{
				"if",
				append([]interface{}{exprType + ".ne"}, assert[1:]...),
				[]interface{}{
					[]interface{}{
						"return",
						[]interface{}{
							"i32.const",
							strconv.Itoa(testId),
						},
					},
				},
			}

		default:
			t.Logf("%s skipped", assertName)
			continue
		}

		testFunc = append(testFunc, test)
		testId++
	}

	testFunc = append(testFunc, []interface{}{
		"return",
		[]interface{}{
			"i32.const",
			strconv.Itoa(magic),
		},
	})

	module = append(module, testFunc)

	module = append(module, []interface{}{
		"start",
		"$test",
	})

	m := loadModule(module)
	binary := m.GenCode()

	f, err := os.Create("testdata/code")
	if err != nil {
		t.Fatal(err)
	}

	if _, err := f.Write(binary); err != nil {
		t.Fatal(err)
	}

	if objdump {
		dump := exec.Command("objdump", "-D", "-bbinary", "-mi386:x86-64", "testdata/code")
		dump.Stdout = os.Stdout
		dump.Stderr = os.Stderr

		if err := dump.Run(); err != nil {
			t.Fatal(err)
		}
	}

	if !execCompiled {
		cc := exec.Command("cc", "-g", "-o", "testdata/exec", "testdata/exec.c")
		cc.Stdout = os.Stdout
		cc.Stderr = os.Stderr

		if err := cc.Run(); err != nil {
			t.Fatal(err)
		}

		execCompiled = true
	}

	exec := exec.Command("testdata/exec", "testdata/code")
	exec.Stdin = f
	exec.Stdout = os.Stdout
	exec.Stderr = os.Stderr

	if err := exec.Run(); err != nil {
		t.Fatal(err)
	}
}

func invoke2call(exports map[string]string, x interface{}) {
	if item, ok := x.([]interface{}); ok {
		if s, ok := item[0].(string); ok && s == "invoke" {
			name1 := item[1].(string)
			name2, found := exports[name1]
			if !found {
				panic(name1)
			}

			item[0] = "call"
			item[1] = name2
		}

		for _, e := range item {
			invoke2call(exports, e)
		}
	}
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net/http"
	"os"
	"strconv"
)

func main() {
	log.Print("starting server...")
	http.Handle("/convert", &convertHandler{currencies: currencies()})

	// Determine port for HTTP service.
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("defaulting to port %s", port)
	}

	// Start HTTP server.
	log.Printf("listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

// ConvertRequest stores request data
type ConvertRequest struct {
	Value string `json:"value"`
}

// ConvertResponse stores response data
type ConvertResponse struct {
	Answer int `json:"answer"`
}

type convertHandler struct {
	currencies map[string]float64
}

func currencies() map[string]float64 {
	return map[string]float64{
		"JPY": 100,
		"USD": 0.91,
		"EUR": 0.75,
		"BRL": 4.77,
		"AUD": 1.18,
	}
}

// IsValid validates request data
func (s *ConvertRequest) IsValid() bool {
	if len(s.Value) < 4 {
		return false
	}
	if _, err := strconv.Atoi(s.Value[3:]); err != nil {
		return false
	}
	return true
}

func (h *convertHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var convertRequest ConvertRequest
	err := json.NewDecoder(r.Body).Decode(&convertRequest)
	if err != nil {
		returnError(w, "Failed to decode data")
		return
	}
	if !convertRequest.IsValid() {
		returnError(w, fmt.Sprintf("Invalid format: %s", convertRequest.Value))
		return
	}
	if _, ok := h.currencies[convertRequest.Value[:3]]; !ok {
		returnError(w, fmt.Sprintf("Unknown currency: %s", convertRequest.Value[:3]))
		return
	}

	num, _ := strconv.Atoi(convertRequest.Value[3:])
	currency := h.currencies[convertRequest.Value[:3]]
	answer := math.Floor(h.currencies["JPY"] / currency * float64(num))

	convertResponse := ConvertResponse{Answer: int(answer)}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(convertResponse)
}

// ErrorJSON defines error message as JSON
type ErrorJSON struct {
	Error   string `json:"error"`
	Message string `json:"message"`
}

func returnError(w http.ResponseWriter, message string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusBadRequest)
	errorJSON := ErrorJSON{
		Error:   "Bad Request",
		Message: message,
	}
	json.NewEncoder(w).Encode(errorJSON)
}
<commit_msg>Update exchange rates<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"math"
	"net/http"
	"os"
	"strconv"
)

func main() {
	log.Print("starting server...")
	http.Handle("/convert", &convertHandler{currencies: currencies()})

	// Determine port for HTTP service.
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("defaulting to port %s", port)
	}

	// Start HTTP server.
	log.Printf("listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

// ConvertRequest stores request data
type ConvertRequest struct {
	Value string `json:"value"`
}

// ConvertResponse stores response data
type ConvertResponse struct {
	Answer int `json:"answer"`
}

type convertHandler struct {
	currencies map[string]float64
}

func currencies() map[string]float64 {
	return map[string]float64{
		"JPY": 100,
		"USD": 0.82,
		"EUR": 0.74,
		"BRL": 3.96,
		"AUD": 1.09,
	}
}

// IsValid validates request data
func (s *ConvertRequest) IsValid() bool {
	if len(s.Value) < 4 {
		return false
	}
	if _, err := strconv.Atoi(s.Value[3:]); err != nil {
		return false
	}
	return true
}

func (h *convertHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var convertRequest ConvertRequest
	err := json.NewDecoder(r.Body).Decode(&convertRequest)
	if err != nil {
		returnError(w, "Failed to decode data")
		return
	}
	if !convertRequest.IsValid() {
		returnError(w, fmt.Sprintf("Invalid format: %s", convertRequest.Value))
		return
	}
	if _, ok := h.currencies[convertRequest.Value[:3]]; !ok {
		returnError(w, fmt.Sprintf("Unknown currency: %s", convertRequest.Value[:3]))
		return
	}

	num, _ := strconv.Atoi(convertRequest.Value[3:])
	currency := h.currencies[convertRequest.Value[:3]]
	answer := math.Floor(h.currencies["JPY"] / currency * float64(num))

	convertResponse := ConvertResponse{Answer: int(answer)}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(convertResponse)
}

// ErrorJSON defines error message as JSON
type ErrorJSON struct {
	Error   string `json:"error"`
	Message string `json:"message"`
}

func returnError(w http.ResponseWriter, message string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusBadRequest)
	errorJSON := ErrorJSON{
		Error:   "Bad Request",
		Message: message,
	}
	json.NewEncoder(w).Encode(errorJSON)
}
<|endoftext|>


<commit_before>package Common

import (
	//	"log"
	. "ParquetType"
	"reflect"
	"strings"
)

func BitNum(num uint64) uint64 { //the number of bits needed by the num; 0 needs 0, 1 need 1, 2 need 2, 3 need 2 ....
	var bitn int64 = 63
	for (bitn >= 0) && (((1 << uint64(bitn)) & num) == 0) {
		bitn--
	}
	return uint64(bitn + 1)
}

func Cmp(ai interface{}, bi interface{}) int {
	if ai == nil && bi != nil {
		return -1
	} else if ai == nil && bi == nil {
		return 0
	} else if ai != nil && bi == nil {
		return 1
	}

	name := reflect.TypeOf(ai).Name()
	switch name {
	case "BOOLEAN":
		a, b := 0, 0
		if ai.(BOOLEAN) {
			a = 1
		}
		if bi.(BOOLEAN) {
			b = 1
		}
		return a - b

	case "INT32":
		a, b := ai.(INT32), bi.(INT32)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT64":
		a, b := ai.(INT64), bi.(INT64)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT96":
		a, b := []byte(ai.(INT96)), []byte(bi.(INT96))
		fa, fb := (a[11] >> 7), (b[11] >> 7)
		if fa > fb {
			return -1
		} else if fa < fb {
			return 1
		} else if fa == 1 {
			for i := 11; i >= 0; i-- {
				if a[i] > b[i] {
					return -1
				} else if a[i] < b[i] {
					return 1
				}
			}
			return 0
		} else {
			for i := 11; i >= 0; i-- {
				if a[i] > b[i] {
					return 1
				} else if a[i] < b[i] {
					return -1
				}
			}
			return 0
		}

	case "FLOAT":
		a, b := ai.(FLOAT), bi.(FLOAT)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "DOUBLE":
		a, b := ai.(FLOAT), bi.(FLOAT)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "BYTE_ARRAY":
		a, b := ai.(BYTE_ARRAY), bi.(BYTE_ARRAY)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "FIXED_LEN_BYTE_ARRAY":
		a, b := ai.(FIXED_LEN_BYTE_ARRAY), bi.(FIXED_LEN_BYTE_ARRAY)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UTF8":
		a, b := ai.(UTF8), bi.(UTF8)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT_8":
		a, b := ai.(INT_8), bi.(INT_8)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT_16":
		a, b := ai.(INT_16), bi.(INT_16)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT_32":
		a, b := ai.(INT_32), bi.(INT_32)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT_64":
		a, b := ai.(INT_64), bi.(INT_64)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UINT_8":
		a, b := ai.(UINT_8), bi.(UINT_8)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UINT_16":
		a, b := ai.(UINT_16), bi.(UINT_16)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UINT_32":
		a, b := ai.(UINT_32), bi.(UINT_32)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UINT_64":
		a, b := ai.(UINT_64), bi.(UINT_64)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "DATE":
		a, b := ai.(DATE), bi.(DATE)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "TIME_MILLIS":
		a, b := ai.(TIME_MILLIS), bi.(TIME_MILLIS)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "TIME_MICROS":
		a, b := ai.(TIME_MICROS), bi.(TIME_MICROS)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "TIMESTAMP_MILLIS":
		a, b := ai.(TIMESTAMP_MILLIS), bi.(TIMESTAMP_MILLIS)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "TIMESTAMP_MICROS":
		a, b := ai.(TIMESTAMP_MICROS), bi.(TIMESTAMP_MICROS)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INTERVAL":
		a, b := []byte(ai.(INTERVAL)), []byte(bi.(INTERVAL))
		for i := 11; i >= 0; i-- {
			if a[i] > b[i] {
				return 1
			} else if a[i] < b[i] {
				return -1
			}
		}
		return 0

	case "DCEIMAL":
		a, b := []byte(ai.(DECIMAL)), []byte(bi.(DECIMAL))
		fa, fb := (a[0] >> 7), (b[0] >> 7)
		la, lb := len(a), len(b)
		if fa > fb {
			return -1
		} else if fa < fb {
			return 1
		} else {
			i, j := 0, 0
			for i < la || j < lb {
				ba, bb := byte(0x0), byte(0x0)
				if i < la {
					ba = a[i]
					i++
				}
				if j < lb {
					bb = b[j]
					j++
				}
				if ba > bb {
					if fa == 1 {
						return -1
					} else {
						return 1
					}
				} else if ba < bb {
					if fa == 1 {
						return 1
					} else {
						return -1
					}
				}
			}
			return 0
		}
	}
	return 0
}

func Max(a interface{}, b interface{}) interface{} {
	if a == nil {
		return b
	}
	if b == nil {
		return a
	}
	if Cmp(a, b) > 0 {
		return a
	}
	return b
}

func Min(a interface{}, b interface{}) interface{} {
	if a == nil {
		return b
	}
	if b == nil {
		return a
	}
	if Cmp(a, b) > 0 {
		return b
	}
	return a
}

func SizeOf(val reflect.Value) int64 {
	tk := val.Type().Kind()
	if tk == reflect.Slice {
		var size int64 = 0
		for i := 0; i < val.Len(); i++ {
			size += SizeOf(val.Index(i))
		}
		return size
	} else if tk == reflect.Struct {
		var size int64 = 0
		for i := 0; i < val.Type().NumField(); i++ {
			size += SizeOf(val.Field(i))
		}
		return size

	} else if tk == reflect.Map {
		var size int64 = 0
		keys := val.MapKeys()
		for i := 0; i < len(keys); i++ {
			size += SizeOf(keys[i])
			size += SizeOf(val.MapIndex(keys[i]))
		}
		return size
	}

	switch val.Type().Name() {
	case "BOOLEAN":
		return 1
	case "INT32":
		return 4
	case "INT64":
		return 8
	case "INT96":
		return 12
	case "FLOAT":
		return 4
	case "DOUBLE":
		return 8
	case "BYTE_ARRAY":
		return int64(val.Len())
	case "FIXED_LEN_BYTE_ARRAY":
		return int64(val.Len())
	case "UTF8":
		return int64(val.Len())
	case "INT_8":
		return 4
	case "INT_16":
		return 4
	case "INT_32":
		return 4
	case "INT_64":
		return 8
	case "UINT_8":
		return 4
	case "UINT_16":
		return 4
	case "UINT_32":
		return 4
	case "UINT_64":
		return 8
	case "DATE":
		return 4
	case "TIME_MILLIS":
		return 4
	case "TIME_MICROS":
		return 8
	case "TIMESTAMP_MILLIS":
		return 8
	case "TIMESTAMP_MICROS":
		return 8
	case "INTERVAL":
		return 12
	case "DECIMAL":
		return int64(val.Len())
	}

	return 4
}

func PathToStr(path []string) string {
	return strings.Join(path, ".")
}

func StrToPath(str string) []string {
	return strings.Split(str, ".")
}
<commit_msg>fix SizeOf bug<commit_after>package Common

import (
	//	"log"
	. "ParquetType"
	"reflect"
	"strings"
)

func BitNum(num uint64) uint64 { //the number of bits needed by the num; 0 needs 0, 1 need 1, 2 need 2, 3 need 2 ....
	var bitn int64 = 63
	for (bitn >= 0) && (((1 << uint64(bitn)) & num) == 0) {
		bitn--
	}
	return uint64(bitn + 1)
}

func Cmp(ai interface{}, bi interface{}) int {
	if ai == nil && bi != nil {
		return -1
	} else if ai == nil && bi == nil {
		return 0
	} else if ai != nil && bi == nil {
		return 1
	}

	name := reflect.TypeOf(ai).Name()
	switch name {
	case "BOOLEAN":
		a, b := 0, 0
		if ai.(BOOLEAN) {
			a = 1
		}
		if bi.(BOOLEAN) {
			b = 1
		}
		return a - b

	case "INT32":
		a, b := ai.(INT32), bi.(INT32)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT64":
		a, b := ai.(INT64), bi.(INT64)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT96":
		a, b := []byte(ai.(INT96)), []byte(bi.(INT96))
		fa, fb := (a[11] >> 7), (b[11] >> 7)
		if fa > fb {
			return -1
		} else if fa < fb {
			return 1
		} else if fa == 1 {
			for i := 11; i >= 0; i-- {
				if a[i] > b[i] {
					return -1
				} else if a[i] < b[i] {
					return 1
				}
			}
			return 0
		} else {
			for i := 11; i >= 0; i-- {
				if a[i] > b[i] {
					return 1
				} else if a[i] < b[i] {
					return -1
				}
			}
			return 0
		}

	case "FLOAT":
		a, b := ai.(FLOAT), bi.(FLOAT)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "DOUBLE":
		a, b := ai.(FLOAT), bi.(FLOAT)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "BYTE_ARRAY":
		a, b := ai.(BYTE_ARRAY), bi.(BYTE_ARRAY)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "FIXED_LEN_BYTE_ARRAY":
		a, b := ai.(FIXED_LEN_BYTE_ARRAY), bi.(FIXED_LEN_BYTE_ARRAY)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UTF8":
		a, b := ai.(UTF8), bi.(UTF8)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT_8":
		a, b := ai.(INT_8), bi.(INT_8)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT_16":
		a, b := ai.(INT_16), bi.(INT_16)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT_32":
		a, b := ai.(INT_32), bi.(INT_32)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INT_64":
		a, b := ai.(INT_64), bi.(INT_64)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UINT_8":
		a, b := ai.(UINT_8), bi.(UINT_8)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UINT_16":
		a, b := ai.(UINT_16), bi.(UINT_16)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UINT_32":
		a, b := ai.(UINT_32), bi.(UINT_32)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "UINT_64":
		a, b := ai.(UINT_64), bi.(UINT_64)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "DATE":
		a, b := ai.(DATE), bi.(DATE)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "TIME_MILLIS":
		a, b := ai.(TIME_MILLIS), bi.(TIME_MILLIS)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "TIME_MICROS":
		a, b := ai.(TIME_MICROS), bi.(TIME_MICROS)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "TIMESTAMP_MILLIS":
		a, b := ai.(TIMESTAMP_MILLIS), bi.(TIMESTAMP_MILLIS)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "TIMESTAMP_MICROS":
		a, b := ai.(TIMESTAMP_MICROS), bi.(TIMESTAMP_MICROS)
		if a > b {
			return 1
		} else if a < b {
			return -1
		}
		return 0

	case "INTERVAL":
		a, b := []byte(ai.(INTERVAL)), []byte(bi.(INTERVAL))
		for i := 11; i >= 0; i-- {
			if a[i] > b[i] {
				return 1
			} else if a[i] < b[i] {
				return -1
			}
		}
		return 0

	case "DCEIMAL":
		a, b := []byte(ai.(DECIMAL)), []byte(bi.(DECIMAL))
		fa, fb := (a[0] >> 7), (b[0] >> 7)
		la, lb := len(a), len(b)
		if fa > fb {
			return -1
		} else if fa < fb {
			return 1
		} else {
			i, j := 0, 0
			for i < la || j < lb {
				ba, bb := byte(0x0), byte(0x0)
				if i < la {
					ba = a[i]
					i++
				}
				if j < lb {
					bb = b[j]
					j++
				}
				if ba > bb {
					if fa == 1 {
						return -1
					} else {
						return 1
					}
				} else if ba < bb {
					if fa == 1 {
						return 1
					} else {
						return -1
					}
				}
			}
			return 0
		}
	}
	return 0
}

func Max(a interface{}, b interface{}) interface{} {
	if a == nil {
		return b
	}
	if b == nil {
		return a
	}
	if Cmp(a, b) > 0 {
		return a
	}
	return b
}

func Min(a interface{}, b interface{}) interface{} {
	if a == nil {
		return b
	}
	if b == nil {
		return a
	}
	if Cmp(a, b) > 0 {
		return b
	}
	return a
}

func SizeOf(val reflect.Value) int64 {
	tk := val.Type().Kind()

	if tk == reflect.Ptr {
		if val.IsNil() {
			return 0
		}
		val = val.Elem()
		return SizeOf(val)
	}

	if tk == reflect.Slice {
		var size int64 = 0
		for i := 0; i < val.Len(); i++ {
			size += SizeOf(val.Index(i))
		}
		return size
	} else if tk == reflect.Struct {
		var size int64 = 0
		for i := 0; i < val.Type().NumField(); i++ {
			size += SizeOf(val.Field(i))
		}
		return size

	} else if tk == reflect.Map {
		var size int64 = 0
		keys := val.MapKeys()
		for i := 0; i < len(keys); i++ {
			size += SizeOf(keys[i])
			size += SizeOf(val.MapIndex(keys[i]))
		}
		return size
	}

	switch val.Type().Name() {
	case "BOOLEAN":
		return 1
	case "INT32":
		return 4
	case "INT64":
		return 8
	case "INT96":
		return 12
	case "FLOAT":
		return 4
	case "DOUBLE":
		return 8
	case "BYTE_ARRAY":
		return int64(val.Len())
	case "FIXED_LEN_BYTE_ARRAY":
		return int64(val.Len())
	case "UTF8":
		return int64(val.Len())
	case "INT_8":
		return 4
	case "INT_16":
		return 4
	case "INT_32":
		return 4
	case "INT_64":
		return 8
	case "UINT_8":
		return 4
	case "UINT_16":
		return 4
	case "UINT_32":
		return 4
	case "UINT_64":
		return 8
	case "DATE":
		return 4
	case "TIME_MILLIS":
		return 4
	case "TIME_MICROS":
		return 8
	case "TIMESTAMP_MILLIS":
		return 8
	case "TIMESTAMP_MICROS":
		return 8
	case "INTERVAL":
		return 12
	case "DECIMAL":
		return int64(val.Len())
	}

	return 4
}

func PathToStr(path []string) string {
	return strings.Join(path, ".")
}

func StrToPath(str string) []string {
	return strings.Split(str, ".")
}
<|endoftext|>


<commit_before>package hpcloud

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

/* Server flavours Smallest to Largest */
type Flavor int

const (
	XSmall = Flavor(100) + iota
	Small
	Medium
	Large
	XLarge
	DblXLarge
)

/* Available images */
type ServerImage int

const (
	UbuntuLucid10_04Kernel    = ServerImage(1235)
	UbuntuLucid10_04          = 1236
	UbuntuMaverick10_10Kernel = 1237
	UbuntuMaverick10_10       = 1238
	UbuntuNatty11_04Kernel    = 1239
	UbuntuNatty11_04          = 1240
	UbuntuOneiric11_10        = 5579
	UbuntuPrecise12_04        = 8419
	CentOS5_8Server64         = 54021
	CentOS6_2Server64Kernel   = 1356
	CentOS6_2Server64Ramdisk  = 1357
	CentOS6_2Server64         = 1358
	DebianSqueeze6_0_3Kernel  = 1359
	DebianSqueeze6_0_3Ramdisk = 1360
	DebianSqueeze6_0_3Server  = 1361
	Fedora16Server64          = 16291
	BitNamiDrupal7_14_0       = 22729
	BitNamiWebPack1_2_0       = 22731
	BitNamiDevPack1_0_0       = 4654
	ActiveStateStackatov1_2_6 = 14345
	ActiveStateStackatov2_2_2 = 59297
	ActiveStateStackatov2_2_3 = 60815
	EnterpriseDBPPAS9_1_2     = 9953
	EnterpriseDBPSQL9_1_3     = 9995
)

type Link struct {
	HREF string `json:"href"`
	Rel  string `json:"rel"`
}

/*
  Several embedded types are simply an ID string with a slice of Link
*/
type IDLink struct {
	Name  string `json:"name"`
	ID    string `json:"id"`
	Links []Link `json:"links"`
}

type Image struct {
	I struct {
		Name     string            `json:"name"`
		ID       string            `json:"id"`
		Links    []Link            `json:"links"`
		Progress int               `json:"progress"`
		Metadata map[string]string `json:"metadata"`
		Status   string            `json:"status"`
		Updated  string            `json:"updated"`
	} `json:"image"`
}

type Images struct {
	I []IDLink `json:"images"`
}

/*
  This type describes the JSON data which should be sent to the create
  server resource.
*/
type Server struct {
	ConfigDrive    bool              `json:"config_drive"`
	FlavorRef      Flavor            `json:"flavorRef"`
	ImageRef       ServerImage       `json:"imageRef"`
	MaxCount       int               `json:"max_count"`
	MinCount       int               `json:"min_count"`
	Name           string            `json:"name"`
	Key            string            `json:"key_name"`
	Personality    string            `json:"personality"`
	UserData       string            `json:"user_data"`
	SecurityGroups []IDLink          `json:"security_groups"`
	Metadata       map[string]string `json:"metadata"`
}

/*
  This type describes the JSON response from a successful CreateServer
  call.
*/
type ServerResponse struct {
	S struct {
		Status         string            `json:"status"`
		Updated        string            `json:"update"`
		HostID         string            `json:"hostId"`
		UserID         string            `json:"user_id"`
		Name           string            `json:"name"`
		Links          []Link            `json:"links"`
		Addresses      interface{}       `json:"addresses"`
		TenantID       string            `json:"tenant_id"`
		Image          IDLink            `json:"image"`
		Created        string            `json:"created"`
		UUID           string            `json:"uuid"`
		AccessIPv4     string            `json:"accessIPv4"`
		AccessIPv6     string            `json:"accessIPv6"`
		KeyName        string            `json:"key_name"`
		AdminPass      string            `json:"adminPass"`
		Flavor         IDLink            `json:"flavor"`
		ConfigDrive    string            `json:"config_drive"`
		ID             int64             `json:"id"`
		SecurityGroups []IDLink          `json:"security_groups"`
		Metadata       map[string]string `json:"metadata"`
	} `json:"server"`
}

/*
  CreateServer creates a new server in the HPCloud using the
  settings found in the Server instance passed to this function.

  This function implements the interface as described in:-
  * https://docs.hpcloud.com/api/compute/
  * section 4.4.5.2 Create Server
*/
func (a Access) CreateServer(s Server) (*ServerResponse, error) {
	b, err := s.MarshalJSON()
	if err != nil {
		return nil, err
	}

	body, err := a.baseComputeRequest("servers", "POST",
		strings.NewReader(string(b)),
	)
	if err != nil {
		return nil, err
	}
	sr := &ServerResponse{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return sr, nil
}

/*
  DeleteServer deletes the server with the `server_id`.

  This function implements the interface described in:-
  * https://docs.hpcloud.com/api/compute/
  * Section 4.4.6.3 Delete Server
*/
func (a Access) DeleteServer(server_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s", server_id),
		"DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

/*
  RebootServer will reboot the server with the `server_id`.

  This function implements the interface described in:-
  * https://docs.hpcloud.com/api/compute/
  * Section 4.4.7.1 Reboot Server
*/
func (a Access) RebootServer(server_id string) error {
	/*
			 The docs mention that a hard reboot will be used
		     no matter what, so there's no point making a type
		     or make the type of reboot an option
	*/
	s := `{"reboot":{"type":"HARD"}}`
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s/action", server_id),
		"POST", strings.NewReader(s),
	)
	if err != nil {
		return err
	}
	return nil

}

/*
  ListFlavors will list all the available flavours
  on the HPCloud compute API.
*/
func (a Access) ListFlavors() (*Flavors, error) {
	body, err := a.baseComputeRequest("flavors", "GET", nil)
	if err != nil {
		return nil, err
	}

	fl := &Flavors{}
	err = json.Unmarshal(body, fl)
	if err != nil {
		return nil, err
	}
	return fl, nil
}

func (a Access) ListImages() (*Images, error) {
	body, err := a.baseComputeRequest("images", "GET", nil)
	if err != nil {
		return nil, err
	}
	im := &Images{}
	err = json.Unmarshal(body, im)
	if err != nil {
		return nil, err
	}
	return im, nil
}

func (a Access) DeleteImage(image_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) ListImage(image_id string) (*Image, error) {
	body, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "GET", nil,
	)
	if err != nil {
		return nil, err
	}
	i := &Image{}
	err = json.Unmarshal(body, i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

/*
  baseComputeRequest encapsulates the main basic request
  which is done for each endpoint in the Compute API.

  In the ComputeAPI all endpoints generally succeed on
  a 200/202 return code and fail on the usual fail codes.

  We simply check for the known good return codes and return
  the body in those cases or we fail with the appropriate
  response.
*/
func (a Access) baseComputeRequest(url, method string, b io.Reader) ([]byte, error) {
	path := fmt.Sprintf("%s%s/%s", COMPUTE_URL, a.TenantID, url)
	req, err := http.NewRequest(method, path, b)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Auth-Token", a.AuthToken())
	req.Header.Add("Content-type", "application/json")
	resp, err := a.Client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch resp.StatusCode {
	case http.StatusAccepted:
	case http.StatusNonAuthoritativeInfo:
	case http.StatusOK:
		return body, nil
	case http.StatusNotFound:
		nf := &NotFound{}
		err = json.Unmarshal(body, nf)
		if err != nil {
			return nil, err
		}
		return nil, errors.New(nf.NF.Message)
	default:
		br := &BadRequest{}
		err = json.Unmarshal(body, br)
		if err != nil {
			return nil, err
		}
		return nil, errors.New(br.B.Message)
	}
	panic("Unreachable")
}

/*
  MarshalJSON implements the Marshaler interface for the
  Server type.

  We implement this interface because when creating a server
  we have optional values and since Go has zero-values and
  does *not* have configurable zero values we need to make
  sure that zero-values are converted to known good values.

  As such:
    * FlavorRef is checked if it's a valid reference.
    * Ditto for ImageRef.
    * Name cannot be blank.
    * If the key is missing, it'll not put anything in.
    * The config_drive defaults to false anyway, no need
      to send a false value.
    * Min/MaxCount are ignored if they are zero.
    * UserData is ignored if it's a blank string.
    * Personality is ignored if it's a blank string.
    * Metadata/SecurityGroups are ignored if they have len(0)
*/
func (s Server) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString("")
	b.WriteString(`{"server":{`)
	/* The available images are 100-105, x-small to x-large. */
	if s.FlavorRef < 100 || s.FlavorRef > 105 {
		return []byte{},
			errors.New("Flavor Reference refers to a non-existant flavour.")
	} else {
		b.WriteString(fmt.Sprintf(`"flavorRef":%d`, s.FlavorRef))
	}
	if s.ImageRef == 0 {
		return []byte{},
			errors.New("An image name is required.")
	} else {
		b.WriteString(fmt.Sprintf(`,"imageRef":%d`, s.ImageRef))
	}
	if s.Name == "" {
		return []byte{},
			errors.New("A name is required")
	} else {
		b.WriteString(fmt.Sprintf(`,"name":"%s"`, s.Name))
	}

	/* Optional items */
	/* The max size of a personality string is 255 bytes. */
	if len(s.Personality) > 255 {
		return []byte{},
			errors.New("Server's personality cannot have >255 bytes.")
	} else if s.Personality != "" {
		b.WriteString(fmt.Sprintf(`,"personality":"%s",`, s.Personality))
	}
	if s.Key != "" {
		b.WriteString(fmt.Sprintf(`,"key_name":"%s"`, s.Key))
	}
	if s.ConfigDrive {
		b.WriteString(`,"config_drive": true`)
	}
	if s.MinCount > 0 {
		b.WriteString(fmt.Sprintf(`,"min_count":%d`, s.MinCount))
	}
	if s.MaxCount > 0 {
		b.WriteString(fmt.Sprintf(`,"max_count":%d`, s.MaxCount))
	}
	if s.UserData != "" {
		/* user_data needs to be base64'd */
		newb := make([]byte, 0, len(s.UserData))
		base64.StdEncoding.Encode([]byte(s.UserData), newb)
		b.WriteString(fmt.Sprintf(`,"user_data": "%s",`, string(newb)))
	}

	/* Ignore the metadata if there isn't any, it's optional. */
	if len(s.Metadata) > 0 {
		fmt.Println(len(s.Metadata))
		b.WriteString(`,"metadata":{`)
		cnt := 0
		for key, value := range s.Metadata {
			b.WriteString(fmt.Sprintf(`"%s": "%s"`, key, value))
			if cnt+1 != len(s.Metadata) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("}")
			}
		}
	}
	/* Ignore the Security Groups if there isn't any, it's optional. */
	if len(s.SecurityGroups) > 0 {
		b.WriteString(`,"security_groups":[`)
		cnt := 0
		for _, sg := range s.SecurityGroups {
			b.WriteString(fmt.Sprintf(`{"name": "%s"}`, sg.Name))
			if cnt+1 != len(s.SecurityGroups) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("]")
			}
		}
	}
	b.WriteString("}}")
	return b.Bytes(), nil
}
<commit_msg>Use the FailureResponse methods.<commit_after>package hpcloud

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

/* Server flavours Smallest to Largest */
type Flavor int

const (
	XSmall = Flavor(100) + iota
	Small
	Medium
	Large
	XLarge
	DblXLarge
)

/* Available images */
type ServerImage int

const (
	UbuntuLucid10_04Kernel    = ServerImage(1235)
	UbuntuLucid10_04          = 1236
	UbuntuMaverick10_10Kernel = 1237
	UbuntuMaverick10_10       = 1238
	UbuntuNatty11_04Kernel    = 1239
	UbuntuNatty11_04          = 1240
	UbuntuOneiric11_10        = 5579
	UbuntuPrecise12_04        = 8419
	CentOS5_8Server64         = 54021
	CentOS6_2Server64Kernel   = 1356
	CentOS6_2Server64Ramdisk  = 1357
	CentOS6_2Server64         = 1358
	DebianSqueeze6_0_3Kernel  = 1359
	DebianSqueeze6_0_3Ramdisk = 1360
	DebianSqueeze6_0_3Server  = 1361
	Fedora16Server64          = 16291
	BitNamiDrupal7_14_0       = 22729
	BitNamiWebPack1_2_0       = 22731
	BitNamiDevPack1_0_0       = 4654
	ActiveStateStackatov1_2_6 = 14345
	ActiveStateStackatov2_2_2 = 59297
	ActiveStateStackatov2_2_3 = 60815
	EnterpriseDBPPAS9_1_2     = 9953
	EnterpriseDBPSQL9_1_3     = 9995
)

type Link struct {
	HREF string `json:"href"`
	Rel  string `json:"rel"`
}

/*
  Several embedded types are simply an ID string with a slice of Link
*/
type IDLink struct {
	Name  string `json:"name"`
	ID    string `json:"id"`
	Links []Link `json:"links"`
}

type Image struct {
	I struct {
		Name     string            `json:"name"`
		ID       string            `json:"id"`
		Links    []Link            `json:"links"`
		Progress int               `json:"progress"`
		Metadata map[string]string `json:"metadata"`
		Status   string            `json:"status"`
		Updated  string            `json:"updated"`
	} `json:"image"`
}

type Images struct {
	I []IDLink `json:"images"`
}

/*
  This type describes the JSON data which should be sent to the create
  server resource.
*/
type Server struct {
	ConfigDrive    bool              `json:"config_drive"`
	FlavorRef      Flavor            `json:"flavorRef"`
	ImageRef       ServerImage       `json:"imageRef"`
	MaxCount       int               `json:"max_count"`
	MinCount       int               `json:"min_count"`
	Name           string            `json:"name"`
	Key            string            `json:"key_name"`
	Personality    string            `json:"personality"`
	UserData       string            `json:"user_data"`
	SecurityGroups []IDLink          `json:"security_groups"`
	Metadata       map[string]string `json:"metadata"`
}

/*
  This type describes the JSON response from a successful CreateServer
  call.
*/
type ServerResponse struct {
	S struct {
		Status         string            `json:"status"`
		Updated        string            `json:"update"`
		HostID         string            `json:"hostId"`
		UserID         string            `json:"user_id"`
		Name           string            `json:"name"`
		Links          []Link            `json:"links"`
		Addresses      interface{}       `json:"addresses"`
		TenantID       string            `json:"tenant_id"`
		Image          IDLink            `json:"image"`
		Created        string            `json:"created"`
		UUID           string            `json:"uuid"`
		AccessIPv4     string            `json:"accessIPv4"`
		AccessIPv6     string            `json:"accessIPv6"`
		KeyName        string            `json:"key_name"`
		AdminPass      string            `json:"adminPass"`
		Flavor         IDLink            `json:"flavor"`
		ConfigDrive    string            `json:"config_drive"`
		ID             int64             `json:"id"`
		SecurityGroups []IDLink          `json:"security_groups"`
		Metadata       map[string]string `json:"metadata"`
	} `json:"server"`
}

/*
  CreateServer creates a new server in the HPCloud using the
  settings found in the Server instance passed to this function.

  This function implements the interface as described in:-
  * https://docs.hpcloud.com/api/compute/
  * section 4.4.5.2 Create Server
*/
func (a Access) CreateServer(s Server) (*ServerResponse, error) {
	b, err := s.MarshalJSON()
	if err != nil {
		return nil, err
	}

	body, err := a.baseComputeRequest("servers", "POST",
		strings.NewReader(string(b)),
	)
	if err != nil {
		return nil, err
	}
	sr := &ServerResponse{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return sr, nil
}

/*
  DeleteServer deletes the server with the `server_id`.

  This function implements the interface described in:-
  * https://docs.hpcloud.com/api/compute/
  * Section 4.4.6.3 Delete Server
*/
func (a Access) DeleteServer(server_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s", server_id),
		"DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

/*
  RebootServer will reboot the server with the `server_id`.

  This function implements the interface described in:-
  * https://docs.hpcloud.com/api/compute/
  * Section 4.4.7.1 Reboot Server
*/
func (a Access) RebootServer(server_id string) error {
	/*
			 The docs mention that a hard reboot will be used
		     no matter what, so there's no point making a type
		     or make the type of reboot an option
	*/
	s := `{"reboot":{"type":"HARD"}}`
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s/action", server_id),
		"POST", strings.NewReader(s),
	)
	if err != nil {
		return err
	}
	return nil

}

/*
  ListFlavors will list all the available flavours
  on the HPCloud compute API.
*/
func (a Access) ListFlavors() (*Flavors, error) {
	body, err := a.baseComputeRequest("flavors", "GET", nil)
	if err != nil {
		return nil, err
	}

	fl := &Flavors{}
	err = json.Unmarshal(body, fl)
	if err != nil {
		return nil, err
	}
	return fl, nil
}

func (a Access) ListImages() (*Images, error) {
	body, err := a.baseComputeRequest("images", "GET", nil)
	if err != nil {
		return nil, err
	}
	im := &Images{}
	err = json.Unmarshal(body, im)
	if err != nil {
		return nil, err
	}
	return im, nil
}

func (a Access) DeleteImage(image_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) ListImage(image_id string) (*Image, error) {
	body, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "GET", nil,
	)
	if err != nil {
		return nil, err
	}
	i := &Image{}
	err = json.Unmarshal(body, i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

/*
  baseComputeRequest encapsulates the main basic request
  which is done for each endpoint in the Compute API.

  In the ComputeAPI all endpoints generally succeed on
  a 200/202 return code and fail on the usual fail codes.

  We simply check for the known good return codes and return
  the body in those cases or we fail with the appropriate
  response.
*/
func (a Access) baseComputeRequest(url, method string, b io.Reader) ([]byte, error) {
	path := fmt.Sprintf("%s%s/%s", COMPUTE_URL, a.TenantID, url)
	req, err := http.NewRequest(method, path, b)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Auth-Token", a.AuthToken())
	req.Header.Add("Content-type", "application/json")
	resp, err := a.Client.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	switch resp.StatusCode {
	case http.StatusAccepted:
	case http.StatusNonAuthoritativeInfo:
	case http.StatusOK:
		return body, nil
	case http.StatusNotFound:
		nf := &NotFound{}
		err = json.Unmarshal(body, nf)
		if err != nil {
			return nil, err
		}
		return nil, errors.New(nf.Message())
	default:
		br := &BadRequest{}
		err = json.Unmarshal(body, br)
		if err != nil {
			return nil, err
		}
		return nil, errors.New(br.Message())
	}
	panic("Unreachable")
}

/*
  MarshalJSON implements the Marshaler interface for the
  Server type.

  We implement this interface because when creating a server
  we have optional values and since Go has zero-values and
  does *not* have configurable zero values we need to make
  sure that zero-values are converted to known good values.

  As such:
    * FlavorRef is checked if it's a valid reference.
    * Ditto for ImageRef.
    * Name cannot be blank.
    * If the key is missing, it'll not put anything in.
    * The config_drive defaults to false anyway, no need
      to send a false value.
    * Min/MaxCount are ignored if they are zero.
    * UserData is ignored if it's a blank string.
    * Personality is ignored if it's a blank string.
    * Metadata/SecurityGroups are ignored if they have len(0)
*/
func (s Server) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString("")
	b.WriteString(`{"server":{`)
	/* The available images are 100-105, x-small to x-large. */
	if s.FlavorRef < 100 || s.FlavorRef > 105 {
		return []byte{},
			errors.New("Flavor Reference refers to a non-existant flavour.")
	} else {
		b.WriteString(fmt.Sprintf(`"flavorRef":%d`, s.FlavorRef))
	}
	if s.ImageRef == 0 {
		return []byte{},
			errors.New("An image name is required.")
	} else {
		b.WriteString(fmt.Sprintf(`,"imageRef":%d`, s.ImageRef))
	}
	if s.Name == "" {
		return []byte{},
			errors.New("A name is required")
	} else {
		b.WriteString(fmt.Sprintf(`,"name":"%s"`, s.Name))
	}

	/* Optional items */
	/* The max size of a personality string is 255 bytes. */
	if len(s.Personality) > 255 {
		return []byte{},
			errors.New("Server's personality cannot have >255 bytes.")
	} else if s.Personality != "" {
		b.WriteString(fmt.Sprintf(`,"personality":"%s",`, s.Personality))
	}
	if s.Key != "" {
		b.WriteString(fmt.Sprintf(`,"key_name":"%s"`, s.Key))
	}
	if s.ConfigDrive {
		b.WriteString(`,"config_drive": true`)
	}
	if s.MinCount > 0 {
		b.WriteString(fmt.Sprintf(`,"min_count":%d`, s.MinCount))
	}
	if s.MaxCount > 0 {
		b.WriteString(fmt.Sprintf(`,"max_count":%d`, s.MaxCount))
	}
	if s.UserData != "" {
		/* user_data needs to be base64'd */
		newb := make([]byte, 0, len(s.UserData))
		base64.StdEncoding.Encode([]byte(s.UserData), newb)
		b.WriteString(fmt.Sprintf(`,"user_data": "%s",`, string(newb)))
	}

	/* Ignore the metadata if there isn't any, it's optional. */
	if len(s.Metadata) > 0 {
		fmt.Println(len(s.Metadata))
		b.WriteString(`,"metadata":{`)
		cnt := 0
		for key, value := range s.Metadata {
			b.WriteString(fmt.Sprintf(`"%s": "%s"`, key, value))
			if cnt+1 != len(s.Metadata) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("}")
			}
		}
	}
	/* Ignore the Security Groups if there isn't any, it's optional. */
	if len(s.SecurityGroups) > 0 {
		b.WriteString(`,"security_groups":[`)
		cnt := 0
		for _, sg := range s.SecurityGroups {
			b.WriteString(fmt.Sprintf(`{"name": "%s"}`, sg.Name))
			if cnt+1 != len(s.SecurityGroups) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("]")
			}
		}
	}
	b.WriteString("}}")
	return b.Bytes(), nil
}
<|endoftext|>