<commit_before>package terraform

import (
	"fmt"
	"sync"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/depgraph"
)

// Terraform is the primary structure that is used to interact with
// Terraform from code, and can perform operations such as returning
// all resources, a resource tree, a specific resource, etc.
type Terraform struct {
	config    *config.Config
	graph     *depgraph.Graph
	mapping   map[*config.Resource]*terraformProvider
	variables map[string]string
}

// terraformProvider contains internal state information about a resource
// provider for Terraform.
type terraformProvider struct {
	Provider ResourceProvider
	Config   *config.ProviderConfig

	sync.Once
}

// This is a function type used to implement a walker for the resource
// tree internally on the Terraform structure.
type genericWalkFunc func(*Resource) (map[string]string, error)

// Config is the configuration that must be given to instantiate
// a Terraform structure.
type Config struct {
	Config    *config.Config
	Providers map[string]ResourceProviderFactory
	Variables map[string]string

	computedPlaceholder string
}

// New creates a new Terraform structure, initializes resource providers
// for the given configuration, etc.
//
// Semantic checks of the entire configuration structure are done at this
// time, as well as richer checks such as verifying that the resource providers
// can be properly initialized, can be configured, etc.
func New(c *Config) (*Terraform, error) {
	var errs []error

	// Calculate the computed key placeholder
	c.computedPlaceholder = "tf_computed_placeholder"

	// Validate that all required variables have values
	if err := smcVariables(c); err != nil {
		errs = append(errs, err...)
	}

	// Match all the resources with a provider and initialize the providers
	mapping, err := smcProviders(c)
	if err != nil {
		errs = append(errs, err...)
	}

	// Validate all the configurations, once.
	tps := make(map[*terraformProvider]struct{})
	for _, tp := range mapping {
		if _, ok := tps[tp]; !ok {
			tps[tp] = struct{}{}
		}
	}
	for tp, _ := range tps {
		var rc *ResourceConfig
		if tp.Config != nil {
			rc = NewResourceConfig(tp.Config.RawConfig)
		}

		_, tpErrs := tp.Provider.Validate(rc)
		if len(tpErrs) > 0 {
			errs = append(errs, tpErrs...)
		}
	}

	// Build the resource graph
	graph := c.Config.Graph()
	if err := graph.Validate(); err != nil {
		errs = append(errs, fmt.Errorf(
			"Resource graph has an error: %s", err))
	}

	// If we accumulated any errors, then return them all
	if len(errs) > 0 {
		return nil, &MultiError{Errors: errs}
	}

	return &Terraform{
		config:    c.Config,
		graph:     graph,
		mapping:   mapping,
		variables: c.Variables,
	}, nil
}

