<commit_before>package scheduler

import (
	"fmt"
	"strings"

	"github.com/docker/swarmkit/api"
	"github.com/docker/swarmkit/manager/constraint"
)

// Filter checks whether the given task can run on the given node.
// A filter may only operate
type Filter interface {
	// SetTask returns true when the filter is enabled for a given task
	// and assigns the task to the filter. It returns false if the filter
	// isn't applicable to this task.  For instance, a constraints filter
	// would return `false` if the task doesn't contain any constraints.
	SetTask(*api.Task) bool

	// Check returns true if the task assigned by SetTask can be scheduled
	// into the given node. This function should not be called if SetTask
	// returned false.
	Check(*NodeInfo) bool

	// Explain what a failure of this filter means
	Explain(nodes int) string
}

// ReadyFilter checks that the node is ready to schedule tasks.
type ReadyFilter struct {
}

// SetTask returns true when the filter is enabled for a given task.
func (f *ReadyFilter) SetTask(_ *api.Task) bool {
	return true
}

// Check returns true if the task can be scheduled into the given node.
func (f *ReadyFilter) Check(n *NodeInfo) bool {
	return n.Status.State == api.NodeStatus_READY &&
		n.Spec.Availability == api.NodeAvailabilityActive
}

// Explain returns an explanation of a failure.
func (f *ReadyFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "1 node not available for new tasks"
	}
	return fmt.Sprintf("%d nodes not available for new tasks", nodes)
}

// ResourceFilter checks that the node has enough resources available to run
// the task.
type ResourceFilter struct {
	reservations *api.Resources
}

// SetTask returns true when the filter is enabled for a given task.
func (f *ResourceFilter) SetTask(t *api.Task) bool {
	r := t.Spec.Resources
	if r == nil || r.Reservations == nil {
		return false
	}
	if r.Reservations.NanoCPUs == 0 && r.Reservations.MemoryBytes == 0 {
		return false
	}
	f.reservations = r.Reservations
	return true
}

// Check returns true if the task can be scheduled into the given node.
func (f *ResourceFilter) Check(n *NodeInfo) bool {
	if f.reservations.NanoCPUs > n.AvailableResources.NanoCPUs {
		return false
	}

	if f.reservations.MemoryBytes > n.AvailableResources.MemoryBytes {
		return false
	}

	return true
}

// Explain returns an explanation of a failure.
func (f *ResourceFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "insufficient resources on 1 node"
	}
	return fmt.Sprintf("insufficient resources on %d nodes", nodes)
}

// PluginFilter checks that the node has a specific volume plugin installed
type PluginFilter struct {
	t *api.Task
}

func referencesVolumePlugin(mount api.Mount) bool {
	return mount.Type == api.MountTypeVolume &&
		mount.VolumeOptions != nil &&
		mount.VolumeOptions.DriverConfig != nil &&
		mount.VolumeOptions.DriverConfig.Name != "" &&
		mount.VolumeOptions.DriverConfig.Name != "local"

}

// SetTask returns true when the filter is enabled for a given task.
func (f *PluginFilter) SetTask(t *api.Task) bool {
	c := t.Spec.GetContainer()

	var volumeTemplates bool
	if c != nil {
		for _, mount := range c.Mounts {
			if referencesVolumePlugin(mount) {
				volumeTemplates = true
				break
			}
		}
	}

	if (c != nil && volumeTemplates) || len(t.Networks) > 0 {
		f.t = t
		return true
	}

	return false
}

// Check returns true if the task can be scheduled into the given node.
// TODO(amitshukla): investigate storing Plugins as a map so it can be easily probed
func (f *PluginFilter) Check(n *NodeInfo) bool {
	// Get list of plugins on the node
	nodePlugins := n.Description.Engine.Plugins

	// Check if all volume plugins required by task are installed on node
	container := f.t.Spec.GetContainer()
	if container != nil {
		for _, mount := range container.Mounts {
			if referencesVolumePlugin(mount) {
				if !f.pluginExistsOnNode("Volume", mount.VolumeOptions.DriverConfig.Name, nodePlugins) {
					return false
				}
			}
		}
	}

	// Check if all network plugins required by task are installed on node
	for _, tn := range f.t.Networks {
		if tn.Network != nil && tn.Network.DriverState != nil && tn.Network.DriverState.Name != "" {
			if !f.pluginExistsOnNode("Network", tn.Network.DriverState.Name, nodePlugins) {
				return false
			}
		}
	}
	return true
}

// pluginExistsOnNode returns true if the (pluginName, pluginType) pair is present in nodePlugins
func (f *PluginFilter) pluginExistsOnNode(pluginType string, pluginName string, nodePlugins []api.PluginDescription) bool {
	for _, np := range nodePlugins {
		if pluginType != np.Type {
			continue
		}
		if pluginName == np.Name {
			return true
		}
		// This does not use the reference package to avoid the
		// overhead of parsing references as part of the scheduling
		// loop. This is okay only because plugin names are a very
		// strict subset of the reference grammar that is always
		// name:tag.
		if strings.HasPrefix(np.Name, pluginName) && np.Name[len(pluginName):] == ":latest" {
			return true
		}
	}
	return false
}

// Explain returns an explanation of a failure.
func (f *PluginFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "missing plugin on 1 node"
	}
	return fmt.Sprintf("missing plugin on %d nodes", nodes)
}

// ConstraintFilter selects only nodes that match certain labels.
type ConstraintFilter struct {
	constraints []constraint.Constraint
}

// SetTask returns true when the filter is enable for a given task.
func (f *ConstraintFilter) SetTask(t *api.Task) bool {
	if t.Spec.Placement == nil || len(t.Spec.Placement.Constraints) == 0 {
		return false
	}

	constraints, err := constraint.Parse(t.Spec.Placement.Constraints)
	if err != nil {
		// constraints have been validated at controlapi
		// if in any case it finds an error here, treat this task
		// as constraint filter disabled.
		return false
	}
	f.constraints = constraints
	return true
}

// Check returns true if the task's constraint is supported by the given node.
func (f *ConstraintFilter) Check(n *NodeInfo) bool {
	return constraint.NodeMatches(f.constraints, n.Node)
}

// Explain returns an explanation of a failure.
func (f *ConstraintFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "scheduling constraints not satisfied on 1 node"
	}
	return fmt.Sprintf("scheduling constraints not satisfied on %d nodes", nodes)
}

// PlatformFilter selects only nodes that run the required platform.
type PlatformFilter struct {
	supportedPlatforms []*api.Platform
}

// SetTask returns true when the filter is enabled for a given task.
func (f *PlatformFilter) SetTask(t *api.Task) bool {
	placement := t.Spec.Placement
	if placement != nil {
		// copy the platform information
		f.supportedPlatforms = placement.Platforms
		if len(placement.Platforms) > 0 {
			return true
		}
	}
	return false
}

// Check returns true if the task can be scheduled into the given node.
func (f *PlatformFilter) Check(n *NodeInfo) bool {
	// if the supportedPlatforms field is empty, then either it wasn't
	// provided or there are no constraints
	if len(f.supportedPlatforms) == 0 {
		return true
	}
	// check if the platform for the node is supported
	if n.Description != nil {
		if nodePlatform := n.Description.Platform; nodePlatform != nil {
			for _, p := range f.supportedPlatforms {
				if f.platformEqual(*p, *nodePlatform) {
					return true
				}
			}
		}
	}
	return false
}

func (f *PlatformFilter) platformEqual(imgPlatform, nodePlatform api.Platform) bool {
	// normalize "x86_64" architectures to "amd64"
	if imgPlatform.Architecture == "x86_64" {
		imgPlatform.Architecture = "amd64"
	}
	if nodePlatform.Architecture == "x86_64" {
		nodePlatform.Architecture = "amd64"
	}

	if (imgPlatform.Architecture == "" || imgPlatform.Architecture == nodePlatform.Architecture) && (imgPlatform.OS == "" || imgPlatform.OS == nodePlatform.OS) {
		return true
	}
	return false
}

// Explain returns an explanation of a failure.
func (f *PlatformFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "unsupported platform on 1 node"
	}
	return fmt.Sprintf("unsupported platform on %d nodes", nodes)
}

// HostPortFilter checks that the node has a specific port available.
type HostPortFilter struct {
	t *api.Task
}

// SetTask returns true when the filter is enabled for a given task.
func (f *HostPortFilter) SetTask(t *api.Task) bool {
	if t.Endpoint != nil {
		for _, port := range t.Endpoint.Ports {
			if port.PublishMode == api.PublishModeHost && port.PublishedPort != 0 {
				f.t = t
				return true
			}
		}
	}

	return false
}

// Check returns true if the task can be scheduled into the given node.
func (f *HostPortFilter) Check(n *NodeInfo) bool {
	for _, port := range f.t.Endpoint.Ports {
		if port.PublishMode == api.PublishModeHost && port.PublishedPort != 0 {
			portSpec := hostPortSpec{protocol: port.Protocol, publishedPort: port.PublishedPort}
			if _, ok := n.usedHostPorts[portSpec]; ok {
				return false
			}
		}
	}

	return true
}

// Explain returns an explanation of a failure.
func (f *HostPortFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "host-mode port already in use on 1 node"
	}
	return fmt.Sprintf("host-mode port already in use on %d nodes", nodes)
}
<commit_msg>Only filter on plugins if running with docker engine.<commit_after>package scheduler

import (
	"fmt"
	"strings"

	"github.com/docker/swarmkit/api"
	"github.com/docker/swarmkit/manager/constraint"
)