func (t *Terraform) Apply(p *Plan) (*State, error) {
	result := new(State)
	err := t.graph.Walk(t.applyWalkFn(p, result))
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (t *Terraform) Plan(s *State) (*Plan, error) {
	result := new(Plan)
	err := t.graph.Walk(t.planWalkFn(s, result))
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (t *Terraform) Refresh(*State) (*State, error) {
	return nil, nil
}

func (t *Terraform) applyWalkFn(
	p *Plan,
	result *State) depgraph.WalkFunc {
	var l sync.Mutex

	// Initialize the result
	result.init()

	cb := func(r *Resource) (map[string]string, error) {
		// Get the latest diff since there are no computed values anymore
		diff, err := r.Provider.Diff(r.State, r.Config)
		if err != nil {
			return nil, err
		}

		rs, err := r.Provider.Apply(r.State, diff)
		if err != nil {
			return nil, err
		}

		// If no state was returned, then no variables were updated so
		// just return.
		if rs == nil {
			return nil, nil
		}

		// Update the resulting diff
		l.Lock()
		result.Resources[r.Id] = rs
		l.Unlock()

		// Determine the new state and update variables
		vars := make(map[string]string)
		for ak, av := range rs.Attributes {
			vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av
		}

		return vars, nil
	}

	return t.genericWalkFn(p.State, p.Diff, p.Vars, cb)
}

func (t *Terraform) planWalkFn(
	state *State, result *Plan) depgraph.WalkFunc {
	var l sync.Mutex

	// Initialize the result diff so we can write to it
	result.init()

	// Write our configuration out
	result.Config = t.config

	// Copy the variables
	result.Vars = make(map[string]string)
	for k, v := range t.variables {
		result.Vars[k] = v
	}

	cb := func(r *Resource) (map[string]string, error) {
		// Refresh the state so we're working with the latest resource info
		newState, err := r.Provider.Refresh(r.State)
		if err != nil {
			return nil, err
		}

		// Get a diff from the newest state
		diff, err := r.Provider.Diff(newState, r.Config)
		if err != nil {
			return nil, err
		}

		l.Lock()
		if !diff.Empty() {
			result.Diff.Resources[r.Id] = diff
		}
		result.State.Resources[r.Id] = newState
		l.Unlock()

		// Determine the new state and update variables
		vars := make(map[string]string)
		rs := newState
		if !diff.Empty() {
			rs = r.State.MergeDiff(diff)
		}
		if rs != nil {
			for ak, av := range rs.Attributes {
				vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av
			}
		}

		return vars, nil
	}

	return t.genericWalkFn(state, nil, t.variables, cb)
}

func (t *Terraform) genericWalkFn(
	state *State,
	diff *Diff,
	invars map[string]string,
	cb genericWalkFunc) depgraph.WalkFunc {
	var l sync.Mutex

	// Initialize the variables for application
	vars := make(map[string]string)
	for k, v := range invars {
		vars[fmt.Sprintf("var.%s", k)] = v
	}

	return func(n *depgraph.Noun) error {
		// If it is the root node, ignore
		if n.Meta == nil {
			return nil
		}

		switch n.Meta.(type) {
		case *config.ProviderConfig:
			// Ignore, we don't treat this any differently since we always
			// initialize the provider on first use and use a lock to make
			// sure we only do this once.
			return nil
		case *config.Resource:
			// Continue
		}

		r := n.Meta.(*config.Resource)
		p := t.mapping[r]
		if p == nil {
			panic(fmt.Sprintf("No provider for resource: %s", r.Id()))
		}

		// Initialize the provider if we haven't already
		if err := p.init(vars); err != nil {
			return err
		}

		// Get the resource state
		var rs *ResourceState
		if state != nil {
			rs = state.Resources[r.Id()]
		}

		// Get the resource diff
		var rd *ResourceDiff
		if diff != nil {
			rd = diff.Resources[r.Id()]
		}

		if len(vars) > 0 {
			if err := r.RawConfig.Interpolate(vars); err != nil {
				panic(fmt.Sprintf("Interpolate error: %s", err))
			}
		}

		// If we have no state, then create an empty state with the
		// type fulfilled at the least.
		if rs == nil {
			rs = new(ResourceState)
		}
		rs.Type = r.Type

		// Call the callack
		newVars, err := cb(&Resource{
			Id:       r.Id(),
			Config:   NewResourceConfig(r.RawConfig),
			Diff:     rd,
			Provider: p.Provider,
			State:    rs,
		})
		if err != nil {
			return err
		}

		if len(newVars) > 0 {
			// Acquire a lock since this function is called in parallel
			l.Lock()
			defer l.Unlock()

			// Update variables
			for k, v := range newVars {
				vars[k] = v
			}
		}

		return nil
	}
}

func (t *terraformProvider) init(vars map[string]string) (err error) {
	t.Once.Do(func() {
		var rc *ResourceConfig
		if t.Config != nil {
			if err := t.Config.RawConfig.Interpolate(vars); err != nil {
				panic(err)
			}

			rc = &ResourceConfig{
				ComputedKeys: t.Config.RawConfig.UnknownKeys(),
				Raw:          t.Config.RawConfig.Config(),
			}
		}

		err = t.Provider.Configure(rc)
	})

	return
}
<commit_msg>terraform: some comments<commit_after>package terraform

import (
	"fmt"
	"sync"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/depgraph"
)

// Terraform is the primary structure that is used to interact with
// Terraform from code, and can perform operations such as returning
// all resources, a resource tree, a specific resource, etc.
type Terraform struct {
	config    *config.Config
	graph     *depgraph.Graph
	mapping   map[*config.Resource]*terraformProvider
	variables map[string]string
}

// terraformProvider contains internal state information about a resource
// provider for Terraform.
type terraformProvider struct {
	Provider ResourceProvider
	Config   *config.ProviderConfig

	sync.Once
}

// This is a function type used to implement a walker for the resource
// tree internally on the Terraform structure.
type genericWalkFunc func(*Resource) (map[string]string, error)

// Config is the configuration that must be given to instantiate
// a Terraform structure.
type Config struct {
	Config    *config.Config
	Providers map[string]ResourceProviderFactory
	Variables map[string]string

	computedPlaceholder string
}

// New creates a new Terraform structure, initializes resource providers
// for the given configuration, etc.
//
// Semantic checks of the entire configuration structure are done at this
// time, as well as richer checks such as verifying that the resource providers
// can be properly initialized, can be configured, etc.
func New(c *Config) (*Terraform, error) {
	var errs []error

	// Calculate the computed key placeholder
	c.computedPlaceholder = "tf_computed_placeholder"

	// Validate that all required variables have values
	if err := smcVariables(c); err != nil {
		errs = append(errs, err...)
	}

	// Match all the resources with a provider and initialize the providers
	mapping, err := smcProviders(c)
	if err != nil {
		errs = append(errs, err...)
	}

	// Validate all the configurations, once.
	tps := make(map[*terraformProvider]struct{})
	for _, tp := range mapping {
		if _, ok := tps[tp]; !ok {
			tps[tp] = struct{}{}
		}
	}
	for tp, _ := range tps {
		var rc *ResourceConfig
		if tp.Config != nil {
			rc = NewResourceConfig(tp.Config.RawConfig)
		}

		_, tpErrs := tp.Provider.Validate(rc)
		if len(tpErrs) > 0 {
			errs = append(errs, tpErrs...)
		}
	}

	// Build the resource graph
	graph := c.Config.Graph()
	if err := graph.Validate(); err != nil {
		errs = append(errs, fmt.Errorf(
			"Resource graph has an error: %s", err))
	}

	// If we accumulated any errors, then return them all
	if len(errs) > 0 {
		return nil, &MultiError{Errors: errs}
	}

	return &Terraform{
		config:    c.Config,
		graph:     graph,
		mapping:   mapping,
		variables: c.Variables,
	}, nil
}

func (t *Terraform) Apply(p *Plan) (*State, error) {
	result := new(State)
	err := t.graph.Walk(t.applyWalkFn(p, result))
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (t *Terraform) Plan(s *State) (*Plan, error) {
	result := new(Plan)
	err := t.graph.Walk(t.planWalkFn(s, result))
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (t *Terraform) Refresh(*State) (*State, error) {
	return nil, nil
}

func (t *Terraform) applyWalkFn(
	p *Plan,
	result *State) depgraph.WalkFunc {
	var l sync.Mutex

	// Initialize the result
	result.init()

	cb := func(r *Resource) (map[string]string, error) {
		// Get the latest diff since there are no computed values anymore
		diff, err := r.Provider.Diff(r.State, r.Config)
		if err != nil {
			return nil, err
		}

		// TODO(mitchellh): we need to verify the diff doesn't change
		// anything and that the diff has no computed values (pre-computed)

		// With the completed diff, apply!
		rs, err := r.Provider.Apply(r.State, diff)
		if err != nil {
			return nil, err
		}

		// If no state was returned, then no variables were updated so
		// just return.
		if rs == nil {
			return nil, nil
		}

		// Update the resulting diff
		l.Lock()
		result.Resources[r.Id] = rs
		l.Unlock()

		// Determine the new state and update variables
		vars := make(map[string]string)
		for ak, av := range rs.Attributes {
			vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av
		}

		return vars, nil
	}

	return t.genericWalkFn(p.State, p.Diff, p.Vars, cb)
}

func (t *Terraform) planWalkFn(
	state *State, result *Plan) depgraph.WalkFunc {
	var l sync.Mutex

	// Initialize the result diff so we can write to it
	result.init()

	// Write our configuration out
	result.Config = t.config

	// Copy the variables
	result.Vars = make(map[string]string)
	for k, v := range t.variables {
		result.Vars[k] = v
	}

	cb := func(r *Resource) (map[string]string, error) {
		// Refresh the state so we're working with the latest resource info
		newState, err := r.Provider.Refresh(r.State)
		if err != nil {
			return nil, err
		}

		// Get a diff from the newest state
		diff, err := r.Provider.Diff(newState, r.Config)
		if err != nil {
			return nil, err
		}

		l.Lock()
		if !diff.Empty() {
			result.Diff.Resources[r.Id] = diff
		}
		result.State.Resources[r.Id] = newState
		l.Unlock()

		// Determine the new state and update variables
		vars := make(map[string]string)
		rs := newState
		if !diff.Empty() {
			rs = r.State.MergeDiff(diff)
		}
		if rs != nil {
			for ak, av := range rs.Attributes {
				vars[fmt.Sprintf("%s.%s", r.Id, ak)] = av
			}
		}

		return vars, nil
	}

	return t.genericWalkFn(state, nil, t.variables, cb)
}

func (t *Terraform) genericWalkFn(
	state *State,
	diff *Diff,
	invars map[string]string,
	cb genericWalkFunc) depgraph.WalkFunc {
	var l sync.Mutex

	// Initialize the variables for application
	vars := make(map[string]string)
	for k, v := range invars {
		vars[fmt.Sprintf("var.%s", k)] = v
	}

	return func(n *depgraph.Noun) error {
		// If it is the root node, ignore
		if n.Meta == nil {
			return nil
		}

		switch n.Meta.(type) {
		case *config.ProviderConfig:
			// Ignore, we don't treat this any differently since we always
			// initialize the provider on first use and use a lock to make
			// sure we only do this once.
			return nil
		case *config.Resource:
			// Continue
		}

		r := n.Meta.(*config.Resource)
		p := t.mapping[r]
		if p == nil {
			panic(fmt.Sprintf("No provider for resource: %s", r.Id()))
		}

		// Initialize the provider if we haven't already
		if err := p.init(vars); err != nil {
			return err
		}

		// Get the resource state
		var rs *ResourceState
		if state != nil {
			rs = state.Resources[r.Id()]
		}

		// Get the resource diff
		var rd *ResourceDiff
		if diff != nil {
			rd = diff.Resources[r.Id()]
		}

		if len(vars) > 0 {
			if err := r.RawConfig.Interpolate(vars); err != nil {
				panic(fmt.Sprintf("Interpolate error: %s", err))
			}
		}

		// If we have no state, then create an empty state with the
		// type fulfilled at the least.
		if rs == nil {
			rs = new(ResourceState)
		}
		rs.Type = r.Type

		// Call the callack
		newVars, err := cb(&Resource{
			Id:       r.Id(),
			Config:   NewResourceConfig(r.RawConfig),
			Diff:     rd,
			Provider: p.Provider,
			State:    rs,
		})
		if err != nil {
			return err
		}

		if len(newVars) > 0 {
			// Acquire a lock since this function is called in parallel
			l.Lock()
			defer l.Unlock()

			// Update variables
			for k, v := range newVars {
				vars[k] = v
			}
		}

		return nil
	}
}

func (t *terraformProvider) init(vars map[string]string) (err error) {
	t.Once.Do(func() {
		var rc *ResourceConfig
		if t.Config != nil {
			if err := t.Config.RawConfig.Interpolate(vars); err != nil {
				panic(err)
			}

			rc = &ResourceConfig{
				ComputedKeys: t.Config.RawConfig.UnknownKeys(),
				Raw:          t.Config.RawConfig.Config(),
			}
		}

		err = t.Provider.Configure(rc)
	})

	return
}
<|endoftext|>


<commit_before>package server

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"net"
)

func TestParsingOfEnvironmentVariables(t *testing.T) {
	a := assert.New(t)

	originalArgs := os.Args
	os.Args = []string{os.Args[0]}
	defer func() { os.Args = originalArgs }()

	// given: some environment variables
	os.Setenv("GUBLE_HTTP_LISTEN", "http_listen")
	defer os.Unsetenv("GUBLE_HTTP_LISTEN")

	os.Setenv("GUBLE_LOG", "debug")
	defer os.Unsetenv("GUBLE_LOG")

	os.Setenv("GUBLE_ENV", "dev")
	defer os.Unsetenv("GUBLE_ENV")

	os.Setenv("GUBLE_KVS", "kvs-backend")
	defer os.Unsetenv("GUBLE_KVS")

	os.Setenv("GUBLE_STORAGE_PATH", os.TempDir())
	defer os.Unsetenv("GUBLE_STORAGE_PATH")

	os.Setenv("GUBLE_HEALTH_ENDPOINT", "health_endpoint")
	defer os.Unsetenv("GUBLE_HEALTH_ENDPOINT")

	os.Setenv("GUBLE_METRICS_ENDPOINT", "metrics_endpoint")
	defer os.Unsetenv("GUBLE_METRICS_ENDPOINT")

	os.Setenv("GUBLE_MS", "ms-backend")
	defer os.Unsetenv("GUBLE_MS")

	os.Setenv("GUBLE_GCM", "true")
	defer os.Unsetenv("GUBLE_GCM")

	os.Setenv("GUBLE_GCM_API_KEY", "gcm-api-key")
	defer os.Unsetenv("GUBLE_GCM_API_KEY")

	os.Setenv("GUBLE_GCM_WORKERS", "3")
	defer os.Unsetenv("GUBLE_GCM_WORKERS")

	os.Setenv("GUBLE_NODE_ID", "1")
	defer os.Unsetenv("GUBLE_NODE_ID")

	os.Setenv("GUBLE_NODE_PORT", "10000")
	defer os.Unsetenv("GUBLE_NODE_PORT")

	os.Setenv("GUBLE_PG_HOST", "pg-host")
	defer os.Unsetenv("GUBLE_PG_HOST")

	os.Setenv("GUBLE_PG_PORT", "5432")
	defer os.Unsetenv("GUBLE_PG_PORT")

	os.Setenv("GUBLE_PG_USER", "pg-user")
	defer os.Unsetenv("GUBLE_PG_USER")

	os.Setenv("GUBLE_PG_PASSWORD", "pg-password")
	defer os.Unsetenv("GUBLE_PG_PASSWORD")

	os.Setenv("GUBLE_PG_DBNAME", "pg-dbname")
	defer os.Unsetenv("GUBLE_PG_DBNAME")

	os.Setenv("GUBLE_NODE_REMOTES", "127.0.0.1:8080 127.0.0.1:20002")
	defer os.Unsetenv("GUBLE_NODE_REMOTES")

	// when we parse the arguments from environment variables
	parseConfig()

	// then the parsed parameters are correctly set
	assertArguments(a)
}

func TestParsingArgs(t *testing.T) {
	a := assert.New(t)

	originalArgs := os.Args

	defer func() { os.Args = originalArgs }()

	// given: a command line
	os.Args = []string{os.Args[0],
		"--http", "http_listen",
		"--env", "dev",
		"--log", "debug",
		"--storage-path", os.TempDir(),
		"--kvs", "kvs-backend",
		"--ms", "ms-backend",
		"--health-endpoint", "health_endpoint",
		"--metrics-endpoint", "metrics_endpoint",
		"--gcm",
		"--gcm-api-key", "gcm-api-key",
		"--gcm-workers", "3",
		"--node-id", "1",
		"--node-port", "10000",
		"--pg-host", "pg-host",
		"--pg-port", "5432",
		"--pg-user", "pg-user",
		"--pg-password", "pg-password",
		"--pg-dbname", "pg-dbname",
		"--tcplist", "127.0.0.1:8080 127.0.0.1:20002",
	}

	// when we parse the arguments from command-line flags
	parseConfig()

	// then the parsed parameters are correctly set
	assertArguments(a)
}

func assertArguments(a *assert.Assertions) {
	a.Equal("http_listen", *config.HttpListen)
	a.Equal("kvs-backend", *config.KVS)
	a.Equal(os.TempDir(), *config.StoragePath)
	a.Equal("ms-backend", *config.MS)
	a.Equal("health_endpoint", *config.HealthEndpoint)

	a.Equal("metrics_endpoint", *config.MetricsEndpoint)

	a.Equal(true, *config.GCM.Enabled)
	a.Equal("gcm-api-key", *config.GCM.APIKey)
	a.Equal(3, *config.GCM.Workers)

	a.Equal(1, *config.Cluster.NodeID)
	a.Equal(10000, *config.Cluster.NodePort)

	a.Equal("pg-host", *config.Postgres.Host)
	a.Equal(5432, *config.Postgres.Port)
	a.Equal("pg-user", *config.Postgres.User)
	a.Equal("pg-password", *config.Postgres.Password)
	a.Equal("pg-dbname", *config.Postgres.DbName)

	a.Equal("debug", *config.Log)
	a.Equal("dev", *config.EnvName)
	assertRemotesCluster(a)

}

func assertRemotesCluster(a *assert.Assertions) {
	ip1, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
	ip2, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:20002")
	ipList := make(tcpAddrList, 0)
	ipList = append(ipList, ip1)
	ipList = append(ipList, ip2)
	a.Equal(ipList, *config.Cluster.Remotes)
}
<commit_msg>adding tests<commit_after>package server

import (
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"net"
)

func TestParsingOfEnvironmentVariables(t *testing.T) {
	a := assert.New(t)

	originalArgs := os.Args
	os.Args = []string{os.Args[0]}
	defer func() { os.Args = originalArgs }()

	// given: some environment variables
	os.Setenv("GUBLE_HTTP_LISTEN", "http_listen")
	defer os.Unsetenv("GUBLE_HTTP_LISTEN")

	os.Setenv("GUBLE_LOG", "debug")
	defer os.Unsetenv("GUBLE_LOG")

	os.Setenv("GUBLE_ENV", "dev")
	defer os.Unsetenv("GUBLE_ENV")

	os.Setenv("GUBLE_PROFILE", "mem")
	defer os.Unsetenv("GUBLE_PROFILE")

	os.Setenv("GUBLE_KVS", "kvs-backend")
	defer os.Unsetenv("GUBLE_KVS")

	os.Setenv("GUBLE_STORAGE_PATH", os.TempDir())
	defer os.Unsetenv("GUBLE_STORAGE_PATH")

	os.Setenv("GUBLE_HEALTH_ENDPOINT", "health_endpoint")
	defer os.Unsetenv("GUBLE_HEALTH_ENDPOINT")

	os.Setenv("GUBLE_METRICS_ENDPOINT", "metrics_endpoint")
	defer os.Unsetenv("GUBLE_METRICS_ENDPOINT")

	os.Setenv("GUBLE_MS", "ms-backend")
	defer os.Unsetenv("GUBLE_MS")

	os.Setenv("GUBLE_GCM", "true")
	defer os.Unsetenv("GUBLE_GCM")

	os.Setenv("GUBLE_GCM_API_KEY", "gcm-api-key")
	defer os.Unsetenv("GUBLE_GCM_API_KEY")

	os.Setenv("GUBLE_GCM_WORKERS", "3")
	defer os.Unsetenv("GUBLE_GCM_WORKERS")

	os.Setenv("GUBLE_NODE_ID", "1")
	defer os.Unsetenv("GUBLE_NODE_ID")

	os.Setenv("GUBLE_NODE_PORT", "10000")
	defer os.Unsetenv("GUBLE_NODE_PORT")

	os.Setenv("GUBLE_PG_HOST", "pg-host")
	defer os.Unsetenv("GUBLE_PG_HOST")

	os.Setenv("GUBLE_PG_PORT", "5432")
	defer os.Unsetenv("GUBLE_PG_PORT")

	os.Setenv("GUBLE_PG_USER", "pg-user")
	defer os.Unsetenv("GUBLE_PG_USER")

	os.Setenv("GUBLE_PG_PASSWORD", "pg-password")
	defer os.Unsetenv("GUBLE_PG_PASSWORD")

	os.Setenv("GUBLE_PG_DBNAME", "pg-dbname")
	defer os.Unsetenv("GUBLE_PG_DBNAME")

	os.Setenv("GUBLE_NODE_REMOTES", "127.0.0.1:8080 127.0.0.1:20002")
	defer os.Unsetenv("GUBLE_NODE_REMOTES")

	// when we parse the arguments from environment variables
	parseConfig()

	// then the parsed parameters are correctly set
	assertArguments(a)
}

func TestParsingArgs(t *testing.T) {
	a := assert.New(t)

	originalArgs := os.Args

	defer func() { os.Args = originalArgs }()

	// given: a command line
	os.Args = []string{os.Args[0],
		"--http", "http_listen",
		"--env", "dev",
		"--log", "debug",
		"--profile", "mem",
		"--storage-path", os.TempDir(),
		"--kvs", "kvs-backend",
		"--ms", "ms-backend",
		"--health-endpoint", "health_endpoint",
		"--metrics-endpoint", "metrics_endpoint",
		"--gcm",
		"--gcm-api-key", "gcm-api-key",
		"--gcm-workers", "3",
		"--node-id", "1",
		"--node-port", "10000",
		"--pg-host", "pg-host",
		"--pg-port", "5432",
		"--pg-user", "pg-user",
		"--pg-password", "pg-password",
		"--pg-dbname", "pg-dbname",
		"--tcplist", "127.0.0.1:8080 127.0.0.1:20002",
	}

	// when we parse the arguments from command-line flags
	parseConfig()

	// then the parsed parameters are correctly set
	assertArguments(a)
}

func assertArguments(a *assert.Assertions) {
	a.Equal("http_listen", *config.HttpListen)
	a.Equal("kvs-backend", *config.KVS)
	a.Equal(os.TempDir(), *config.StoragePath)
	a.Equal("ms-backend", *config.MS)
	a.Equal("health_endpoint", *config.HealthEndpoint)

	a.Equal("metrics_endpoint", *config.MetricsEndpoint)

	a.Equal(true, *config.GCM.Enabled)
	a.Equal("gcm-api-key", *config.GCM.APIKey)
	a.Equal(3, *config.GCM.Workers)

	a.Equal(1, *config.Cluster.NodeID)
	a.Equal(10000, *config.Cluster.NodePort)

	a.Equal("pg-host", *config.Postgres.Host)
	a.Equal(5432, *config.Postgres.Port)
	a.Equal("pg-user", *config.Postgres.User)
	a.Equal("pg-password", *config.Postgres.Password)
	a.Equal("pg-dbname", *config.Postgres.DbName)

	a.Equal("debug", *config.Log)
	a.Equal("dev", *config.EnvName)
	a.Equal("mem", *config.Profile)

	assertClusterRemotes(a)
}

func assertClusterRemotes(a *assert.Assertions) {
	ip1, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
	ip2, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:20002")
	ipList := make(tcpAddrList, 0)
	ipList = append(ipList, ip1)
	ipList = append(ipList, ip2)
	a.Equal(ipList, *config.Cluster.Remotes)
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"os"
	"strings"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/config/module"
	"github.com/hashicorp/terraform/helper/hilmapstructure"
)

// Variables returns the fully loaded set of variables to use with
// ContextOpts and NewContext, loading any additional variables from
// the environment or any other sources.
//
// The given module tree doesn't need to be loaded.
func Variables(
	m *module.Tree,
	override map[string]interface{}) (map[string]interface{}, error) {
	result := make(map[string]interface{})

	// Variables are loaded in the following sequence. Each additional step
	// will override conflicting variable keys from prior steps:
	//
	//   * Take default values from config
	//   * Take values from TF_VAR_x env vars
	//   * Take values specified in the "override" param which is usually
	//     from -var, -var-file, etc.
	//

	// First load from the config
	for _, v := range m.Config().Variables {
		// If the var has no default, ignore
		if v.Default == nil {
			continue
		}

		// If the type isn't a string, we use it as-is since it is a rich type
		if v.Type() != config.VariableTypeString {
			result[v.Name] = v.Default
			continue
		}

		// v.Default has already been parsed as HCL but it may be an int type
		switch typedDefault := v.Default.(type) {
		case string:
			if typedDefault == "" {
				continue
			}
			result[v.Name] = typedDefault
		case int, int64:
			result[v.Name] = fmt.Sprintf("%d", typedDefault)
		case float32, float64:
			result[v.Name] = fmt.Sprintf("%f", typedDefault)
		case bool:
			result[v.Name] = fmt.Sprintf("%t", typedDefault)
		default:
			panic(fmt.Sprintf(
				"Unknown default var type: %T\n\n"+
					"THIS IS A BUG. Please report it.",
				v.Default))
		}
	}

	// Load from env vars
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, VarEnvPrefix) {
			continue
		}

		// Strip off the prefix and get the value after the first "="
		idx := strings.Index(v, "=")
		k := v[len(VarEnvPrefix):idx]
		v = v[idx+1:]

		// Override the configuration-default values. Note that *not* finding the variable
		// in configuration is OK, as we don't want to preclude people from having multiple
		// sets of TF_VAR_whatever in their environment even if it is a little weird.
		for _, schema := range m.Config().Variables {
			if schema.Name != k {
				continue
			}

			varType := schema.Type()
			varVal, err := parseVariableAsHCL(k, v, varType)
			if err != nil {
				return nil, err
			}

			switch varType {
			case config.VariableTypeMap:
				varSetMap(result, k, varVal)
			default:
				result[k] = varVal
			}
		}
	}

	// Load from overrides
	for k, v := range override {
		for _, schema := range m.Config().Variables {
			if schema.Name != k {
				continue
			}

			switch schema.Type() {
			case config.VariableTypeList:
				result[k] = v
			case config.VariableTypeMap:
				varSetMap(result, k, v)
			case config.VariableTypeString:
				// Convert to a string and set. We don't catch any errors
				// here because the validation step later should catch
				// any type errors.
				var strVal string
				if err := hilmapstructure.WeakDecode(v, &strVal); err == nil {
					result[k] = strVal
				} else {
					result[k] = v
				}
			default:
				panic(fmt.Sprintf(
					"Unhandled var type: %T\n\n"+
						"THIS IS A BUG. Please report it.",
					schema.Type()))
			}
		}
	}

	return result, nil
}

// varSetMap sets or merges the map in "v" with the key "k" in the
// "current" set of variables. This is just a private function to remove
// duplicate logic in Variables
func varSetMap(current map[string]interface{}, k string, v interface{}) {
	existing, ok := current[k]
	if !ok {
		current[k] = v
		return
	}

	existingMap, ok := existing.(map[string]interface{})
	if !ok {
		panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k))
	}

	switch typedV := v.(type) {
	case []map[string]interface{}:
		for newKey, newVal := range typedV[0] {
			existingMap[newKey] = newVal
		}
	case map[string]interface{}:
		for newKey, newVal := range typedV {
			existingMap[newKey] = newVal
		}
	default:
		panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k))
	}
}
<commit_msg>Return an error for setting a non-map to a map<commit_after>package terraform

import (
	"fmt"
	"os"
	"strings"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/config/module"
	"github.com/hashicorp/terraform/helper/hilmapstructure"
)

// Variables returns the fully loaded set of variables to use with
// ContextOpts and NewContext, loading any additional variables from
// the environment or any other sources.
//
// The given module tree doesn't need to be loaded.
func Variables(
	m *module.Tree,
	override map[string]interface{}) (map[string]interface{}, error) {
	result := make(map[string]interface{})

	// Variables are loaded in the following sequence. Each additional step
	// will override conflicting variable keys from prior steps:
	//
	//   * Take default values from config
	//   * Take values from TF_VAR_x env vars
	//   * Take values specified in the "override" param which is usually
	//     from -var, -var-file, etc.
	//

	// First load from the config
	for _, v := range m.Config().Variables {
		// If the var has no default, ignore
		if v.Default == nil {
			continue
		}

		// If the type isn't a string, we use it as-is since it is a rich type
		if v.Type() != config.VariableTypeString {
			result[v.Name] = v.Default
			continue
		}

		// v.Default has already been parsed as HCL but it may be an int type
		switch typedDefault := v.Default.(type) {
		case string:
			if typedDefault == "" {
				continue
			}
			result[v.Name] = typedDefault
		case int, int64:
			result[v.Name] = fmt.Sprintf("%d", typedDefault)
		case float32, float64:
			result[v.Name] = fmt.Sprintf("%f", typedDefault)
		case bool:
			result[v.Name] = fmt.Sprintf("%t", typedDefault)
		default:
			panic(fmt.Sprintf(
				"Unknown default var type: %T\n\n"+
					"THIS IS A BUG. Please report it.",
				v.Default))
		}
	}

	// Load from env vars
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, VarEnvPrefix) {
			continue
		}

		// Strip off the prefix and get the value after the first "="
		idx := strings.Index(v, "=")
		k := v[len(VarEnvPrefix):idx]
		v = v[idx+1:]

		// Override the configuration-default values. Note that *not* finding the variable
		// in configuration is OK, as we don't want to preclude people from having multiple
		// sets of TF_VAR_whatever in their environment even if it is a little weird.
		for _, schema := range m.Config().Variables {
			if schema.Name != k {
				continue
			}

			varType := schema.Type()
			varVal, err := parseVariableAsHCL(k, v, varType)
			if err != nil {
				return nil, err
			}

			switch varType {
			case config.VariableTypeMap:
				if err := varSetMap(result, k, varVal); err != nil {
					return nil, err
				}
			default:
				result[k] = varVal
			}
		}
	}

	// Load from overrides
	for k, v := range override {
		for _, schema := range m.Config().Variables {
			if schema.Name != k {
				continue
			}

			switch schema.Type() {
			case config.VariableTypeList:
				result[k] = v
			case config.VariableTypeMap:
				if err := varSetMap(result, k, v); err != nil {
					return nil, err
				}
			case config.VariableTypeString:
				// Convert to a string and set. We don't catch any errors
				// here because the validation step later should catch
				// any type errors.
				var strVal string
				if err := hilmapstructure.WeakDecode(v, &strVal); err == nil {
					result[k] = strVal
				} else {
					result[k] = v
				}
			default:
				panic(fmt.Sprintf(
					"Unhandled var type: %T\n\n"+
						"THIS IS A BUG. Please report it.",
					schema.Type()))
			}
		}
	}

	return result, nil
}