// Filter checks whether the given task can run on the given node.
// A filter may only operate
type Filter interface {
	// SetTask returns true when the filter is enabled for a given task
	// and assigns the task to the filter. It returns false if the filter
	// isn't applicable to this task.  For instance, a constraints filter
	// would return `false` if the task doesn't contain any constraints.
	SetTask(*api.Task) bool

	// Check returns true if the task assigned by SetTask can be scheduled
	// into the given node. This function should not be called if SetTask
	// returned false.
	Check(*NodeInfo) bool

	// Explain what a failure of this filter means
	Explain(nodes int) string
}

// ReadyFilter checks that the node is ready to schedule tasks.
type ReadyFilter struct {
}

// SetTask returns true when the filter is enabled for a given task.
func (f *ReadyFilter) SetTask(_ *api.Task) bool {
	return true
}

// Check returns true if the task can be scheduled into the given node.
func (f *ReadyFilter) Check(n *NodeInfo) bool {
	return n.Status.State == api.NodeStatus_READY &&
		n.Spec.Availability == api.NodeAvailabilityActive
}

// Explain returns an explanation of a failure.
func (f *ReadyFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "1 node not available for new tasks"
	}
	return fmt.Sprintf("%d nodes not available for new tasks", nodes)
}

// ResourceFilter checks that the node has enough resources available to run
// the task.
type ResourceFilter struct {
	reservations *api.Resources
}

// SetTask returns true when the filter is enabled for a given task.
func (f *ResourceFilter) SetTask(t *api.Task) bool {
	r := t.Spec.Resources
	if r == nil || r.Reservations == nil {
		return false
	}
	if r.Reservations.NanoCPUs == 0 && r.Reservations.MemoryBytes == 0 {
		return false
	}
	f.reservations = r.Reservations
	return true
}

// Check returns true if the task can be scheduled into the given node.
func (f *ResourceFilter) Check(n *NodeInfo) bool {
	if f.reservations.NanoCPUs > n.AvailableResources.NanoCPUs {
		return false
	}

	if f.reservations.MemoryBytes > n.AvailableResources.MemoryBytes {
		return false
	}

	return true
}

// Explain returns an explanation of a failure.
func (f *ResourceFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "insufficient resources on 1 node"
	}
	return fmt.Sprintf("insufficient resources on %d nodes", nodes)
}

// PluginFilter checks that the node has a specific volume plugin installed
type PluginFilter struct {
	t *api.Task
}

func referencesVolumePlugin(mount api.Mount) bool {
	return mount.Type == api.MountTypeVolume &&
		mount.VolumeOptions != nil &&
		mount.VolumeOptions.DriverConfig != nil &&
		mount.VolumeOptions.DriverConfig.Name != "" &&
		mount.VolumeOptions.DriverConfig.Name != "local"

}

// SetTask returns true when the filter is enabled for a given task.
func (f *PluginFilter) SetTask(t *api.Task) bool {
	c := t.Spec.GetContainer()

	var volumeTemplates bool
	if c != nil {
		for _, mount := range c.Mounts {
			if referencesVolumePlugin(mount) {
				volumeTemplates = true
				break
			}
		}
	}

	if (c != nil && volumeTemplates) || len(t.Networks) > 0 {
		f.t = t
		return true
	}

	return false
}

// Check returns true if the task can be scheduled into the given node.
// TODO(amitshukla): investigate storing Plugins as a map so it can be easily probed
func (f *PluginFilter) Check(n *NodeInfo) bool {
	if n.Description == nil || n.Description.Engine == nil {
		// If the node is not running Engine, plugins are not
		// supported.
		return true
	}

	// Get list of plugins on the node
	nodePlugins := n.Description.Engine.Plugins

	// Check if all volume plugins required by task are installed on node
	container := f.t.Spec.GetContainer()
	if container != nil {
		for _, mount := range container.Mounts {
			if referencesVolumePlugin(mount) {
				if !f.pluginExistsOnNode("Volume", mount.VolumeOptions.DriverConfig.Name, nodePlugins) {
					return false
				}
			}
		}
	}

	// Check if all network plugins required by task are installed on node
	for _, tn := range f.t.Networks {
		if tn.Network != nil && tn.Network.DriverState != nil && tn.Network.DriverState.Name != "" {
			if !f.pluginExistsOnNode("Network", tn.Network.DriverState.Name, nodePlugins) {
				return false
			}
		}
	}
	return true
}

// pluginExistsOnNode returns true if the (pluginName, pluginType) pair is present in nodePlugins
func (f *PluginFilter) pluginExistsOnNode(pluginType string, pluginName string, nodePlugins []api.PluginDescription) bool {
	for _, np := range nodePlugins {
		if pluginType != np.Type {
			continue
		}
		if pluginName == np.Name {
			return true
		}
		// This does not use the reference package to avoid the
		// overhead of parsing references as part of the scheduling
		// loop. This is okay only because plugin names are a very
		// strict subset of the reference grammar that is always
		// name:tag.
		if strings.HasPrefix(np.Name, pluginName) && np.Name[len(pluginName):] == ":latest" {
			return true
		}
	}
	return false
}

// Explain returns an explanation of a failure.
func (f *PluginFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "missing plugin on 1 node"
	}
	return fmt.Sprintf("missing plugin on %d nodes", nodes)
}

// ConstraintFilter selects only nodes that match certain labels.
type ConstraintFilter struct {
	constraints []constraint.Constraint
}

// SetTask returns true when the filter is enable for a given task.
func (f *ConstraintFilter) SetTask(t *api.Task) bool {
	if t.Spec.Placement == nil || len(t.Spec.Placement.Constraints) == 0 {
		return false
	}

	constraints, err := constraint.Parse(t.Spec.Placement.Constraints)
	if err != nil {
		// constraints have been validated at controlapi
		// if in any case it finds an error here, treat this task
		// as constraint filter disabled.
		return false
	}
	f.constraints = constraints
	return true
}

// Check returns true if the task's constraint is supported by the given node.
func (f *ConstraintFilter) Check(n *NodeInfo) bool {
	return constraint.NodeMatches(f.constraints, n.Node)
}

// Explain returns an explanation of a failure.
func (f *ConstraintFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "scheduling constraints not satisfied on 1 node"
	}
	return fmt.Sprintf("scheduling constraints not satisfied on %d nodes", nodes)
}

// PlatformFilter selects only nodes that run the required platform.
type PlatformFilter struct {
	supportedPlatforms []*api.Platform
}

// SetTask returns true when the filter is enabled for a given task.
func (f *PlatformFilter) SetTask(t *api.Task) bool {
	placement := t.Spec.Placement
	if placement != nil {
		// copy the platform information
		f.supportedPlatforms = placement.Platforms
		if len(placement.Platforms) > 0 {
			return true
		}
	}
	return false
}

// Check returns true if the task can be scheduled into the given node.
func (f *PlatformFilter) Check(n *NodeInfo) bool {
	// if the supportedPlatforms field is empty, then either it wasn't
	// provided or there are no constraints
	if len(f.supportedPlatforms) == 0 {
		return true
	}
	// check if the platform for the node is supported
	if n.Description != nil {
		if nodePlatform := n.Description.Platform; nodePlatform != nil {
			for _, p := range f.supportedPlatforms {
				if f.platformEqual(*p, *nodePlatform) {
					return true
				}
			}
		}
	}
	return false
}

func (f *PlatformFilter) platformEqual(imgPlatform, nodePlatform api.Platform) bool {
	// normalize "x86_64" architectures to "amd64"
	if imgPlatform.Architecture == "x86_64" {
		imgPlatform.Architecture = "amd64"
	}
	if nodePlatform.Architecture == "x86_64" {
		nodePlatform.Architecture = "amd64"
	}

	if (imgPlatform.Architecture == "" || imgPlatform.Architecture == nodePlatform.Architecture) && (imgPlatform.OS == "" || imgPlatform.OS == nodePlatform.OS) {
		return true
	}
	return false
}

// Explain returns an explanation of a failure.
func (f *PlatformFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "unsupported platform on 1 node"
	}
	return fmt.Sprintf("unsupported platform on %d nodes", nodes)
}

// HostPortFilter checks that the node has a specific port available.
type HostPortFilter struct {
	t *api.Task
}

// SetTask returns true when the filter is enabled for a given task.
func (f *HostPortFilter) SetTask(t *api.Task) bool {
	if t.Endpoint != nil {
		for _, port := range t.Endpoint.Ports {
			if port.PublishMode == api.PublishModeHost && port.PublishedPort != 0 {
				f.t = t
				return true
			}
		}
	}

	return false
}

// Check returns true if the task can be scheduled into the given node.
func (f *HostPortFilter) Check(n *NodeInfo) bool {
	for _, port := range f.t.Endpoint.Ports {
		if port.PublishMode == api.PublishModeHost && port.PublishedPort != 0 {
			portSpec := hostPortSpec{protocol: port.Protocol, publishedPort: port.PublishedPort}
			if _, ok := n.usedHostPorts[portSpec]; ok {
				return false
			}
		}
	}

	return true
}

// Explain returns an explanation of a failure.
func (f *HostPortFilter) Explain(nodes int) string {
	if nodes == 1 {
		return "host-mode port already in use on 1 node"
	}
	return fmt.Sprintf("host-mode port already in use on %d nodes", nodes)
}
<|endoftext|>


<commit_before>package security

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"strings"

	"github.com/chrislusf/seaweedfs/weed/glog"
)

var (
	ErrUnauthorized = errors.New("unauthorized token")
)

/*
Guard is to ensure data access security.
There are 2 ways to check access:
1. white list. It's checking request ip address.
2. JSON Web Token(JWT) generated from secretKey.
  The jwt can come from:
  1. url parameter jwt=...
  2. request header "Authorization"
  3. cookie with the name "jwt"

The white list is checked first because it is easy.
Then the JWT is checked.

The Guard will also check these claims if provided:
1. "exp" Expiration Time
2. "nbf" Not Before

Generating JWT:
1. use HS256 to sign
2. optionally set "exp", "nbf" fields, in Unix time,
   the number of seconds elapsed since January 1, 1970 UTC.

Referenced:
https://github.com/pkieltyka/jwtauth/blob/master/jwtauth.go

*/
type Guard struct {
	whiteList []string
	SecretKey Secret

	isActive bool
}

func NewGuard(whiteList []string, secretKey string) *Guard {
	g := &Guard{whiteList: whiteList, SecretKey: Secret(secretKey)}
	g.isActive = len(g.whiteList) != 0 || len(g.SecretKey) != 0
	return g
}

func (g *Guard) WhiteList(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	if !g.isActive {
		//if no security needed, just skip all checkings
		return f
	}
	return func(w http.ResponseWriter, r *http.Request) {
		if err := g.checkWhiteList(w, r); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		f(w, r)
	}
}

func (g *Guard) Secure(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	if !g.isActive {
		//if no security needed, just skip all checkings
		return f
	}
	return func(w http.ResponseWriter, r *http.Request) {
		if err := g.checkJwt(w, r); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		f(w, r)
	}
}

func GetActualRemoteHost(r *http.Request) (host string, err error) {
	host = r.Header.Get("HTTP_X_FORWARDED_FOR")
	if host == "" {
		host = r.Header.Get("X-FORWARDED-FOR")
		glog.V(0).Infof("X-FORWARDED-FOR: %s", host)
	} else {
		glog.V(0).Infof("HTTP_X_FORWARDED_FOR: %s", host)
	}
	if strings.Contains(host, ",") {
		host = host[0:strings.Index(host, ",")]
	}
	if host == "" {
		host, _, err = net.SplitHostPort(r.RemoteAddr)
	}
	return
}

func (g *Guard) checkWhiteList(w http.ResponseWriter, r *http.Request) error {
	if len(g.whiteList) == 0 {
		return nil
	}

	host, err := GetActualRemoteHost(r)
	if err == nil {
		for _, ip := range g.whiteList {

			// If the whitelist entry contains a "/" it
			// is a CIDR range, and we should check the
			// remote host is within it
			if strings.Contains(ip, "/") {
				_, cidrnet, err := net.ParseCIDR(ip)
				if err != nil {
					panic(err)
				}
				remote := net.ParseIP(host)
				if cidrnet.Contains(remote) {
					return nil
				}
			}

			//
			// Otherwise we're looking for a literal match.
			//
			if ip == host {
				return nil
			}
		}
	}

	glog.V(0).Infof("Not in whitelist: %s", host)
	return fmt.Errorf("Not in whitelis: %s", host)
}

func (g *Guard) checkJwt(w http.ResponseWriter, r *http.Request) error {
	if g.checkWhiteList(w, r) == nil {
		return nil
	}

	if len(g.SecretKey) == 0 {
		return nil
	}

	tokenStr := GetJwt(r)

	if tokenStr == "" {
		return ErrUnauthorized
	}

	// Verify the token
	token, err := DecodeJwt(g.SecretKey, tokenStr)
	if err != nil {
		glog.V(1).Infof("Token verification error from %s: %v", r.RemoteAddr, err)
		return ErrUnauthorized
	}
	if !token.Valid {
		glog.V(1).Infof("Token invliad from %s: %v", r.RemoteAddr, tokenStr)
		return ErrUnauthorized
	}

	glog.V(1).Infof("No permission from %s", r.RemoteAddr)
	return fmt.Errorf("No write permisson from %s", r.RemoteAddr)
}
<commit_msg>修复whitelist验证的一个bug<commit_after>package security

import (
	"errors"
	"fmt"
	"net"
	"net/http"
	"strings"

	"github.com/chrislusf/seaweedfs/weed/glog"
)

var (
	ErrUnauthorized = errors.New("unauthorized token")
)

/*
Guard is to ensure data access security.
There are 2 ways to check access:
1. white list. It's checking request ip address.
2. JSON Web Token(JWT) generated from secretKey.
  The jwt can come from:
  1. url parameter jwt=...
  2. request header "Authorization"
  3. cookie with the name "jwt"

The white list is checked first because it is easy.
Then the JWT is checked.

The Guard will also check these claims if provided:
1. "exp" Expiration Time
2. "nbf" Not Before

Generating JWT:
1. use HS256 to sign
2. optionally set "exp", "nbf" fields, in Unix time,
   the number of seconds elapsed since January 1, 1970 UTC.

Referenced:
https://github.com/pkieltyka/jwtauth/blob/master/jwtauth.go

*/
type Guard struct {
	whiteList []string
	SecretKey Secret

	isActive bool
}

func NewGuard(whiteList []string, secretKey string) *Guard {
	g := &Guard{whiteList: whiteList, SecretKey: Secret(secretKey)}
	g.isActive = len(g.whiteList) != 0 || len(g.SecretKey) != 0
	return g
}

func (g *Guard) WhiteList(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	if !g.isActive {
		//if no security needed, just skip all checkings
		return f
	}
	return func(w http.ResponseWriter, r *http.Request) {
		if err := g.checkWhiteList(w, r); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		f(w, r)
	}
}

func (g *Guard) Secure(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
	if !g.isActive {
		//if no security needed, just skip all checkings
		return f
	}
	return func(w http.ResponseWriter, r *http.Request) {
		if err := g.checkJwt(w, r); err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		f(w, r)
	}
}

func GetActualRemoteHost(r *http.Request) (host string, err error) {
	host = r.Header.Get("HTTP_X_FORWARDED_FOR")
	if host == "" {
		host = r.Header.Get("X-FORWARDED-FOR")
	}
	if strings.Contains(host, ",") {
		host = host[0:strings.Index(host, ",")]
	}
	if host == "" {
		host, _, err = net.SplitHostPort(r.RemoteAddr)
	} else {
		host, _, err = net.SplitHostPort(host)
	}
	return
}

func (g *Guard) checkWhiteList(w http.ResponseWriter, r *http.Request) error {
	if len(g.whiteList) == 0 {
		return nil
	}

	host, err := GetActualRemoteHost(r)
	if err == nil {
		for _, ip := range g.whiteList {

			// If the whitelist entry contains a "/" it
			// is a CIDR range, and we should check the
			// remote host is within it
			if strings.Contains(ip, "/") {
				_, cidrnet, err := net.ParseCIDR(ip)
				if err != nil {
					panic(err)
				}
				remote := net.ParseIP(host)
				if cidrnet.Contains(remote) {
					return nil
				}
			}

			//
			// Otherwise we're looking for a literal match.
			//
			if ip == host {
				return nil
			}
		}
	}

	glog.V(0).Infof("Not in whitelist: %s", host)
	return fmt.Errorf("Not in whitelis: %s", host)
}

func (g *Guard) checkJwt(w http.ResponseWriter, r *http.Request) error {
	if g.checkWhiteList(w, r) == nil {
		return nil
	}

	if len(g.SecretKey) == 0 {
		return nil
	}

	tokenStr := GetJwt(r)

	if tokenStr == "" {
		return ErrUnauthorized
	}

	// Verify the token
	token, err := DecodeJwt(g.SecretKey, tokenStr)
	if err != nil {
		glog.V(1).Infof("Token verification error from %s: %v", r.RemoteAddr, err)
		return ErrUnauthorized
	}
	if !token.Valid {
		glog.V(1).Infof("Token invliad from %s: %v", r.RemoteAddr, tokenStr)
		return ErrUnauthorized
	}

	glog.V(1).Infof("No permission from %s", r.RemoteAddr)
	return fmt.Errorf("No write permisson from %s", r.RemoteAddr)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sort"
	"strings"

	"github.com/samuel/go-thrift/parser"
)

type byServiceName []*Service

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

func wrapServices(v *parser.Thrift, state *State) ([]*Service, error) {
	var services []*Service
	for _, s := range v.Services {
		if err := Validate(s); err != nil {
			return nil, err
		}

		services = append(services, &Service{s, state, nil})
	}

	// Have a stable ordering for services so code generation is consistent.
	sort.Sort(byServiceName(services))
	return services, nil
}

// Service is a wrapper for parser.Service.
type Service struct {
	*parser.Service

	state          *State
	ExtendsService *Service
}

// ThriftName returns the thrift identifier for this service.
func (s *Service) ThriftName() string {
	return s.Service.Name
}

// Interface returns the name of the interface representing the service.
func (s *Service) Interface() string {
	return "TChan" + goPublicName(s.Name)
}

// ClientStruct returns the name of the unexported struct that satisfies the interface as a client.
func (s *Service) ClientStruct() string {
	return "tchan" + goPublicName(s.Name) + "Client"
}

// ClientConstructor returns the name of the constructor used to create a client.
func (s *Service) ClientConstructor() string {
	return "NewTChan" + goPublicName(s.Name) + "Client"
}

// InternalClientConstructor returns the name of the internal constructor used to create the client
// struct directly. This returns the type of ClientStruct rather than the interface, and is used
// to recursively create any base service clients.
func (s *Service) InternalClientConstructor() string {
	return "newTChan" + goPublicName(s.Name) + "Client"
}

// ServerStruct returns the name of the unexported struct that satisfies TChanServer.
func (s *Service) ServerStruct() string {
	return "tchan" + goPublicName(s.Name) + "Server"
}

// ServerConstructor returns the name of the constructor used to create the TChanServer interface.
func (s *Service) ServerConstructor() string {
	return "NewTChan" + goPublicName(s.Name) + "Server"
}

// InternalServerConstructor is the name of the internal constructor used to create the service
// directly. This returns the type of ServerStruct rather than the interface, and is used
// to recursively create any base service structs.
func (s *Service) InternalServerConstructor() string {
	return "newTChan" + goPublicName(s.Name) + "Server"
}