// varSetMap sets or merges the map in "v" with the key "k" in the
// "current" set of variables. This is just a private function to remove
// duplicate logic in Variables
func varSetMap(current map[string]interface{}, k string, v interface{}) error {
	existing, ok := current[k]
	if !ok {
		current[k] = v
		return nil
	}

	existingMap, ok := existing.(map[string]interface{})
	if !ok {
		panic(fmt.Sprintf("%q is not a map, this is a bug in Terraform.", k))
	}

	switch typedV := v.(type) {
	case []map[string]interface{}:
		for newKey, newVal := range typedV[0] {
			existingMap[newKey] = newVal
		}
	case map[string]interface{}:
		for newKey, newVal := range typedV {
			existingMap[newKey] = newVal
		}
	default:
		return fmt.Errorf("variable %q should be type map, got %s", k, hclTypeName(v))
	}
	return nil
}
<|endoftext|>


<commit_before>package uniconfig

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"regexp"
	"strings"
)

var ENV_PREFIX = ""

type ConfigItem struct {
	Section string
	Name    string
	Value   reflect.Value
	Help    string
}

func (i *ConfigItem) EnvVarName() string {
	name := strings.ToUpper(i.Name)
	if i.Section == "" {
		return ENV_PREFIX + name
	}
	return ENV_PREFIX + strings.ToUpper(i.Section) + "_" + name
}

func (i *ConfigItem) CmdFlagName() string {
	name := strings.ToLower(i.Name)
	if i.Section == "" {
		return name
	}
	return strings.ToLower(i.Section) + "-" + name
}

func (i *ConfigItem) InitFlag() {
	name := i.CmdFlagName()
	switch i.Value.Kind() {
	case reflect.String:
		v := i.Value.Addr().Interface().(*string)
		flag.StringVar(v, name, *v, i.Help)
	case reflect.Int:
		v := i.Value.Addr().Interface().(*int)
		flag.IntVar(v, name, *v, i.Help)
	case reflect.Int64:
		v := i.Value.Addr().Interface().(*int64)
		flag.Int64Var(v, name, *v, i.Help)
	case reflect.Bool:
		v := i.Value.Addr().Interface().(*bool)
		flag.BoolVar(v, name, *v, i.Help)
	case reflect.Slice:
		switch i.Value.Type() {
		case intSliceType:
			v := i.Value.Addr().Interface().(*[]int)
			v1 := NewIntSlice(v)
			flag.Var(v1, name, i.Help)
		case strSliceType:
			v := i.Value.Addr().Interface().(*[]string)
			v1 := NewStrSlice(v)
			flag.Var(v1, name, i.Help)
		case floatSliceType:
			v := i.Value.Addr().Interface().(*[]float64)
			v1 := NewFloatSlice(v)
			flag.Var(v1, name, i.Help)
		default:
			log.Fatalf("Unexpected type of config entry: %v", i)
		}
	default:
		log.Fatalf("Unexpected type of config entry: %v", i)
	}
}

func ScanConfig(config interface{}) []*ConfigItem {
	items := make([]*ConfigItem, 0)

	eConfig := reflect.ValueOf(config).Elem()
	tConfig := eConfig.Type()

	for i := 0; i < tConfig.NumField(); i++ {
		f := tConfig.Field(i)

		if f.PkgPath != "" {
			continue // skip private fields
		}
		v := eConfig.Field(i)
		if f.Type.Kind() == reflect.Struct {
			for j := 0; j < f.Type.NumField(); j++ {
				ff := f.Type.Field(j)
				if ff.PkgPath != "" {
					continue // skip private fields
				}
				vv := v.Field(j)
				item := &ConfigItem{
					Section: f.Name,
					Name:    ff.Name,
					Value:   vv,
					Help:    ff.Tag.Get("help"),
				}
				items = append(items, item)
			}
			continue
		}
		item := &ConfigItem{
			Section: "",
			Name:    f.Name,
			Value:   v,
			Help:    f.Tag.Get("help"),
		}
		items = append(items, item)
	}
	return items
}

func LoadFromEnv(configItems []*ConfigItem) {
	for _, item := range configItems {
		v := os.Getenv(item.EnvVarName())
		if v != "" {
			flag.Set(item.CmdFlagName(), v)
		}
	}
}

func ParseIniFile(inifile io.Reader) map[string]string {
	scanner := bufio.NewScanner(inifile)
	result := make(map[string]string)
	// keys are stored in form of KEY or SECTION_KEY, always-uppercase
	// (so they match our convention for environment variable names)
	section := ""

	reSection := regexp.MustCompile(`^\[(.*)\]$`)
	reKeyValue := regexp.MustCompile(`^([^=]+?)\s*=\s*(.*)$`)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if len(line) == 0 || line[0] == ';' || line[0] == '#' {
			continue
		}
		if m := reSection.FindStringSubmatch(line); m != nil {
			section = strings.ToUpper(m[1])
			continue
		}
		if m := reKeyValue.FindStringSubmatch(line); m != nil {
			key := strings.ToUpper(m[1])
			if section != "" {
				key = section + "_" + key
			}
			value := m[2]
			result[key] = value
			continue
		}
	}
	return result
}

func GetConfigPathFromCmd(args []string) string {
	// We will search the command line for --config option.
	// We must do this manually and not via `flag` package,
	// because cmd flags must override config file params
	// (and therefore flag.Parse() must be called *after* we've read the config file)
	reArgValue := regexp.MustCompile(`^[^=]+="?(.+?)"?$`) // optionally quoted value
	for i, arg := range args {
		if (arg == "-config" || arg == "--config") && i < len(args)-1 {
			return args[i+1]
		}
		if strings.HasPrefix(arg, "-config=") || strings.HasPrefix(arg, "--config=") {
			if m := reArgValue.FindStringSubmatch(arg); m != nil {
				return m[1]
			}
		}
	}
	return ""
}

func SetFromParsedIniFile(configItems []*ConfigItem, ini map[string]string) {
	for _, item := range configItems {
		k := item.EnvVarName()
		v, ok := ini[k]
		if ok {
			flag.Set(item.CmdFlagName(), v)
		}
	}
}

func LoadFromConfigFile(configItems []*ConfigItem) {
	configFilename := GetConfigPathFromCmd(os.Args[1:])
	if configFilename == "" {
		return
	}
	f, err := os.Open(configFilename)
	if err != nil {
		log.Fatalf("Error loading config file %s: %s", configFilename, err)
	}
	defer f.Close()
	dict := ParseIniFile(f)
	SetFromParsedIniFile(configItems, dict)
}

func ItemsAsIniFile(configItems []*ConfigItem) string {
	sections := make(map[string][]*ConfigItem)
	for _, item := range configItems {
		sections[item.Section] = append(sections[item.Section], item)
	}
	iniLines := make([]string, 0)
	dumpSection := func(section string) {
		for _, item := range sections[section] {
			line := fmt.Sprintf("%s = %v", item.Name, item.Value.Interface())
			iniLines = append(iniLines, line)
		}
		iniLines = append(iniLines, "")
	}
	dumpSection("")
	delete(sections, "")
	for section := range sections {
		line := fmt.Sprintf("[%s]", section)
		iniLines = append(iniLines, line)
		dumpSection(section)
	}
	return strings.Join(iniLines, "\n")
}

func InitFlags(configItems []*ConfigItem) {
	flag.String("config", "", "path to configuration file") // only to provide help
	for _, item := range configItems {
		item.InitFlag()
	}
}

func LoadFromFlags(configItems []*ConfigItem) {
	flag.Parse()
}

func ConfigAsIniFile(config interface{}) string {
	items := ScanConfig(config)
	return ItemsAsIniFile(items)
}

func Load(config interface{}) {
	items := ScanConfig(config)
	InitFlags(items)
	LoadFromConfigFile(items)
	LoadFromEnv(items)
	LoadFromFlags(items)
}
<commit_msg>convert ENV_PREFIX to CamelCase<commit_after>package uniconfig

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"regexp"
	"strings"
)

var EnvPrefix = ""

type ConfigItem struct {
	Section string
	Name    string
	Value   reflect.Value
	Help    string
}

func (i *ConfigItem) EnvVarName() string {
	name := strings.ToUpper(i.Name)
	if i.Section == "" {
		return EnvPrefix + name
	}
	return EnvPrefix + strings.ToUpper(i.Section) + "_" + name
}

func (i *ConfigItem) CmdFlagName() string {
	name := strings.ToLower(i.Name)
	if i.Section == "" {
		return name
	}
	return strings.ToLower(i.Section) + "-" + name
}

func (i *ConfigItem) InitFlag() {
	name := i.CmdFlagName()
	switch i.Value.Kind() {
	case reflect.String:
		v := i.Value.Addr().Interface().(*string)
		flag.StringVar(v, name, *v, i.Help)
	case reflect.Int:
		v := i.Value.Addr().Interface().(*int)
		flag.IntVar(v, name, *v, i.Help)
	case reflect.Int64:
		v := i.Value.Addr().Interface().(*int64)
		flag.Int64Var(v, name, *v, i.Help)
	case reflect.Bool:
		v := i.Value.Addr().Interface().(*bool)
		flag.BoolVar(v, name, *v, i.Help)
	case reflect.Slice:
		switch i.Value.Type() {
		case intSliceType:
			v := i.Value.Addr().Interface().(*[]int)
			v1 := NewIntSlice(v)
			flag.Var(v1, name, i.Help)
		case strSliceType:
			v := i.Value.Addr().Interface().(*[]string)
			v1 := NewStrSlice(v)
			flag.Var(v1, name, i.Help)
		case floatSliceType:
			v := i.Value.Addr().Interface().(*[]float64)
			v1 := NewFloatSlice(v)
			flag.Var(v1, name, i.Help)
		default:
			log.Fatalf("Unexpected type of config entry: %v", i)
		}
	default:
		log.Fatalf("Unexpected type of config entry: %v", i)
	}
}

func ScanConfig(config interface{}) []*ConfigItem {
	items := make([]*ConfigItem, 0)

	eConfig := reflect.ValueOf(config).Elem()
	tConfig := eConfig.Type()

	for i := 0; i < tConfig.NumField(); i++ {
		f := tConfig.Field(i)

		if f.PkgPath != "" {
			continue // skip private fields
		}
		v := eConfig.Field(i)
		if f.Type.Kind() == reflect.Struct {
			for j := 0; j < f.Type.NumField(); j++ {
				ff := f.Type.Field(j)
				if ff.PkgPath != "" {
					continue // skip private fields
				}
				vv := v.Field(j)
				item := &ConfigItem{
					Section: f.Name,
					Name:    ff.Name,
					Value:   vv,
					Help:    ff.Tag.Get("help"),
				}
				items = append(items, item)
			}
			continue
		}
		item := &ConfigItem{
			Section: "",
			Name:    f.Name,
			Value:   v,
			Help:    f.Tag.Get("help"),
		}
		items = append(items, item)
	}
	return items
}