// HasExtends returns whether this service extends another service.
func (s *Service) HasExtends() bool {
	return s.ExtendsService != nil
}

// ExtendsServicePrefix returns a package selector (if any) for the extended service.
func (s *Service) ExtendsServicePrefix() string {
	if dotIndex := strings.Index(s.Extends, "."); dotIndex > 0 {
		return s.Extends[:dotIndex+1]
	}
	return ""
}

type byMethodName []*Method

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

// Methods returns the methods defined on this service.
func (s *Service) Methods() []*Method {
	var methods []*Method
	for _, m := range s.Service.Methods {
		methods = append(methods, &Method{m, s, s.state})
	}
	sort.Sort(byMethodName(methods))
	return methods
}

// Method is a wrapper for parser.Method.
type Method struct {
	*parser.Method

	service *Service
	state   *State
}

// ThriftName returns the thrift identifier for this function.
func (m *Method) ThriftName() string {
	return m.Method.Name
}

// Name returns the go method name.
func (m *Method) Name() string {
	return goPublicName(m.Method.Name)
}

// HandleFunc is the go method name for the handle function which decodes the payload.
func (m *Method) HandleFunc() string {
	return "handle" + goPublicName(m.Method.Name)
}

// Arguments returns the argument declarations for this method.
func (m *Method) Arguments() []*Field {
	var args []*Field
	for _, f := range m.Method.Arguments {
		args = append(args, &Field{f, m.state})
	}
	return args
}

// Exceptions returns the exceptions that this method may return.
func (m *Method) Exceptions() []*Field {
	var args []*Field
	for _, f := range m.Method.Exceptions {
		args = append(args, &Field{f, m.state})
	}
	return args
}

// HasReturn returns false if this method is declared as void in the Thrift file.
func (m *Method) HasReturn() bool {
	return m.Method.ReturnType != nil
}

// HasExceptions returns true if this method has
func (m *Method) HasExceptions() bool {
	return len(m.Method.Exceptions) > 0
}

func (m *Method) argResPrefix() string {
	return goPublicName(m.service.Name) + m.Name()
}

// ArgsType returns the Go name for the struct used to encode the method's arguments.
func (m *Method) ArgsType() string {
	return m.argResPrefix() + "Args"
}

// ResultType returns the Go name for the struct used to encode the method's result.
func (m *Method) ResultType() string {
	return m.argResPrefix() + "Result"
}

// ArgList returns the argument list for the function.
func (m *Method) ArgList() string {
	args := []string{"ctx " + contextType()}
	for _, arg := range m.Arguments() {
		args = append(args, arg.Declaration())
	}
	return strings.Join(args, ", ")
}

// CallList creates the call to a function satisfying Interface from an Args struct.
func (m *Method) CallList(reqStruct string) string {
	args := []string{"ctx"}
	for _, arg := range m.Arguments() {
		args = append(args, reqStruct+"."+arg.ArgStructName())
	}
	return strings.Join(args, ", ")
}

// RetType returns the go return type of the method.
func (m *Method) RetType() string {
	if !m.HasReturn() {
		return "error"
	}
	return fmt.Sprintf("(%v, %v)", m.state.goType(m.Method.ReturnType), "error")
}

// WrapResult wraps the result variable before being used in the result struct.
func (m *Method) WrapResult(respVar string) string {
	if !m.HasReturn() {
		panic("cannot wrap a return when there is no return mode")
	}

	if m.state.isResultPointer(m.ReturnType) {
		return respVar
	}
	return "&" + respVar
}

// ReturnWith takes the result name and the error name, and generates the return expression.
func (m *Method) ReturnWith(respName string, errName string) string {
	if !m.HasReturn() {
		return errName
	}
	return fmt.Sprintf("%v, %v", respName, errName)
}

// Field is a wrapper for parser.Field.
type Field struct {
	*parser.Field

	state *State
}

// Declaration returns the declaration for this field.
func (a *Field) Declaration() string {
	return fmt.Sprintf("%s %s", a.Name(), a.ArgType())
}

// Name returns the field name.
func (a *Field) Name() string {
	return goName(a.Field.Name)
}

// ArgType returns the Go type for the given field.
func (a *Field) ArgType() string {
	return a.state.goType(a.Type)
}

// ArgStructName returns the name of this field in the Args struct generated by thrift.
func (a *Field) ArgStructName() string {
	return goPublicFieldName(a.Field.Name)
}
<commit_msg>Cache methods on Service object<commit_after>package main

import (
	"fmt"
	"sort"
	"strings"

	"github.com/samuel/go-thrift/parser"
)

type byServiceName []*Service

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

func wrapServices(v *parser.Thrift, state *State) ([]*Service, error) {
	var services []*Service
	for _, s := range v.Services {
		if err := Validate(s); err != nil {
			return nil, err
		}

		services = append(services, &Service{
			Service: s,
			state:   state,
		})
	}

	// Have a stable ordering for services so code generation is consistent.
	sort.Sort(byServiceName(services))
	return services, nil
}

// Service is a wrapper for parser.Service.
type Service struct {
	*parser.Service
	state *State

	// ExtendsService is not set in wrap, but in setExtends.
	ExtendsService *Service

	// methods is a cache of all methods.
	methods []*Method
}

// ThriftName returns the thrift identifier for this service.
func (s *Service) ThriftName() string {
	return s.Service.Name
}

// Interface returns the name of the interface representing the service.
func (s *Service) Interface() string {
	return "TChan" + goPublicName(s.Name)
}

// ClientStruct returns the name of the unexported struct that satisfies the interface as a client.
func (s *Service) ClientStruct() string {
	return "tchan" + goPublicName(s.Name) + "Client"
}

// ClientConstructor returns the name of the constructor used to create a client.
func (s *Service) ClientConstructor() string {
	return "NewTChan" + goPublicName(s.Name) + "Client"
}

// InternalClientConstructor returns the name of the internal constructor used to create the client
// struct directly. This returns the type of ClientStruct rather than the interface, and is used
// to recursively create any base service clients.
func (s *Service) InternalClientConstructor() string {
	return "newTChan" + goPublicName(s.Name) + "Client"
}

// ServerStruct returns the name of the unexported struct that satisfies TChanServer.
func (s *Service) ServerStruct() string {
	return "tchan" + goPublicName(s.Name) + "Server"
}

// ServerConstructor returns the name of the constructor used to create the TChanServer interface.
func (s *Service) ServerConstructor() string {
	return "NewTChan" + goPublicName(s.Name) + "Server"
}

// InternalServerConstructor is the name of the internal constructor used to create the service
// directly. This returns the type of ServerStruct rather than the interface, and is used
// to recursively create any base service structs.
func (s *Service) InternalServerConstructor() string {
	return "newTChan" + goPublicName(s.Name) + "Server"
}

// HasExtends returns whether this service extends another service.
func (s *Service) HasExtends() bool {
	return s.ExtendsService != nil
}

// ExtendsServicePrefix returns a package selector (if any) for the extended service.
func (s *Service) ExtendsServicePrefix() string {
	if dotIndex := strings.Index(s.Extends, "."); dotIndex > 0 {
		return s.Extends[:dotIndex+1]
	}
	return ""
}

type byMethodName []*Method

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

// Methods returns the methods on this service, not including methods from inherited services.
func (s *Service) Methods() []*Method {
	if s.methods != nil {
		return s.methods
	}

	for _, m := range s.Service.Methods {
		s.methods = append(s.methods, &Method{m, s, s.state})
	}
	sort.Sort(byMethodName(s.methods))
	return s.methods
}

// Method is a wrapper for parser.Method.
type Method struct {
	*parser.Method

	service *Service
	state   *State
}

// ThriftName returns the thrift identifier for this function.
func (m *Method) ThriftName() string {
	return m.Method.Name
}

// Name returns the go method name.
func (m *Method) Name() string {
	return goPublicName(m.Method.Name)
}

// HandleFunc is the go method name for the handle function which decodes the payload.
func (m *Method) HandleFunc() string {
	return "handle" + goPublicName(m.Method.Name)
}

// Arguments returns the argument declarations for this method.
func (m *Method) Arguments() []*Field {
	var args []*Field
	for _, f := range m.Method.Arguments {
		args = append(args, &Field{f, m.state})
	}
	return args
}

// Exceptions returns the exceptions that this method may return.
func (m *Method) Exceptions() []*Field {
	var args []*Field
	for _, f := range m.Method.Exceptions {
		args = append(args, &Field{f, m.state})
	}
	return args
}

// HasReturn returns false if this method is declared as void in the Thrift file.
func (m *Method) HasReturn() bool {
	return m.Method.ReturnType != nil
}

// HasExceptions returns true if this method has
func (m *Method) HasExceptions() bool {
	return len(m.Method.Exceptions) > 0
}

func (m *Method) argResPrefix() string {
	return goPublicName(m.service.Name) + m.Name()
}

// ArgsType returns the Go name for the struct used to encode the method's arguments.
func (m *Method) ArgsType() string {
	return m.argResPrefix() + "Args"
}

// ResultType returns the Go name for the struct used to encode the method's result.
func (m *Method) ResultType() string {
	return m.argResPrefix() + "Result"
}

// ArgList returns the argument list for the function.
func (m *Method) ArgList() string {
	args := []string{"ctx " + contextType()}
	for _, arg := range m.Arguments() {
		args = append(args, arg.Declaration())
	}
	return strings.Join(args, ", ")
}

// CallList creates the call to a function satisfying Interface from an Args struct.
func (m *Method) CallList(reqStruct string) string {
	args := []string{"ctx"}
	for _, arg := range m.Arguments() {
		args = append(args, reqStruct+"."+arg.ArgStructName())
	}
	return strings.Join(args, ", ")
}