func LoadFromEnv(configItems []*ConfigItem) {
	for _, item := range configItems {
		v := os.Getenv(item.EnvVarName())
		if v != "" {
			flag.Set(item.CmdFlagName(), v)
		}
	}
}

func ParseIniFile(inifile io.Reader) map[string]string {
	scanner := bufio.NewScanner(inifile)
	result := make(map[string]string)
	// keys are stored in form of KEY or SECTION_KEY, always-uppercase
	// (so they match our convention for environment variable names)
	section := ""

	reSection := regexp.MustCompile(`^\[(.*)\]$`)
	reKeyValue := regexp.MustCompile(`^([^=]+?)\s*=\s*(.*)$`)

	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if len(line) == 0 || line[0] == ';' || line[0] == '#' {
			continue
		}
		if m := reSection.FindStringSubmatch(line); m != nil {
			section = strings.ToUpper(m[1])
			continue
		}
		if m := reKeyValue.FindStringSubmatch(line); m != nil {
			key := strings.ToUpper(m[1])
			if section != "" {
				key = section + "_" + key
			}
			value := m[2]
			result[key] = value
			continue
		}
	}
	return result
}

func GetConfigPathFromCmd(args []string) string {
	// We will search the command line for --config option.
	// We must do this manually and not via `flag` package,
	// because cmd flags must override config file params
	// (and therefore flag.Parse() must be called *after* we've read the config file)
	reArgValue := regexp.MustCompile(`^[^=]+="?(.+?)"?$`) // optionally quoted value
	for i, arg := range args {
		if (arg == "-config" || arg == "--config") && i < len(args)-1 {
			return args[i+1]
		}
		if strings.HasPrefix(arg, "-config=") || strings.HasPrefix(arg, "--config=") {
			if m := reArgValue.FindStringSubmatch(arg); m != nil {
				return m[1]
			}
		}
	}
	return ""
}

func SetFromParsedIniFile(configItems []*ConfigItem, ini map[string]string) {
	for _, item := range configItems {
		k := item.EnvVarName()
		v, ok := ini[k]
		if ok {
			flag.Set(item.CmdFlagName(), v)
		}
	}
}

func LoadFromConfigFile(configItems []*ConfigItem) {
	configFilename := GetConfigPathFromCmd(os.Args[1:])
	if configFilename == "" {
		return
	}
	f, err := os.Open(configFilename)
	if err != nil {
		log.Fatalf("Error loading config file %s: %s", configFilename, err)
	}
	defer f.Close()
	dict := ParseIniFile(f)
	SetFromParsedIniFile(configItems, dict)
}

func ItemsAsIniFile(configItems []*ConfigItem) string {
	sections := make(map[string][]*ConfigItem)
	for _, item := range configItems {
		sections[item.Section] = append(sections[item.Section], item)
	}
	iniLines := make([]string, 0)
	dumpSection := func(section string) {
		for _, item := range sections[section] {
			line := fmt.Sprintf("%s = %v", item.Name, item.Value.Interface())
			iniLines = append(iniLines, line)
		}
		iniLines = append(iniLines, "")
	}
	dumpSection("")
	delete(sections, "")
	for section := range sections {
		line := fmt.Sprintf("[%s]", section)
		iniLines = append(iniLines, line)
		dumpSection(section)
	}
	return strings.Join(iniLines, "\n")
}

func InitFlags(configItems []*ConfigItem) {
	flag.String("config", "", "path to configuration file") // only to provide help
	for _, item := range configItems {
		item.InitFlag()
	}
}

func LoadFromFlags(configItems []*ConfigItem) {
	flag.Parse()
}

func ConfigAsIniFile(config interface{}) string {
	items := ScanConfig(config)
	return ItemsAsIniFile(items)
}

func Load(config interface{}) {
	items := ScanConfig(config)
	InitFlags(items)
	LoadFromConfigFile(items)
	LoadFromEnv(items)
	LoadFromFlags(items)
}
<|endoftext|>


<commit_before>package channel

import (
	"bytes"
	"regexp"
	"time"

	"github.com/scrapli/scrapligo/util"
)

func (c *Channel) read() {
	for {
		select {
		case <-c.done:
			return
		default:
		}

		b, err := c.t.Read()
		if err != nil {
			// we got a transport error, put it into the error channel for processing during
			// the next read activity
			c.Errs <- err
		}

		// not 100% this is required, but has existed in scrapli/scrapligo for a long time and am
		// afraid to remove it!
		b = bytes.ReplaceAll(b, []byte("\r"), []byte(""))

		// trim out all the space we padded in the buffer to read into
		b = bytes.ReplaceAll(b, []byte("\x00"), []byte(""))

		if bytes.Contains(b, []byte("\x1b")) {
			b = util.StripANSI(b)
		}

		c.Q.Enqueue(b)

		if c.ChannelLog != nil {
			_, err = c.ChannelLog.Write(b)
			if err != nil {
				c.l.Criticalf("error writing to channel log, ignoring. error: %s", err)
			}
		}

		time.Sleep(c.ReadDelay)
	}
}

// Read reads and returns the first available bytes from the channel Q object. If there are any
// errors on the Errs channel (these would come from the underlying transport), the error is
// returned with nil for the byte slice.
func (c *Channel) Read() ([]byte, error) {
	select {
	case err := <-c.Errs:
		return nil, err
	default:
	}

	b := c.Q.Dequeue()

	return b, nil
}

// ReadAll reads and returns *all* available bytes form the channel Q object. If there are any
// errors on the Errs channel  (these would come from the underlying transport), the error is
// returned with nil for the byte slice. Be careful using this as it is possible to dequeue "too
// much" from the channel causing us to not be able to "find" the prompt or inputs during normal
// operations. In general, this should probably only be used when connecting to consoles/files.
func (c *Channel) ReadAll() ([]byte, error) {
	select {
	case err := <-c.Errs:
		return nil, err
	default:
	}

	b := c.Q.DequeueAll()

	return b, nil
}

// ReadUntilInput reads bytes out of the channel Q object until the "input" bytes b are "seen" in
// the channel output. Once b is seen, all read bytes are returned.
func (c *Channel) ReadUntilInput(b []byte) ([]byte, error) {
	if len(b) == 0 {
		return nil, nil
	}

	return c.ReadUntilExplicit(b)
}

// ReadUntilPrompt reads bytes out of the channel Q object until the channel PromptPattern regex
// pattern is seen in the output. Once that pattern is seen, all read bytes are returned.
func (c *Channel) ReadUntilPrompt() ([]byte, error) {
	var rb []byte

	for {
		select {
		case err := <-c.Errs:
			return nil, err
		default:
		}

		nb := c.Q.Dequeue()

		if nb == nil {
			continue
		}

		rb = append(rb, nb...)

		if c.PromptPattern.Match(rb) {
			c.l.Debugf("channel read %#v", string(rb))

			return rb, nil
		}
	}
}

// ReadUntilAnyPrompt reads bytes out of the channel Q object until any of the prompts in the
// "prompts" argument are seen in the output. Once any pattern is seen, all read bytes are returned.
func (c *Channel) ReadUntilAnyPrompt(prompts []*regexp.Regexp) ([]byte, error) {
	var rb []byte

	for {
		select {
		case err := <-c.Errs:
			return nil, err
		default:
		}

		nb := c.Q.Dequeue()

		if nb == nil {
			continue
		}

		rb = append(rb, nb...)

		for _, p := range prompts {
			if p.Match(rb) {
				c.l.Debugf("channel read %#v", string(rb))

				return rb, nil
			}
		}
	}
}

// ReadUntilExplicit reads bytes out of the channel Q object until the bytes b are seen in the
// output. Once the bytes are seen all read bytes are returned.
func (c *Channel) ReadUntilExplicit(b []byte) ([]byte, error) {
	var rb []byte

	for {
		select {
		case err := <-c.Errs:
			return nil, err
		default:
		}

		nb := c.Q.Dequeue()

		if nb == nil {
			continue
		}

		rb = append(rb, nb...)

		if bytes.Contains(rb, b) {
			c.l.Debugf("channel read %#v", string(rb))

			return rb, nil
		}
	}
}
<commit_msg>Fix goroutine leak in channel.read when transport is closed<commit_after>package channel

import (
	"bytes"
	"io"
	"regexp"
	"time"

	"github.com/scrapli/scrapligo/util"
)

func (c *Channel) read() {
	for {
		select {
		case <-c.done:
			return
		default:
		}

		b, err := c.t.Read()
		if err != nil {
			if err == io.EOF {
				// the underlying transport was closed so just return
				return
			}
			// we got a transport error, put it into the error channel for processing during
			// the next read activity
			c.Errs <- err
		}

		// not 100% this is required, but has existed in scrapli/scrapligo for a long time and am
		// afraid to remove it!
		b = bytes.ReplaceAll(b, []byte("\r"), []byte(""))

		// trim out all the space we padded in the buffer to read into
		b = bytes.ReplaceAll(b, []byte("\x00"), []byte(""))

		if bytes.Contains(b, []byte("\x1b")) {
			b = util.StripANSI(b)
		}

		c.Q.Enqueue(b)

		if c.ChannelLog != nil {
			_, err = c.ChannelLog.Write(b)
			if err != nil {
				c.l.Criticalf("error writing to channel log, ignoring. error: %s", err)
			}
		}

		time.Sleep(c.ReadDelay)
	}
}

// Read reads and returns the first available bytes from the channel Q object. If there are any
// errors on the Errs channel (these would come from the underlying transport), the error is
// returned with nil for the byte slice.
func (c *Channel) Read() ([]byte, error) {
	select {
	case err := <-c.Errs:
		return nil, err
	default:
	}

	b := c.Q.Dequeue()

	return b, nil
}

// ReadAll reads and returns *all* available bytes form the channel Q object. If there are any
// errors on the Errs channel  (these would come from the underlying transport), the error is
// returned with nil for the byte slice. Be careful using this as it is possible to dequeue "too
// much" from the channel causing us to not be able to "find" the prompt or inputs during normal
// operations. In general, this should probably only be used when connecting to consoles/files.
func (c *Channel) ReadAll() ([]byte, error) {
	select {
	case err := <-c.Errs:
		return nil, err
	default:
	}

	b := c.Q.DequeueAll()

	return b, nil
}

// ReadUntilInput reads bytes out of the channel Q object until the "input" bytes b are "seen" in
// the channel output. Once b is seen, all read bytes are returned.
func (c *Channel) ReadUntilInput(b []byte) ([]byte, error) {
	if len(b) == 0 {
		return nil, nil
	}

	return c.ReadUntilExplicit(b)
}

// ReadUntilPrompt reads bytes out of the channel Q object until the channel PromptPattern regex
// pattern is seen in the output. Once that pattern is seen, all read bytes are returned.
func (c *Channel) ReadUntilPrompt() ([]byte, error) {
	var rb []byte

	for {
		select {
		case err := <-c.Errs:
			return nil, err
		default:
		}

		nb := c.Q.Dequeue()

		if nb == nil {
			continue
		}

		rb = append(rb, nb...)

		if c.PromptPattern.Match(rb) {
			c.l.Debugf("channel read %#v", string(rb))

			return rb, nil
		}
	}
}