// RetType returns the go return type of the method.
func (m *Method) RetType() string {
	if !m.HasReturn() {
		return "error"
	}
	return fmt.Sprintf("(%v, %v)", m.state.goType(m.Method.ReturnType), "error")
}

// WrapResult wraps the result variable before being used in the result struct.
func (m *Method) WrapResult(respVar string) string {
	if !m.HasReturn() {
		panic("cannot wrap a return when there is no return mode")
	}

	if m.state.isResultPointer(m.ReturnType) {
		return respVar
	}
	return "&" + respVar
}

// ReturnWith takes the result name and the error name, and generates the return expression.
func (m *Method) ReturnWith(respName string, errName string) string {
	if !m.HasReturn() {
		return errName
	}
	return fmt.Sprintf("%v, %v", respName, errName)
}

// Field is a wrapper for parser.Field.
type Field struct {
	*parser.Field

	state *State
}

// Declaration returns the declaration for this field.
func (a *Field) Declaration() string {
	return fmt.Sprintf("%s %s", a.Name(), a.ArgType())
}

// Name returns the field name.
func (a *Field) Name() string {
	return goName(a.Field.Name)
}

// ArgType returns the Go type for the given field.
func (a *Field) ArgType() string {
	return a.state.goType(a.Type)
}

// ArgStructName returns the name of this field in the Args struct generated by thrift.
func (a *Field) ArgStructName() string {
	return goPublicFieldName(a.Field.Name)
}
<|endoftext|>


<commit_before>package packet

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/packethost/packngo"
)

func resourcePacketDevice() *schema.Resource {
	return &schema.Resource{
		Create: resourcePacketDeviceCreate,
		Read:   resourcePacketDeviceRead,
		Update: resourcePacketDeviceUpdate,
		Delete: resourcePacketDeviceDelete,

		Schema: map[string]*schema.Schema{
			"project_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"hostname": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"operating_system": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"facility": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"plan": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"billing_cycle": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"state": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"locked": &schema.Schema{
				Type:     schema.TypeBool,
				Computed: true,
			},

			"network": &schema.Schema{
				Type:     schema.TypeList,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"address": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},

						"gateway": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},

						"family": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},

						"cidr": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},

						"public": &schema.Schema{
							Type:     schema.TypeBool,
							Computed: true,
						},
					},
				},
			},

			"created": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"updated": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"user_data": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"tags": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
		},
	}
}

func resourcePacketDeviceCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)

	createRequest := &packngo.DeviceCreateRequest{
		HostName:     d.Get("hostname").(string),
		Plan:         d.Get("plan").(string),
		Facility:     d.Get("facility").(string),
		OS:           d.Get("operating_system").(string),
		BillingCycle: d.Get("billing_cycle").(string),
		ProjectID:    d.Get("project_id").(string),
	}

	if attr, ok := d.GetOk("user_data"); ok {
		createRequest.UserData = attr.(string)
	}

	tags := d.Get("tags.#").(int)
	if tags > 0 {
		createRequest.Tags = make([]string, 0, tags)
		for i := 0; i < tags; i++ {
			key := fmt.Sprintf("tags.%d", i)
			createRequest.Tags = append(createRequest.Tags, d.Get(key).(string))
		}
	}

	log.Printf("[DEBUG] Device create configuration: %#v", createRequest)

	newDevice, _, err := client.Devices.Create(createRequest)
	if err != nil {
		return fmt.Errorf("Error creating device: %s", err)
	}

	// Assign the device id
	d.SetId(newDevice.ID)

	log.Printf("[INFO] Device ID: %s", d.Id())

	_, err = WaitForDeviceAttribute(d, "active", []string{"provisioning"}, "state", meta)
	if err != nil {
		return fmt.Errorf(
			"Error waiting for device (%s) to become ready: %s", d.Id(), err)
	}

	return resourcePacketDeviceRead(d, meta)
}

func resourcePacketDeviceRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)

	// Retrieve the device properties for updating the state
	device, _, err := client.Devices.Get(d.Id())
	if err != nil {
		return fmt.Errorf("Error retrieving device: %s", err)
	}

	d.Set("name", device.Hostname)
	d.Set("plan", device.Plan.Slug)
	d.Set("facility", device.Facility.Code)
	d.Set("operating_system", device.OS.Slug)
	d.Set("state", device.State)
	d.Set("billing_cycle", device.BillingCycle)
	d.Set("locked", device.Locked)
	d.Set("created", device.Created)
	d.Set("udpated", device.Updated)

	tags := make([]string, 0)
	for _, tag := range device.Tags {
		tags = append(tags, tag)
	}
	d.Set("tags", tags)

	networks := make([]map[string]interface{}, 0, 1)
	for _, ip := range device.Network {
		network := make(map[string]interface{})
		network["address"] = ip.Address
		network["gateway"] = ip.Gateway
		network["family"] = ip.Family
		network["cidr"] = ip.Cidr
		network["public"] = ip.Public
		networks = append(networks, network)
	}
	d.Set("network", networks)

	return nil
}

func resourcePacketDeviceUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)

	if d.HasChange("locked") && d.Get("locked").(bool) {
		_, err := client.Devices.Lock(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error locking device (%s): %s", d.Id(), err)
		}
	} else if d.HasChange("locked") {
		_, err := client.Devices.Unlock(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error unlocking device (%s): %s", d.Id(), err)
		}
	}

	return resourcePacketDeviceRead(d, meta)
}

func resourcePacketDeviceDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)

	log.Printf("[INFO] Deleting device: %s", d.Id())
	if _, err := client.Devices.Delete(d.Id()); err != nil {
		return fmt.Errorf("Error deleting device: %s", err)
	}

	return nil
}

func WaitForDeviceAttribute(
	d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}) (interface{}, error) {
	// Wait for the device so we can get the networking attributes
	// that show up after a while
	log.Printf(
		"[INFO] Waiting for device (%s) to have %s of %s",
		d.Id(), attribute, target)

	stateConf := &resource.StateChangeConf{
		Pending:    pending,
		Target:     target,
		Refresh:    newDeviceStateRefreshFunc(d, attribute, meta),
		Timeout:    60 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	return stateConf.WaitForState()
}

func newDeviceStateRefreshFunc(
	d *schema.ResourceData, attribute string, meta interface{}) resource.StateRefreshFunc {
	client := meta.(*packngo.Client)
	return func() (interface{}, string, error) {
		err := resourcePacketDeviceRead(d, meta)
		if err != nil {
			return nil, "", err
		}

		// See if we can access our attribute
		if attr, ok := d.GetOk(attribute); ok {
			// Retrieve the device properties
			device, _, err := client.Devices.Get(d.Id())
			if err != nil {
				return nil, "", fmt.Errorf("Error retrieving device: %s", err)
			}

			return &device, attr.(string), nil
		}

		return nil, "", nil
	}
}

// Powers on the device and waits for it to be active
func powerOnAndWait(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)
	_, err := client.Devices.PowerOn(d.Id())
	if err != nil {
		return err
	}

	// Wait for power on
	_, err = WaitForDeviceAttribute(d, "active", []string{"off"}, "state", client)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add queued as a pending state for Packet provider.<commit_after>package packet

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/packethost/packngo"
)

func resourcePacketDevice() *schema.Resource {
	return &schema.Resource{
		Create: resourcePacketDeviceCreate,
		Read:   resourcePacketDeviceRead,
		Update: resourcePacketDeviceUpdate,
		Delete: resourcePacketDeviceDelete,

		Schema: map[string]*schema.Schema{
			"project_id": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"hostname": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"operating_system": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"facility": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"plan": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"billing_cycle": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"state": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"locked": &schema.Schema{
				Type:     schema.TypeBool,
				Computed: true,
			},

			"network": &schema.Schema{
				Type:     schema.TypeList,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"address": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},

						"gateway": &schema.Schema{
							Type:     schema.TypeString,
							Computed: true,
						},

						"family": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},

						"cidr": &schema.Schema{
							Type:     schema.TypeInt,
							Computed: true,
						},

						"public": &schema.Schema{
							Type:     schema.TypeBool,
							Computed: true,
						},
					},
				},
			},

			"created": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"updated": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},

			"user_data": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"tags": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
		},
	}
}

func resourcePacketDeviceCreate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)

	createRequest := &packngo.DeviceCreateRequest{
		HostName:     d.Get("hostname").(string),
		Plan:         d.Get("plan").(string),
		Facility:     d.Get("facility").(string),
		OS:           d.Get("operating_system").(string),
		BillingCycle: d.Get("billing_cycle").(string),
		ProjectID:    d.Get("project_id").(string),
	}

	if attr, ok := d.GetOk("user_data"); ok {
		createRequest.UserData = attr.(string)
	}

	tags := d.Get("tags.#").(int)
	if tags > 0 {
		createRequest.Tags = make([]string, 0, tags)
		for i := 0; i < tags; i++ {
			key := fmt.Sprintf("tags.%d", i)
			createRequest.Tags = append(createRequest.Tags, d.Get(key).(string))
		}
	}

	log.Printf("[DEBUG] Device create configuration: %#v", createRequest)

	newDevice, _, err := client.Devices.Create(createRequest)
	if err != nil {
		return fmt.Errorf("Error creating device: %s", err)
	}

	// Assign the device id
	d.SetId(newDevice.ID)

	log.Printf("[INFO] Device ID: %s", d.Id())

	_, err = WaitForDeviceAttribute(d, "active", []string{"queued", "provisioning"}, "state", meta)
	if err != nil {
		return fmt.Errorf(
			"Error waiting for device (%s) to become ready: %s", d.Id(), err)
	}

	return resourcePacketDeviceRead(d, meta)
}

func resourcePacketDeviceRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)

	// Retrieve the device properties for updating the state
	device, _, err := client.Devices.Get(d.Id())
	if err != nil {
		return fmt.Errorf("Error retrieving device: %s", err)
	}

	d.Set("name", device.Hostname)
	d.Set("plan", device.Plan.Slug)
	d.Set("facility", device.Facility.Code)
	d.Set("operating_system", device.OS.Slug)
	d.Set("state", device.State)
	d.Set("billing_cycle", device.BillingCycle)
	d.Set("locked", device.Locked)
	d.Set("created", device.Created)
	d.Set("udpated", device.Updated)

	tags := make([]string, 0)
	for _, tag := range device.Tags {
		tags = append(tags, tag)
	}
	d.Set("tags", tags)

	networks := make([]map[string]interface{}, 0, 1)
	for _, ip := range device.Network {
		network := make(map[string]interface{})
		network["address"] = ip.Address
		network["gateway"] = ip.Gateway
		network["family"] = ip.Family
		network["cidr"] = ip.Cidr
		network["public"] = ip.Public
		networks = append(networks, network)
	}
	d.Set("network", networks)

	return nil
}

func resourcePacketDeviceUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)

	if d.HasChange("locked") && d.Get("locked").(bool) {
		_, err := client.Devices.Lock(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error locking device (%s): %s", d.Id(), err)
		}
	} else if d.HasChange("locked") {
		_, err := client.Devices.Unlock(d.Id())

		if err != nil {
			return fmt.Errorf(
				"Error unlocking device (%s): %s", d.Id(), err)
		}
	}

	return resourcePacketDeviceRead(d, meta)
}

func resourcePacketDeviceDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)

	log.Printf("[INFO] Deleting device: %s", d.Id())
	if _, err := client.Devices.Delete(d.Id()); err != nil {
		return fmt.Errorf("Error deleting device: %s", err)
	}

	return nil
}

func WaitForDeviceAttribute(
	d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}) (interface{}, error) {
	// Wait for the device so we can get the networking attributes
	// that show up after a while
	log.Printf(
		"[INFO] Waiting for device (%s) to have %s of %s",
		d.Id(), attribute, target)

	stateConf := &resource.StateChangeConf{
		Pending:    pending,
		Target:     target,
		Refresh:    newDeviceStateRefreshFunc(d, attribute, meta),
		Timeout:    60 * time.Minute,
		Delay:      10 * time.Second,
		MinTimeout: 3 * time.Second,
	}

	return stateConf.WaitForState()
}

func newDeviceStateRefreshFunc(
	d *schema.ResourceData, attribute string, meta interface{}) resource.StateRefreshFunc {
	client := meta.(*packngo.Client)
	return func() (interface{}, string, error) {
		err := resourcePacketDeviceRead(d, meta)
		if err != nil {
			return nil, "", err
		}

		// See if we can access our attribute
		if attr, ok := d.GetOk(attribute); ok {
			// Retrieve the device properties
			device, _, err := client.Devices.Get(d.Id())
			if err != nil {
				return nil, "", fmt.Errorf("Error retrieving device: %s", err)
			}

			return &device, attr.(string), nil
		}

		return nil, "", nil
	}
}

// Powers on the device and waits for it to be active
func powerOnAndWait(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*packngo.Client)
	_, err := client.Devices.PowerOn(d.Id())
	if err != nil {
		return err
	}

	// Wait for power on
	_, err = WaitForDeviceAttribute(d, "active", []string{"off"}, "state", client)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Program aedeploy assists with deploying Go Managed VM apps to production.
// A temporary directory is created; the app, its subdirectories, and all its
// dependencies from $GOPATH are copied into the directory; then the app
// is deployed to production with the provided command.
//
// The app must be in "package main".
//
// This command must be issued from within the root directory of the app
// (where the app.yaml file is located).
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	skipFiles = map[string]bool{
		".git":        true,
		".gitconfig":  true,
		".hg":         true,
		".travis.yml": true,
	}

	gopathCache = map[string]string{}
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\t%s gcloud --verbosity debug preview app deploy --version myversion ./app.yaml\tDeploy app to production\n", os.Args[0])
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		usage()
		os.Exit(1)
	}

	if err := aedeploy(); err != nil {
		fmt.Fprintf(os.Stderr, os.Args[0]+": Error: %v\n", err)
		os.Exit(1)
	}
}

func aedeploy() error {
	tags := []string{"appenginevm"}
	app, err := analyze(tags)
	if err != nil {
		return err
	}

	tmpDir, err := app.bundle()
	if tmpDir != "" {
		defer os.RemoveAll(tmpDir)
	}
	if err != nil {
		return err
	}

	if err := os.Chdir(tmpDir); err != nil {
		return fmt.Errorf("unable to chdir to %v: %v", tmpDir, err)
	}
	return deploy()
}

// deploy calls the provided command to deploy the app from the temporary directory.
func deploy() error {
	cmd := exec.Command(flag.Arg(0), flag.Args()[1:]...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("unable to run %q: %v", strings.Join(flag.Args(), " "), err)
	}
	return nil
}

type app struct {
	appFiles []string
	imports  map[string]string
}

// analyze checks the app for building with the given build tags and returns
// app files, and a map of full directory import names to original import names.
func analyze(tags []string) (*app, error) {
	ctxt := buildContext(tags)
	appFiles, err := appFiles(ctxt)
	if err != nil {
		return nil, err
	}
	gopath := filepath.SplitList(ctxt.GOPATH)
	im, err := imports(ctxt, ".", gopath)
	return &app{
		appFiles: appFiles,
		imports:  im,
	}, err
}

// buildContext returns the context for building the source.
func buildContext(tags []string) *build.Context {
	return &build.Context{
		GOARCH:    "amd64",
		GOOS:      "linux",
		GOROOT:    build.Default.GOROOT,
		GOPATH:    build.Default.GOPATH,
		Compiler:  build.Default.Compiler,
		BuildTags: append(build.Default.BuildTags, tags...),
	}
}

// bundle bundles the app into a temporary directory.
func (s *app) bundle() (tmpdir string, err error) {
	workDir, err := ioutil.TempDir("", "aedeploy")
	if err != nil {
		return "", fmt.Errorf("unable to create tmpdir: %v", err)
	}

	for srcDir, importName := range s.imports {
		dstDir := "_gopath/src/" + importName
		if err := copyTree(workDir, dstDir, srcDir); err != nil {
			return workDir, fmt.Errorf("unable to copy directory %v to %v: %v", srcDir, dstDir, err)
		}
	}
	if err := copyTree(workDir, ".", "."); err != nil {
		return workDir, fmt.Errorf("unable to copy root directory to /app: %v", err)
	}
	return workDir, nil
}

// imports returns a map of all import directories (recursively) used by the app.
// The return value maps full directory names to original import names.
func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) {
	pkg, err := ctxt.ImportDir(srcDir, 0)
	if err != nil {
		return nil, err
	}

	// Resolve all non-standard-library imports
	result := make(map[string]string)
	for _, v := range pkg.Imports {
		if !strings.Contains(v, ".") {
			continue
		}
		src, err := findInGopath(v, gopath)
		if err != nil {
			return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err)
		}
		if _, ok := result[src]; ok { // Already processed
			continue
		}
		result[src] = v
		im, err := imports(ctxt, src, gopath)
		if err != nil {
			return nil, fmt.Errorf("unable to parse package %v: %v", src, err)
		}
		for k, v := range im {
			result[k] = v
		}
	}
	return result, nil
}

// findInGopath searches the gopath for the named import directory.
func findInGopath(dir string, gopath []string) (string, error) {
	if v, ok := gopathCache[dir]; ok {
		return v, nil
	}
	for _, v := range gopath {
		dst := filepath.Join(v, "src", dir)
		if _, err := os.Stat(dst); err == nil {
			gopathCache[dir] = dst
			return dst, nil
		}
	}
	return "", fmt.Errorf("unable to find package %v in gopath %v", dir, gopath)
}

// copyTree copies srcDir to dstDir relative to dstRoot, ignoring skipFiles.
func copyTree(dstRoot, dstDir, srcDir string) error {
	d := filepath.Join(dstRoot, dstDir)
	if err := os.MkdirAll(d, 0755); err != nil {
		return fmt.Errorf("unable to create directory %v: %v", d, err)
	}

	entries, err := ioutil.ReadDir(srcDir)
	if err != nil {
		return fmt.Errorf("unable to read dir %v: %v", srcDir, err)
	}
	for _, entry := range entries {
		n := entry.Name()
		if skipFiles[n] {
			continue
		}
		s := filepath.Join(srcDir, n)
		if entry.Mode()&os.ModeSymlink == os.ModeSymlink {
			if entry, err = os.Stat(s); err != nil {
				return fmt.Errorf("unable to stat %v: %v", s, err)
			}
		}
		d := filepath.Join(dstDir, n)
		if entry.IsDir() {
			if err := copyTree(dstRoot, d, s); err != nil {
				return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
			}
			continue
		}
		if err := copyFile(dstRoot, d, s); err != nil {
			return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
		}
	}
	return nil
}

// copyFile copies src to dst relative to dstRoot.
func copyFile(dstRoot, dst, src string) error {
	s, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("unable to open %v: %v", src, err)
	}
	defer s.Close()

	dst = filepath.Join(dstRoot, dst)
	d, err := os.Create(dst)
	if err != nil {
		return fmt.Errorf("unable to create %v: %v", dst)
	}
	_, err = io.Copy(d, s)
	if err != nil {
		d.Close() // ignore error, copy already failed.
		return fmt.Errorf("unable to copy %v to %v: %v", src, dst, err)
	}
	if err := d.Close(); err != nil {
		return fmt.Errorf("unable to close %v: %v", dst, err)
	}
	return nil
}