// ReadUntilAnyPrompt reads bytes out of the channel Q object until any of the prompts in the
// "prompts" argument are seen in the output. Once any pattern is seen, all read bytes are returned.
func (c *Channel) ReadUntilAnyPrompt(prompts []*regexp.Regexp) ([]byte, error) {
	var rb []byte

	for {
		select {
		case err := <-c.Errs:
			return nil, err
		default:
		}

		nb := c.Q.Dequeue()

		if nb == nil {
			continue
		}

		rb = append(rb, nb...)

		for _, p := range prompts {
			if p.Match(rb) {
				c.l.Debugf("channel read %#v", string(rb))

				return rb, nil
			}
		}
	}
}

// ReadUntilExplicit reads bytes out of the channel Q object until the bytes b are seen in the
// output. Once the bytes are seen all read bytes are returned.
func (c *Channel) ReadUntilExplicit(b []byte) ([]byte, error) {
	var rb []byte

	for {
		select {
		case err := <-c.Errs:
			return nil, err
		default:
		}

		nb := c.Q.Dequeue()

		if nb == nil {
			continue
		}

		rb = append(rb, nb...)

		if bytes.Contains(rb, b) {
			c.l.Debugf("channel read %#v", string(rb))

			return rb, nil
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>bugfix<commit_after><|endoftext|>


<commit_before>// Some parts of this file are taken from Lakrizz' example on GitHub
// https://github.com/lakrizz/go-pidioder/blob/master/main.go

package dioder

import (
	"bufio"
	"errors"
	"image/color"
	"os"
	"strconv"
)

//Pins the numbers of the RGB-pins
type Pins struct {
	Red   string
	Green string
	Blue  string
}

//Dioder the main structure
type Dioder struct {
	PinConfiguration   Pins
	ColorConfiguration color.RGBA
	PiBlaster          string
}

//New creates a new instance
func New(pinConfiguration Pins, piBlasterFile string) Dioder {
	if piBlasterFile == "" {
		piBlasterFile = "/dev/pi-blaster"
	}

	d := Dioder{}

	d.SetPins(pinConfiguration)
	d.PiBlaster = piBlasterFile

	return d
}

// GetCurrentColor returns the current color
func (d *Dioder) GetCurrentColor() color.RGBA {
	return d.ColorConfiguration
}

// SetAll sets the given values for the channels
func (d *Dioder) SetAll(colorSet color.RGBA) {
	d.ColorConfiguration = colorSet
	//Red
	colorSet.R = calculateOpacity(colorSet.R, colorSet.A)
	if colorSet.R != d.ColorConfiguration.R {
		d.SetChannelInteger(colorSet.R, d.PinConfiguration.Red)
	}

	//Green
	colorSet.G = calculateOpacity(colorSet.G, colorSet.A)
	if colorSet.G != d.ColorConfiguration.G {
		d.SetChannelInteger(colorSet.G, d.PinConfiguration.Green)
	}

	//Blue
	colorSet.B = calculateOpacity(colorSet.B, colorSet.A)
	if colorSet.B != d.ColorConfiguration.B {
		d.SetChannelInteger(colorSet.B, d.PinConfiguration.Blue)
	}

}

//SetPins configures the pin-layout
func (d *Dioder) SetPins(pinConfiguration Pins) {
	d.PinConfiguration = pinConfiguration
}

//TurnOff turns off the dioder-strips and saves the current configuration
func (d *Dioder) TurnOff() {
	//Temporary save the configuration
	configuration := d.ColorConfiguration
	d.SetAll(color.RGBA{})
	d.ColorConfiguration = configuration
}

//TurnOn turns the dioder-strips on and restores the previous configuration
func (d *Dioder) TurnOn() {
	if d.ColorConfiguration.A == 0 && d.ColorConfiguration.B == 0 && d.ColorConfiguration.G == 0 && d.ColorConfiguration.R == 0 {
		d.ColorConfiguration = color.RGBA{255, 255, 255, 100}
	}

	//@ToDo: Refactor
	//Ugliy hack, to turn the lights back on
	colorSet := d.ColorConfiguration
	d.ColorConfiguration = color.RGBA{}

	d.SetAll(colorSet)
}

func floatToString(floatValue float64) string {
	return strconv.FormatFloat(floatValue, 'f', 6, 64)
}

//SetColor Sets a color on the given channel
func (d *Dioder) SetColor(channel string, value float64) error {
	piBlasterCommand := channel + "=" + floatToString(value) + "\n"

	file, error := os.OpenFile(d.PiBlaster, os.O_RDWR, os.ModeNamedPipe)

	if error != nil {
		panic(error)
	}

	defer file.Close()

	stream := bufio.NewWriter(file)

	_, error = stream.WriteString(piBlasterCommand)

	if error != nil {
		panic(error)
	}

	stream.Flush()

	return nil
}

//SetChannelInteger check if the value is in the correct range and convert it to float64
func (d *Dioder) SetChannelInteger(value uint8, channel string) error {
	if value > 255 {
		return errors.New("Value can not be over 255")
	}

	if value < 0 {
		return errors.New("Value can not be under 0")
	}

	floatval := float64(value) / 255.0

	d.SetColor(channel, float64(floatval))

	return nil
}

/*func (d *Dioder) fade(currentValue uint8, targetValue uint8, fadeTime time.Duration, channel string) {
	//Fade:
	//Einteilung der Werteminderung pro Zeiteinheit anhand der Werte zwischen currentValue und targetValue
	var neededSteps uint8

	if currentValue < targetValue {
		neededSteps = targetValue - currentValue
	} else {
		neededSteps = currentValue - targetValue
	}

	for neededSteps {
		//set value
		//sleep duration / neededSteps
	}

}*/

//calculateOpacity calculates the value of colorValue after applying some opacity
func calculateOpacity(colorValue uint8, opacity uint8) uint8 {
	var calculatedValue float32

	if opacity != 100 {
		calculatedValue = float32(colorValue) / 100 * float32(opacity)
	} else {
		calculatedValue = float32(colorValue)
	}

	return uint8(calculatedValue)
}
<commit_msg>Just set the color<commit_after>// Some parts of this file are taken from Lakrizz' example on GitHub
// https://github.com/lakrizz/go-pidioder/blob/master/main.go

package dioder

import (
	"bufio"
	"errors"
	"image/color"
	"os"
	"strconv"
)

//Pins the numbers of the RGB-pins
type Pins struct {
	Red   string
	Green string
	Blue  string
}

//Dioder the main structure
type Dioder struct {
	PinConfiguration   Pins
	ColorConfiguration color.RGBA
	PiBlaster          string
}

//New creates a new instance
func New(pinConfiguration Pins, piBlasterFile string) Dioder {
	if piBlasterFile == "" {
		piBlasterFile = "/dev/pi-blaster"
	}

	d := Dioder{}

	d.SetPins(pinConfiguration)
	d.PiBlaster = piBlasterFile

	return d
}

// GetCurrentColor returns the current color
func (d *Dioder) GetCurrentColor() color.RGBA {
	return d.ColorConfiguration
}

// SetAll sets the given values for the channels
func (d *Dioder) SetAll(colorSet color.RGBA) {
	d.ColorConfiguration = colorSet
	//Red
	colorSet.R = calculateOpacity(colorSet.R, colorSet.A)
	d.SetChannelInteger(colorSet.R, d.PinConfiguration.Red)
	//Green
	colorSet.G = calculateOpacity(colorSet.G, colorSet.A)
	d.SetChannelInteger(colorSet.G, d.PinConfiguration.Green)

	//Blue
	colorSet.B = calculateOpacity(colorSet.B, colorSet.A)
	d.SetChannelInteger(colorSet.B, d.PinConfiguration.Blue)
}

//SetPins configures the pin-layout
func (d *Dioder) SetPins(pinConfiguration Pins) {
	d.PinConfiguration = pinConfiguration
}

//TurnOff turns off the dioder-strips and saves the current configuration
func (d *Dioder) TurnOff() {
	//Temporary save the configuration
	configuration := d.ColorConfiguration
	d.SetAll(color.RGBA{})
	d.ColorConfiguration = configuration
}

//TurnOn turns the dioder-strips on and restores the previous configuration
func (d *Dioder) TurnOn() {
	if d.ColorConfiguration.A == 0 && d.ColorConfiguration.B == 0 && d.ColorConfiguration.G == 0 && d.ColorConfiguration.R == 0 {
		d.ColorConfiguration = color.RGBA{255, 255, 255, 100}
	}

	//@ToDo: Refactor
	//Ugliy hack, to turn the lights back on
	colorSet := d.ColorConfiguration
	d.ColorConfiguration = color.RGBA{}

	d.SetAll(colorSet)
}

func floatToString(floatValue float64) string {
	return strconv.FormatFloat(floatValue, 'f', 6, 64)
}

//SetColor Sets a color on the given channel
func (d *Dioder) SetColor(channel string, value float64) error {
	piBlasterCommand := channel + "=" + floatToString(value) + "\n"

	file, error := os.OpenFile(d.PiBlaster, os.O_RDWR, os.ModeNamedPipe)

	if error != nil {
		panic(error)
	}

	defer file.Close()

	stream := bufio.NewWriter(file)

	_, error = stream.WriteString(piBlasterCommand)

	if error != nil {
		panic(error)
	}

	stream.Flush()

	return nil
}

//SetChannelInteger check if the value is in the correct range and convert it to float64
func (d *Dioder) SetChannelInteger(value uint8, channel string) error {
	if value > 255 {
		return errors.New("Value can not be over 255")
	}

	if value < 0 {
		return errors.New("Value can not be under 0")
	}

	floatval := float64(value) / 255.0

	d.SetColor(channel, float64(floatval))

	return nil
}

/*func (d *Dioder) fade(currentValue uint8, targetValue uint8, fadeTime time.Duration, channel string) {
	//Fade:
	//Einteilung der Werteminderung pro Zeiteinheit anhand der Werte zwischen currentValue und targetValue
	var neededSteps uint8

	if currentValue < targetValue {
		neededSteps = targetValue - currentValue
	} else {
		neededSteps = currentValue - targetValue
	}

	for neededSteps {
		//set value
		//sleep duration / neededSteps
	}

}*/

//calculateOpacity calculates the value of colorValue after applying some opacity
func calculateOpacity(colorValue uint8, opacity uint8) uint8 {
	var calculatedValue float32

	if opacity != 100 {
		calculatedValue = float32(colorValue) / 100 * float32(opacity)
	} else {
		calculatedValue = float32(colorValue)
	}

	return uint8(calculatedValue)
}
<|endoftext|>


<commit_before>// Copyright 2017 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package serverutil

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"syscall"
	"testing"
	"time"
)

// TestShutdown launches a child process, sends it SIGTERM, and, by reading its
// standard output, checks that the process runs the required shutdown
// functions. It also checks that the process will be forced to exit if a
// timeout handler stalls.
func TestShutdown(t *testing.T) {
	if os.Getenv(shutdownEnv) == "true" {
		testShutdownChildProcess()
		return
	}

	t.Run("clean", func(t *testing.T) { testShutdown(t, true) })
	t.Run("messy", func(t *testing.T) { testShutdown(t, false) })
}

const (
	shutdownEnv     = "SHUTDOWN_CHILD_PROCESS"
	shutdownKillEnv = shutdownEnv + "_KILL"
)

var shutdownMessages = []string{
	"Hello",
	"How are you?",
	"Goodbye",
}

func testShutdown(t *testing.T, clean bool) {
	cmd := exec.Command(os.Args[0], "-test.run=TestShutdown")
	cmd.Env = []string{shutdownEnv + "=true"}
	if !clean {
		cmd.Env = append(cmd.Env, shutdownKillEnv+"=true")
	}

	// Scan process output line by line.
	rc, err := cmd.StdoutPipe()
	if err != nil {
		t.Fatal(err)
	}
	out := bufio.NewScanner(rc)

	if err := cmd.Start(); err != nil {
		t.Fatal(err)
	}

	// Check that we get the initial "hello" message from the client,
	// so we know it's running.
	readErr := make(chan error, 1)
	go func() {
		out.Scan()
		if err := out.Err(); err != nil {
			readErr <- err
			return
		}
		if got, want := out.Text(), shutdownMessages[0]; got != want {
			readErr <- fmt.Errorf("child said %q, want %q", got, want)
			return
		}
		readErr <- nil
	}()
	select {
	case err := <-readErr:
		if err != nil {
			cmd.Process.Kill()
			t.Fatal(err)
		}
	case <-time.After(2 * time.Second):
		t.Fatal("timed out waiting for child process to say hello")
	}

	// Collect and compare the remaining output lines.
	go func() {
		for n := 1; n < len(shutdownMessages); n++ {
			if !clean && n == 2 {
				// In messy mode the second shutdown
				// handler will not run.
				break
			}
			if !out.Scan() {
				readErr <- fmt.Errorf("child output ended, expected more lines")
				return
			}
			if got, want := out.Text(), shutdownMessages[n]; got != want {
				readErr <- fmt.Errorf("child output line %q, want %q", got, want)
				return
			}
		}
		if out.Scan() {
			readErr <- fmt.Errorf("child output unexpected line %q", out.Text())
			return
		}
		readErr <- nil
	}()

	// Kill the process and wait for it to exit, checking its exit status
	// depending on whether this is a clean or messy text.
	if err := syscall.Kill(cmd.Process.Pid, syscall.SIGTERM); err != nil {
		t.Fatal(err)
	}
	waitErr := make(chan error, 1)
	go func() {
		waitErr <- cmd.Wait()
	}()
	select {
	case err := <-waitErr:
		if err != nil && clean {
			t.Fatalf("child process exited with non-zero status: %v", err)
		} else if err == nil && !clean {
			t.Fatal("child proces exited cleanly, want non-zero status")
		}
	case <-time.After(2 * time.Second):
		t.Fatal("timed out waiting for child process to exit")
	}

	// Check that the output was what we expected.
	if err := <-readErr; err != nil {
		t.Fatal(err)
	}
}

func testShutdownChildProcess() {
	var kill chan bool
	if os.Getenv(shutdownKillEnv) == "true" {
		kill = make(chan bool)
		killSleep = func(time.Duration) {
			<-kill
		}
	}

	fmt.Println(shutdownMessages[0])

	RegisterShutdown(0, func() {
		fmt.Println(shutdownMessages[1])
		if kill != nil {
			kill <- true
			select {} // Block forever, stalling Shutdown.
		}
	})

	RegisterShutdown(1, func() {
		fmt.Println(shutdownMessages[2])
	})

	Shutdown()

	// If for some reason Shutdown returns the test must time out.
	select {}
}
<commit_msg>serverutil: fix race in TestShutdown<commit_after>// Copyright 2017 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package serverutil

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"syscall"
	"testing"
	"time"
)

// TestShutdown launches a child process, sends it SIGTERM, and, by reading its
// standard output, checks that the process runs the required shutdown
// functions. It also checks that the process will be forced to exit if a
// timeout handler stalls.
func TestShutdown(t *testing.T) {
	if os.Getenv(shutdownEnv) == "true" {
		testShutdownChildProcess()
		return
	}

	t.Run("clean", func(t *testing.T) { testShutdown(t, true) })
	t.Run("messy", func(t *testing.T) { testShutdown(t, false) })
}

const (
	shutdownEnv     = "SHUTDOWN_CHILD_PROCESS"
	shutdownKillEnv = shutdownEnv + "_KILL"
)

var shutdownMessages = []string{
	"Hello",
	"How are you?",
	"Goodbye",
}

func testShutdown(t *testing.T, clean bool) {
	cmd := exec.Command(os.Args[0], "-test.run=TestShutdown")
	cmd.Env = []string{shutdownEnv + "=true"}
	if !clean {
		cmd.Env = append(cmd.Env, shutdownKillEnv+"=true")
	}

	// Scan process output line by line.
	rc, err := cmd.StdoutPipe()
	if err != nil {
		t.Fatal(err)
	}
	out := bufio.NewScanner(rc)

	if err := cmd.Start(); err != nil {
		t.Fatal(err)
	}

	// Check that we get the initial "hello" message from the client,
	// so we know it's running.
	readErr := make(chan error, 1)
	go func() {
		out.Scan()
		if err := out.Err(); err != nil {
			readErr <- err
			return
		}
		if got, want := out.Text(), shutdownMessages[0]; got != want {
			readErr <- fmt.Errorf("child said %q, want %q", got, want)
			return
		}
		readErr <- nil
	}()
	select {
	case err := <-readErr:
		if err != nil {
			cmd.Process.Kill()
			t.Fatal(err)
		}
	case <-time.After(2 * time.Second):
		t.Fatal("timed out waiting for child process to say hello")
	}

	// Collect and compare the remaining output lines.
	waitErr := make(chan error, 1)
	go func() {
		for n := 1; n < len(shutdownMessages); n++ {
			if !clean && n == 2 {
				// In messy mode the second shutdown
				// handler will not run.
				break
			}
			if !out.Scan() {
				readErr <- fmt.Errorf("child output ended, expected more lines")
				return
			}
			if got, want := out.Text(), shutdownMessages[n]; got != want {
				readErr <- fmt.Errorf("child output line %q, want %q", got, want)
				return
			}
		}
		if out.Scan() {
			readErr <- fmt.Errorf("child output unexpected line %q", out.Text())
			return
		}
		readErr <- nil
		waitErr <- cmd.Wait()
	}()

	// Kill the process and wait for it to exit.
	if err := syscall.Kill(cmd.Process.Pid, syscall.SIGTERM); err != nil {
		t.Fatal(err)
	}

	// Check that the output was what we expected.
	if err := <-readErr; err != nil {
		cmd.Process.Kill()
		t.Fatal(err)
	}

	// Check exit status.
	select {
	case err := <-waitErr:
		if err != nil && clean {
			t.Fatalf("child process exited with non-zero status: %v", err)
		} else if err == nil && !clean {
			t.Fatal("child proces exited cleanly, want non-zero status")
		}
	case <-time.After(2 * time.Second):
		cmd.Process.Kill()
		t.Fatal("timed out waiting for child process to exit")
	}
}

func testShutdownChildProcess() {
	var kill chan bool
	if os.Getenv(shutdownKillEnv) == "true" {
		kill = make(chan bool)
		killSleep = func(time.Duration) {
			<-kill
		}
	}

	fmt.Println(shutdownMessages[0])

	RegisterShutdown(0, func() {
		fmt.Println(shutdownMessages[1])
		if kill != nil {
			kill <- true
			select {} // Block forever, stalling Shutdown.
		}
	})

	RegisterShutdown(1, func() {
		fmt.Println(shutdownMessages[2])
	})

	Shutdown()

	// If for some reason Shutdown returns the test must time out.
	select {}
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"fmt"

	"github.com/object88/langd/proto"
	"google.golang.org/grpc/reflection"
)

func (g *GrpcHandler) grpcService() {
	fmt.Printf("gRPC server starting\n")

	proto.RegisterLangdServer(g.S, g)

	// Register reflection service on gRPC server.
	reflection.Register(g.S)

	err := g.S.Serve(g.lis)
	if err != nil {
		fmt.Printf("Got error when stopping grpc service:\n%s\n", err.Error())
	}

	fmt.Printf("gRPC server stopped\n")
}

// Load returns the CPU and memory load
func (g *GrpcHandler) Load(_ context.Context, _ *proto.EmptyRequest) (*proto.LoadReply, error) {
	load := &proto.LoadReply{
		CpuLoad:    g.srv.load.CPU(),    // float32(g.pc.Percent),
		MemoryLoad: g.srv.load.Memory(), // g.pm.Resident,
	}
	return load, nil
}

// Shutdown stops the service process
func (g *GrpcHandler) Shutdown(ctx context.Context, _ *proto.EmptyRequest) (*proto.EmptyReply, error) {
	// fmt.Printf("Requesting stop on JSON server\n")
	// g.SM.Shutdown(ctx)

	fmt.Printf("Requesting stop on gRPC\n")
	// g.S.GracefulStop()

	g.srv.done <- true

	return &proto.EmptyReply{}, nil
}

// Startup is a no-op to start the service
func (g *GrpcHandler) Startup(_ context.Context, _ *proto.EmptyRequest) (*proto.EmptyReply, error) {
	return &proto.EmptyReply{}, nil
}
<commit_msg>Cleanup<commit_after>package server

import (
	"context"
	"fmt"

	"github.com/object88/langd/proto"
	"google.golang.org/grpc/reflection"
)

func (g *GrpcHandler) grpcService() {
	fmt.Printf("gRPC server starting\n")

	proto.RegisterLangdServer(g.S, g)

	// Register reflection service on gRPC server.
	reflection.Register(g.S)

	err := g.S.Serve(g.lis)
	if err != nil {
		fmt.Printf("Got error when stopping grpc service:\n%s\n", err.Error())
	}

	fmt.Printf("gRPC server stopped\n")
}

// Load returns the CPU and memory load
func (g *GrpcHandler) Load(_ context.Context, _ *proto.EmptyRequest) (*proto.LoadReply, error) {
	load := &proto.LoadReply{
		CpuLoad:    g.srv.load.CPU(),
		MemoryLoad: g.srv.load.Memory(),
	}
	return load, nil
}

// Shutdown stops the service process
func (g *GrpcHandler) Shutdown(ctx context.Context, _ *proto.EmptyRequest) (*proto.EmptyReply, error) {
	g.srv.done <- true
	return &proto.EmptyReply{}, nil
}

// Startup is a no-op to start the service
func (g *GrpcHandler) Startup(_ context.Context, _ *proto.EmptyRequest) (*proto.EmptyReply, error) {
	return &proto.EmptyReply{}, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 oglemock

import (
	"fmt"
	"reflect"
)

// Create an Action that invokes the supplied actions one after another. The
// return values from the final action are used; others are ignored.
func DoAll(first Action, others ...Action) Action {
	return &doAll{
		wrapped: append([]Action{first}, others...),
	}
}

type doAll struct {
	wrapped []Action
}

func (a *doAll) SetSignature(signature reflect.Type) (err error) {
	for i, w := range a.wrapped {
		err = w.SetSignature(signature)
		if err != nil {
			err = fmt.Errorf("Action %v: %v", i, err)
			return
		}
	}

	return
}

func (a *doAll) Invoke(methodArgs []interface{}) (rets []interface{}) {
	panic("TODO")
}
<commit_msg>doAll.Invoke<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 oglemock

import (
	"fmt"
	"reflect"
)

// Create an Action that invokes the supplied actions one after another. The
// return values from the final action are used; others are ignored.
func DoAll(first Action, others ...Action) Action {
	return &doAll{
		wrapped: append([]Action{first}, others...),
	}
}

type doAll struct {
	wrapped []Action
}

func (a *doAll) SetSignature(signature reflect.Type) (err error) {
	for i, w := range a.wrapped {
		err = w.SetSignature(signature)
		if err != nil {
			err = fmt.Errorf("Action %v: %v", i, err)
			return
		}
	}

	return
}

func (a *doAll) Invoke(methodArgs []interface{}) (rets []interface{}) {
	for _, w := range a.wrapped {
		rets = w.Invoke(methodArgs)
	}

	return
}
<|endoftext|>


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

package main

import (
	"os"
	"path"
	"fmt"
	"log"
	"net/http"
	"github.com/dotcloud/docker"
	"github.com/dotcloud/docker/registry"
	"github.com/gorilla/mux"
	"regexp"
)

const ContainerDir = "/var/lib/containers/"

type Context struct {
	Path string
	Registry *registry.Registry
	Graph *docker.Graph
	Repositories *docker.TagStore
}

var context Context

func pullImage(c *Context, imgId, registry string, token []string) error {
	history, err := c.Registry.GetRemoteHistory(imgId, registry, token)
	if err != nil {
		return err
	}

	// FIXME: Try to stream the images?
	// FIXME: Launch the getRemoteImage() in goroutines
	for _, id := range history {
		if !c.Graph.Exists(id) {
			log.Printf("Pulling %s metadata\r\n", id)
			imgJson, err := c.Registry.GetRemoteImageJson(id, registry, token)
			if err != nil {
				// FIXME: Keep goging in case of error?
				return err
			}
			img, err := docker.NewImgJson(imgJson)
			if err != nil {
				return fmt.Errorf("Failed to parse json: %s", err)
			}

			// Get the layer
			log.Printf("Pulling %s fs layer\r\n", img.Id)
			layer, _, err := c.Registry.GetRemoteImageLayer(img.Id, registry, token)
			if err != nil {
				return err
			}
			if err := c.Graph.Register(layer, false, img); err != nil {
				return err
			}
		}
	}
	return nil
}

// TODO: add tag support
func pullHandler(w http.ResponseWriter, r *http.Request, c *Context) {
	vars := mux.Vars(r)
	remote := vars["remote"]

	repoData, err := c.Registry.GetRepositoryData(remote)
	if err != nil {
		log.Fatal(err)
	}

	tagsList, err := c.Registry.GetRemoteTags(repoData.Endpoints, remote, repoData.Tokens)
	if err != nil {
		log.Fatal(err)
	}

	for tag, id := range tagsList {
		repoData.ImgList[id].Tag = tag
	}

	for _, img := range repoData.ImgList {
		log.Printf("Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote)
		success := false

		for _, ep := range repoData.Endpoints {
			if err := pullImage(c, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil {
				fmt.Printf("Error while retrieving image for tag: %s; checking next endpoint\n", err)
				continue
			}
			success = true
			break
		}

		if !success {
			log.Fatal("Could not find repository on any of the indexed registries.")
		}
	}

	for tag, id := range tagsList {
		if err := c.Repositories.Set(remote, tag, id, true); err != nil {
			log.Fatal(err)
			return
		}
	}
	if err := c.Repositories.Save(); err != nil {
		log.Fatal(err)
		return
	}

	fmt.Fprintf(w, "%v\n", repoData)
}

func createHandler(w http.ResponseWriter, r *http.Request, c *Context) {
	imageName := r.FormValue("image")

	// TODO: @philips Don't hardcode the tag name here
	image, err := c.Repositories.GetImage(imageName, "latest")
	if err != nil {
		log.Fatal(err)
		return
	}

	if imageName == "" {
		w.WriteHeader(404)
		fmt.Fprintf(w, "Cannot find container image: %s", imageName)
		return
	}

	// Figure out the resting place of the container
	vars := mux.Vars(r)
	container := vars["container"]

	validID := regexp.MustCompile(`^[A-Za-z0-9]+$`)
	if !validID.MatchString(container) {
		w.WriteHeader(400)
		fmt.Fprintf(w, "Invalid container name: %s\n", container)
		return
	}

	container = path.Join(ContainerDir, container)

	err = os.Mkdir(container, 0700)
	if os.IsExist(err) {
		w.WriteHeader(400)
		fmt.Fprintf(w, "Existing container: %s\n", container)
		return
	}
	if err != nil {
		w.WriteHeader(400)
		fmt.Fprint(w, "Error")
		log.Fatal(err)
		return
	}

	copyAll := func(img *docker.Image) (err error) {
		tarball, err := image.TarLayer(docker.Uncompressed)
		if err != nil {
			return err
		}
		if err := docker.Untar(tarball, container); err != nil {
			return err
		}
		return
	}

	err = image.WalkHistory(copyAll)

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

	fmt.Fprint(w, "ok")
	return
}

func setupDocker(r *mux.Router, o Options) {
	context.Path = o.Path
	p := path.Join(context.Path, "containers")
	if err := os.MkdirAll(p, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
		return
	}
	context.Registry = registry.NewRegistry(p)

	p = path.Join(context.Path, "graph")
	if err := os.MkdirAll(p, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
		return
	}
	g, _ := docker.NewGraph(p)
	context.Graph = g

	p = path.Join(context.Path, "repositories")
	t, _ := docker.NewTagStore(p, g)
	context.Repositories = t

	makeHandler := func(fn func(http.ResponseWriter, *http.Request, *Context)) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			fn(w, r, &context)
		}
	}

	r.HandleFunc("/registry/pull/{remote:.*}", makeHandler(pullHandler))
	r.HandleFunc("/container/create/{container:.*}", makeHandler(createHandler))
}
<commit_msg>fix(docker): use img not image in copyAll<commit_after>/*
*  Copyright 2013 CoreOS, Inc
*  Copyright 2013 Docker Authors
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package main

import (
	"os"
	"path"
	"fmt"
	"log"
	"net/http"
	"github.com/dotcloud/docker"
	"github.com/dotcloud/docker/registry"
	"github.com/gorilla/mux"
	"regexp"
)

const ContainerDir = "/var/lib/containers/"

type Context struct {
	Path string
	Registry *registry.Registry
	Graph *docker.Graph
	Repositories *docker.TagStore
}

var context Context

func pullImage(c *Context, imgId, registry string, token []string) error {
	history, err := c.Registry.GetRemoteHistory(imgId, registry, token)
	if err != nil {
		return err
	}

	// FIXME: Try to stream the images?
	// FIXME: Launch the getRemoteImage() in goroutines
	for _, id := range history {
		if !c.Graph.Exists(id) {
			log.Printf("Pulling %s metadata\r\n", id)
			imgJson, err := c.Registry.GetRemoteImageJson(id, registry, token)
			if err != nil {
				// FIXME: Keep goging in case of error?
				return err
			}
			img, err := docker.NewImgJson(imgJson)
			if err != nil {
				return fmt.Errorf("Failed to parse json: %s", err)
			}

			// Get the layer
			log.Printf("Pulling %s fs layer\r\n", img.Id)
			layer, _, err := c.Registry.GetRemoteImageLayer(img.Id, registry, token)
			if err != nil {
				return err
			}
			if err := c.Graph.Register(layer, false, img); err != nil {
				return err
			}
		}
	}
	return nil
}

// TODO: add tag support
func pullHandler(w http.ResponseWriter, r *http.Request, c *Context) {
	vars := mux.Vars(r)
	remote := vars["remote"]

	repoData, err := c.Registry.GetRepositoryData(remote)
	if err != nil {
		log.Fatal(err)
	}

	tagsList, err := c.Registry.GetRemoteTags(repoData.Endpoints, remote, repoData.Tokens)
	if err != nil {
		log.Fatal(err)
	}

	for tag, id := range tagsList {
		repoData.ImgList[id].Tag = tag
	}

	for _, img := range repoData.ImgList {
		log.Printf("Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote)
		success := false

		for _, ep := range repoData.Endpoints {
			if err := pullImage(c, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil {
				fmt.Printf("Error while retrieving image for tag: %s; checking next endpoint\n", err)
				continue
			}
			success = true
			break
		}

		if !success {
			log.Fatal("Could not find repository on any of the indexed registries.")
		}
	}

	for tag, id := range tagsList {
		if err := c.Repositories.Set(remote, tag, id, true); err != nil {
			log.Fatal(err)
			return
		}
	}
	if err := c.Repositories.Save(); err != nil {
		log.Fatal(err)
		return
	}

	fmt.Fprintf(w, "%v\n", repoData)
}

func createHandler(w http.ResponseWriter, r *http.Request, c *Context) {
	imageName := r.FormValue("image")

	// TODO: @philips Don't hardcode the tag name here
	image, err := c.Repositories.GetImage(imageName, "latest")
	if err != nil {
		log.Fatal(err)
		return
	}

	if imageName == "" {
		w.WriteHeader(404)
		fmt.Fprintf(w, "Cannot find container image: %s", imageName)
		return
	}

	// Figure out the resting place of the container
	vars := mux.Vars(r)
	container := vars["container"]

	validID := regexp.MustCompile(`^[A-Za-z0-9]+$`)
	if !validID.MatchString(container) {
		w.WriteHeader(400)
		fmt.Fprintf(w, "Invalid container name: %s\n", container)
		return
	}

	container = path.Join(ContainerDir, container)

	err = os.Mkdir(container, 0700)
	if os.IsExist(err) {
		w.WriteHeader(400)
		fmt.Fprintf(w, "Existing container: %s\n", container)
		return
	}
	if err != nil {
		w.WriteHeader(400)
		fmt.Fprint(w, "Error")
		log.Fatal(err)
		return
	}

	copyAll := func(img *docker.Image) (err error) {
		log.Printf("Copying %s into %s", img.Id, container)
		tarball, err := img.TarLayer(docker.Uncompressed)
		if err != nil {
			return err
		}
		if err := docker.Untar(tarball, container); err != nil {
			return err
		}
		return
	}

	err = image.WalkHistory(copyAll)

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

	fmt.Fprint(w, "ok")
	return
}

func setupDocker(r *mux.Router, o Options) {
	context.Path = o.Path
	p := path.Join(context.Path, "containers")
	if err := os.MkdirAll(p, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
		return
	}
	context.Registry = registry.NewRegistry(p)

	p = path.Join(context.Path, "graph")
	if err := os.MkdirAll(p, 0700); err != nil && !os.IsExist(err) {
		log.Fatal(err)
		return
	}
	g, _ := docker.NewGraph(p)
	context.Graph = g

	p = path.Join(context.Path, "repositories")
	t, _ := docker.NewTagStore(p, g)
	context.Repositories = t

	makeHandler := func(fn func(http.ResponseWriter, *http.Request, *Context)) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			fn(w, r, &context)
		}
	}

	r.HandleFunc("/registry/pull/{remote:.*}", makeHandler(pullHandler))
	r.HandleFunc("/container/create/{container:.*}", makeHandler(createHandler))
}
<|endoftext|>