// appFiles returns a list of all Go source files in the app.
func appFiles(ctxt *build.Context) ([]string, error) {
	pkg, err := ctxt.ImportDir(".", 0)
	if err != nil {
		return nil, err
	}
	if !pkg.IsCommand() {
		return nil, fmt.Errorf(`the root of your app needs to be package "main" (currently %q). Please see https://cloud.google.com/appengine/docs/go/managed-vms for more details on structuring your app.`, pkg.Name)
	}
	var appFiles []string
	for _, f := range pkg.GoFiles {
		n := filepath.Join(".", f)
		appFiles = append(appFiles, n)
	}
	return appFiles, nil
}
<commit_msg>aedeploy: fix minor error in fmt.Errorf call<commit_after>// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Program aedeploy assists with deploying Go Managed VM apps to production.
// A temporary directory is created; the app, its subdirectories, and all its
// dependencies from $GOPATH are copied into the directory; then the app
// is deployed to production with the provided command.
//
// The app must be in "package main".
//
// This command must be issued from within the root directory of the app
// (where the app.yaml file is located).
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	skipFiles = map[string]bool{
		".git":        true,
		".gitconfig":  true,
		".hg":         true,
		".travis.yml": true,
	}

	gopathCache = map[string]string{}
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\t%s gcloud --verbosity debug preview app deploy --version myversion ./app.yaml\tDeploy app to production\n", os.Args[0])
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		usage()
		os.Exit(1)
	}

	if err := aedeploy(); err != nil {
		fmt.Fprintf(os.Stderr, os.Args[0]+": Error: %v\n", err)
		os.Exit(1)
	}
}

func aedeploy() error {
	tags := []string{"appenginevm"}
	app, err := analyze(tags)
	if err != nil {
		return err
	}

	tmpDir, err := app.bundle()
	if tmpDir != "" {
		defer os.RemoveAll(tmpDir)
	}
	if err != nil {
		return err
	}

	if err := os.Chdir(tmpDir); err != nil {
		return fmt.Errorf("unable to chdir to %v: %v", tmpDir, err)
	}
	return deploy()
}

// deploy calls the provided command to deploy the app from the temporary directory.
func deploy() error {
	cmd := exec.Command(flag.Arg(0), flag.Args()[1:]...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("unable to run %q: %v", strings.Join(flag.Args(), " "), err)
	}
	return nil
}

type app struct {
	appFiles []string
	imports  map[string]string
}

// analyze checks the app for building with the given build tags and returns
// app files, and a map of full directory import names to original import names.
func analyze(tags []string) (*app, error) {
	ctxt := buildContext(tags)
	appFiles, err := appFiles(ctxt)
	if err != nil {
		return nil, err
	}
	gopath := filepath.SplitList(ctxt.GOPATH)
	im, err := imports(ctxt, ".", gopath)
	return &app{
		appFiles: appFiles,
		imports:  im,
	}, err
}

// buildContext returns the context for building the source.
func buildContext(tags []string) *build.Context {
	return &build.Context{
		GOARCH:    "amd64",
		GOOS:      "linux",
		GOROOT:    build.Default.GOROOT,
		GOPATH:    build.Default.GOPATH,
		Compiler:  build.Default.Compiler,
		BuildTags: append(build.Default.BuildTags, tags...),
	}
}

// bundle bundles the app into a temporary directory.
func (s *app) bundle() (tmpdir string, err error) {
	workDir, err := ioutil.TempDir("", "aedeploy")
	if err != nil {
		return "", fmt.Errorf("unable to create tmpdir: %v", err)
	}

	for srcDir, importName := range s.imports {
		dstDir := "_gopath/src/" + importName
		if err := copyTree(workDir, dstDir, srcDir); err != nil {
			return workDir, fmt.Errorf("unable to copy directory %v to %v: %v", srcDir, dstDir, err)
		}
	}
	if err := copyTree(workDir, ".", "."); err != nil {
		return workDir, fmt.Errorf("unable to copy root directory to /app: %v", err)
	}
	return workDir, nil
}

// imports returns a map of all import directories (recursively) used by the app.
// The return value maps full directory names to original import names.
func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) {
	pkg, err := ctxt.ImportDir(srcDir, 0)
	if err != nil {
		return nil, err
	}

	// Resolve all non-standard-library imports
	result := make(map[string]string)
	for _, v := range pkg.Imports {
		if !strings.Contains(v, ".") {
			continue
		}
		src, err := findInGopath(v, gopath)
		if err != nil {
			return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err)
		}
		if _, ok := result[src]; ok { // Already processed
			continue
		}
		result[src] = v
		im, err := imports(ctxt, src, gopath)
		if err != nil {
			return nil, fmt.Errorf("unable to parse package %v: %v", src, err)
		}
		for k, v := range im {
			result[k] = v
		}
	}
	return result, nil
}

// findInGopath searches the gopath for the named import directory.
func findInGopath(dir string, gopath []string) (string, error) {
	if v, ok := gopathCache[dir]; ok {
		return v, nil
	}
	for _, v := range gopath {
		dst := filepath.Join(v, "src", dir)
		if _, err := os.Stat(dst); err == nil {
			gopathCache[dir] = dst
			return dst, nil
		}
	}
	return "", fmt.Errorf("unable to find package %v in gopath %v", dir, gopath)
}

// copyTree copies srcDir to dstDir relative to dstRoot, ignoring skipFiles.
func copyTree(dstRoot, dstDir, srcDir string) error {
	d := filepath.Join(dstRoot, dstDir)
	if err := os.MkdirAll(d, 0755); err != nil {
		return fmt.Errorf("unable to create directory %q: %v", d, err)
	}

	entries, err := ioutil.ReadDir(srcDir)
	if err != nil {
		return fmt.Errorf("unable to read dir %q: %v", srcDir, err)
	}
	for _, entry := range entries {
		n := entry.Name()
		if skipFiles[n] {
			continue
		}
		s := filepath.Join(srcDir, n)
		if entry.Mode()&os.ModeSymlink == os.ModeSymlink {
			if entry, err = os.Stat(s); err != nil {
				return fmt.Errorf("unable to stat %v: %v", s, err)
			}
		}
		d := filepath.Join(dstDir, n)
		if entry.IsDir() {
			if err := copyTree(dstRoot, d, s); err != nil {
				return fmt.Errorf("unable to copy dir %q to %q: %v", s, d, err)
			}
			continue
		}
		if err := copyFile(dstRoot, d, s); err != nil {
			return fmt.Errorf("unable to copy dir %q to %q: %v", s, d, err)
		}
	}
	return nil
}

// copyFile copies src to dst relative to dstRoot.
func copyFile(dstRoot, dst, src string) error {
	s, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("unable to open %q: %v", src, err)
	}
	defer s.Close()

	dst = filepath.Join(dstRoot, dst)
	d, err := os.Create(dst)
	if err != nil {
		return fmt.Errorf("unable to create %q: %v", dst, err)
	}
	_, err = io.Copy(d, s)
	if err != nil {
		d.Close() // ignore error, copy already failed.
		return fmt.Errorf("unable to copy %q to %q: %v", src, dst, err)
	}
	if err := d.Close(); err != nil {
		return fmt.Errorf("unable to close %q: %v", dst, err)
	}
	return nil
}

// appFiles returns a list of all Go source files in the app.
func appFiles(ctxt *build.Context) ([]string, error) {
	pkg, err := ctxt.ImportDir(".", 0)
	if err != nil {
		return nil, err
	}
	if !pkg.IsCommand() {
		return nil, fmt.Errorf(`the root of your app needs to be package "main" (currently %q). Please see https://cloud.google.com/appengine/docs/go/managed-vms for more details on structuring your app.`, pkg.Name)
	}
	var appFiles []string
	for _, f := range pkg.GoFiles {
		n := filepath.Join(".", f)
		appFiles = append(appFiles, n)
	}
	return appFiles, nil
}
<|endoftext|>


<commit_before>//go:build !nointegration
// +build !nointegration

/*
Copyright 2022 The Kubernetes 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 (
	"context"
	"net/http"
	"os"
	"os/exec"
	"testing"
	"time"

	"sigs.k8s.io/oci-proxy/internal/integration"
)

// TestIntegrationMain tests the entire, built binary with an integration
// test, pulling images with crane
func TestIntegrationMain(t *testing.T) {
	// setup crane
	rootDir, err := integration.ModuleRootDir()
	if err != nil {
		t.Fatalf("Failed to detect module root dir: %v", err)
	}
	// NOTE: also ensures rootDir/bin is in front of $PATH
	if err := integration.EnsureCrane(rootDir); err != nil {
		t.Fatalf("Failed to ensure crane: %v", err)
	}

	// build binary
	buildCmd := exec.Command("make", "archeio")
	buildCmd.Dir = rootDir
	if err := buildCmd.Run(); err != nil {
		t.Fatalf("Failed to build archeio for integration testing: %v", err)
	}

	// start server in background
	testPort := "61337"
	testAddr := "localhost:" + testPort
	serverErrChan := make(chan error)
	cmdContext, serverCancel := context.WithCancel(context.TODO())
	serverCmd := exec.CommandContext(cmdContext, "archeio")
	serverCmd.Env = append(serverCmd.Env, "PORT="+testPort)
	// serverCmd.Stderr = os.Stderr
	defer serverCancel()
	go func() {
		serverErrChan <- serverCmd.Start()
		serverErrChan <- serverCmd.Wait()
	}()

	// wait for server to be up and running
	startErr := <-serverErrChan
	if startErr != nil {
		t.Fatalf("Failed to start archeio: %v", err)
	}
	if !tryUntil(time.Now().Add(time.Second), func() bool {
		_, err := http.Get("http://" + testAddr + "/v2/")
		return err == nil
	}) {
		t.Fatal("timed out waiting for archeio to be ready")
	}

	// TODO: fake being on AWS
	testPull := func(image string) {
		// nolint:gosec // this is not user suplied input ...
		cmd := exec.Command("crane", "pull", testAddr+"/"+image, os.DevNull)
		//cmd.Stderr = os.Stderr
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Errorf("pull for %q failed: %v", image, err)
			t.Error("output: ")
			t.Error(string(out))
			t.Fail()
		}
	}

	// test pulling pause image
	// TODO: test pulling more things
	testPull("pause:3.1")

	// we're done, cleanup
	if err := serverCmd.Process.Signal(os.Interrupt); err != nil {
		t.Fatalf("failed to signal archeio: %v", err)
	}
	if err := <-serverErrChan; err != nil {
		t.Fatalf("archeio did not exit cleanly: %v", err)
	}
}

// helper that calls `try()“ in a loop until the deadline `until`
// has passed or `try()`returns true, returns whether try ever returned true
func tryUntil(until time.Time, try func() bool) bool {
	for until.After(time.Now()) {
		if try() {
			return true
		}
		time.Sleep(time.Millisecond * 10)
	}
	return false
}
<commit_msg>fix gofmt change<commit_after>//go:build !nointegration
// +build !nointegration

/*
Copyright 2022 The Kubernetes 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 (
	"context"
	"net/http"
	"os"
	"os/exec"
	"testing"
	"time"

	"sigs.k8s.io/oci-proxy/internal/integration"
)

// TestIntegrationMain tests the entire, built binary with an integration
// test, pulling images with crane
func TestIntegrationMain(t *testing.T) {
	// setup crane
	rootDir, err := integration.ModuleRootDir()
	if err != nil {
		t.Fatalf("Failed to detect module root dir: %v", err)
	}
	// NOTE: also ensures rootDir/bin is in front of $PATH
	if err := integration.EnsureCrane(rootDir); err != nil {
		t.Fatalf("Failed to ensure crane: %v", err)
	}

	// build binary
	buildCmd := exec.Command("make", "archeio")
	buildCmd.Dir = rootDir
	if err := buildCmd.Run(); err != nil {
		t.Fatalf("Failed to build archeio for integration testing: %v", err)
	}

	// start server in background
	testPort := "61337"
	testAddr := "localhost:" + testPort
	serverErrChan := make(chan error)
	cmdContext, serverCancel := context.WithCancel(context.TODO())
	serverCmd := exec.CommandContext(cmdContext, "archeio")
	serverCmd.Env = append(serverCmd.Env, "PORT="+testPort)
	// serverCmd.Stderr = os.Stderr
	defer serverCancel()
	go func() {
		serverErrChan <- serverCmd.Start()
		serverErrChan <- serverCmd.Wait()
	}()

	// wait for server to be up and running
	startErr := <-serverErrChan
	if startErr != nil {
		t.Fatalf("Failed to start archeio: %v", err)
	}
	if !tryUntil(time.Now().Add(time.Second), func() bool {
		_, err := http.Get("http://" + testAddr + "/v2/")
		return err == nil
	}) {
		t.Fatal("timed out waiting for archeio to be ready")
	}

	// TODO: fake being on AWS
	testPull := func(image string) {
		// nolint:gosec // this is not user suplied input ...
		cmd := exec.Command("crane", "pull", testAddr+"/"+image, os.DevNull)
		//cmd.Stderr = os.Stderr
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Errorf("pull for %q failed: %v", image, err)
			t.Error("output: ")
			t.Error(string(out))
			t.Fail()
		}
	}

	// test pulling pause image
	// TODO: test pulling more things
	testPull("pause:3.1")

	// we're done, cleanup
	if err := serverCmd.Process.Signal(os.Interrupt); err != nil {
		t.Fatalf("failed to signal archeio: %v", err)
	}
	if err := <-serverErrChan; err != nil {
		t.Fatalf("archeio did not exit cleanly: %v", err)
	}
}

// helper that calls `try()` in a loop until the deadline `until`
// has passed or `try()`returns true, returns whether try ever returned true
func tryUntil(until time.Time, try func() bool) bool {
	for until.After(time.Now()) {
		if try() {
			return true
		}
		time.Sleep(time.Millisecond * 10)
	}
	return false
}
<|endoftext|>


<commit_before>package app

import (
	"net"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/urfave/cli"
)

// ProviderFunc is the function type that can be passed to Action()
type ProviderFunc func(*Context) error

// Action is a convenience function for making cli.Command.Actions that sets up a Context, runs all the providers, cleans up afterward and returns errors from the actions if there is one
func Action(providers ...ProviderFunc) func(c *cli.Context) error {
	providers = append(providers, providers[len(providers)-1])
	providers[len(providers)-2] = (*Context).Preprocess
	return func(cliContext *cli.Context) error {
		c := Context{Context: CliContextWrapper{cliContext}}
		defer cleanup(&c)

		err := foldProviders(&c, providers...)
		return err
	}
}

// Preprocess runs the Preprocess methods on all flags that implement Preprocessor
func (c *Context) Preprocess() error {
	if c.preprocessHasRun {
		return nil
	}
	c.Debug("Preprocessing")
	for _, flag := range c.Command().Flags {
		if gf, ok := flag.(cli.GenericFlag); ok {
			if pp, ok := gf.Value.(Preprocesser); ok {
				c.Debug("--%s b4: %#v", gf.Name, gf.Value)
				err := pp.Preprocess(c)
				if err != nil {
					return err
				}
				c.Debug("after: %#v\n", gf.Value)
			}
		}
	}
	c.preprocessHasRun = true
	return nil
}

// cleanup resets the value of special flags between invocations of global.App.Run so that the tests pass.
// This is needed because the init() functions are only executed once during the testing cycle.
// Outside of the tests, global.App.Run is only called once before the program closes.
func cleanup(c *Context) {
	ips, ok := c.Context.Generic("ip").(*util.IPFlag)
	if ok {
		*ips = make([]net.IP, 0)
	}
	disc, ok := c.Context.Generic("disc").(*util.DiscSpecFlag)
	if ok {
		*disc = make([]brain.Disc, 0)
	}
	size, ok := c.Context.Generic("memory").(*util.SizeSpecFlag)
	if ok {
		*size = 0
	}
	server, ok := c.Context.Generic("server").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("from").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("to").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	group, ok := c.Context.Generic("group").(*GroupNameFlag)
	if ok {
		*group = GroupNameFlag{}
	}

	account, ok := c.Context.Generic("account").(*AccountNameFlag)
	if ok {
		*account = AccountNameFlag{}
	}
}

// foldProviders runs all the providers with the given context, stopping if there's an error
func foldProviders(c *Context, providers ...ProviderFunc) (err error) {
	for i, provider := range providers {
		c.Debug("Running provider #%d (%v)\n", i, provider)
		err = provider(c)
		if err != nil {
			return
		}
	}
	return
}
<commit_msg>add --new-name to the list of flags that get cleaned up between tests<commit_after>package app

import (
	"net"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/util"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	"github.com/urfave/cli"
)

// ProviderFunc is the function type that can be passed to Action()
type ProviderFunc func(*Context) error

// Action is a convenience function for making cli.Command.Actions that sets up a Context, runs all the providers, cleans up afterward and returns errors from the actions if there is one
func Action(providers ...ProviderFunc) func(c *cli.Context) error {
	providers = append(providers, providers[len(providers)-1])
	providers[len(providers)-2] = (*Context).Preprocess
	return func(cliContext *cli.Context) error {
		c := Context{Context: CliContextWrapper{cliContext}}
		defer cleanup(&c)

		err := foldProviders(&c, providers...)
		return err
	}
}

// Preprocess runs the Preprocess methods on all flags that implement Preprocessor
func (c *Context) Preprocess() error {
	if c.preprocessHasRun {
		return nil
	}
	c.Debug("Preprocessing")
	for _, flag := range c.Command().Flags {
		if gf, ok := flag.(cli.GenericFlag); ok {
			if pp, ok := gf.Value.(Preprocesser); ok {
				c.Debug("--%s b4: %#v", gf.Name, gf.Value)
				err := pp.Preprocess(c)
				if err != nil {
					return err
				}
				c.Debug("after: %#v\n", gf.Value)
			}
		}
	}
	c.preprocessHasRun = true
	return nil
}

// cleanup resets the value of special flags between invocations of global.App.Run so that the tests pass.
// This is needed because the init() functions are only executed once during the testing cycle.
// Outside of the tests, global.App.Run is only called once before the program closes.
func cleanup(c *Context) {
	ips, ok := c.Context.Generic("ip").(*util.IPFlag)
	if ok {
		*ips = make([]net.IP, 0)
	}
	disc, ok := c.Context.Generic("disc").(*util.DiscSpecFlag)
	if ok {
		*disc = make([]brain.Disc, 0)
	}
	size, ok := c.Context.Generic("memory").(*util.SizeSpecFlag)
	if ok {
		*size = 0
	}
	server, ok := c.Context.Generic("server").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("new-name").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("from").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	server, ok = c.Context.Generic("to").(*VirtualMachineNameFlag)
	if ok {
		*server = VirtualMachineNameFlag{}
	}
	group, ok := c.Context.Generic("group").(*GroupNameFlag)
	if ok {
		*group = GroupNameFlag{}
	}

	account, ok := c.Context.Generic("account").(*AccountNameFlag)
	if ok {
		*account = AccountNameFlag{}
	}
}

// foldProviders runs all the providers with the given context, stopping if there's an error
func foldProviders(c *Context, providers ...ProviderFunc) (err error) {
	for i, provider := range providers {
		c.Debug("Running provider #%d (%v)\n", i, provider)
		err = provider(c)
		if err != nil {
			return
		}
	}
	return
}
<|endoftext|>