<commit_before>package structs

import (
	"fmt"
	"strings"

	"github.com/hashicorp/consul/acl"
	"github.com/hashicorp/consul/lib"
	"github.com/miekg/dns"
)

// IngressGatewayConfigEntry manages the configuration for an ingress service
// with the given name.
type IngressGatewayConfigEntry struct {
	// Kind of the config entry. This will be set to structs.IngressGateway.
	Kind string

	// Name is used to match the config entry with its associated ingress gateway
	// service. This should match the name provided in the service definition.
	Name string

	// TLS holds the TLS configuration for this gateway.
	TLS GatewayTLSConfig

	// Listeners declares what ports the ingress gateway should listen on, and
	// what services to associated to those ports.
	Listeners []IngressListener

	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
	RaftIndex
}

type IngressListener struct {
	// Port declares the port on which the ingress gateway should listen for traffic.
	Port int

	// Protocol declares what type of traffic this listener is expected to
	// receive. Depending on the protocol, a listener might support multiplexing
	// services over a single port, or additional discovery chain features. The
	// current supported values are: (tcp | http).
	Protocol string

	// Services declares the set of services to which the listener forwards
	// traffic.
	//
	// For "tcp" protocol listeners, only a single service is allowed.
	// For "http" listeners, multiple services can be declared.
	Services []IngressService
}

type IngressService struct {
	// Name declares the service to which traffic should be forwarded.
	//
	// This can either be a specific service, or the wildcard specifier,
	// "*". If the wildcard specifier is provided, the listener must be of "http"
	// protocol and means that the listener will forward traffic to all services.
	//
	// A name can be specified on multiple listeners, and will be exposed on both
	// of the listeners
	Name string

	// Hosts is a list of hostnames which should be associated to this service on
	// the defined listener. Only allowed on layer 7 protocols, this will be used
	// to route traffic to the service by matching the Host header of the HTTP
	// request.
	//
	// If a host is provided for a service that also has a wildcard specifier
	// defined, the host will override the wildcard-specifier-provided
	// "<service-name>.*" domain for that listener.
	//
	// This cannot be specified when using the wildcard specifier, "*", or when
	// using a "tcp" listener.
	Hosts []string

	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
}

type GatewayTLSConfig struct {
	// Indicates that TLS should be enabled for this gateway service
	Enabled bool
}

func (e *IngressGatewayConfigEntry) GetKind() string {
	return IngressGateway
}

func (e *IngressGatewayConfigEntry) GetName() string {
	if e == nil {
		return ""
	}

	return e.Name
}

func (e *IngressGatewayConfigEntry) Normalize() error {
	if e == nil {
		return fmt.Errorf("config entry is nil")
	}

	e.Kind = IngressGateway
	for i, listener := range e.Listeners {
		if listener.Protocol == "" {
			listener.Protocol = "tcp"
		}

		listener.Protocol = strings.ToLower(listener.Protocol)
		for i := range listener.Services {
			listener.Services[i].EnterpriseMeta.Normalize()
		}

		// Make sure to set the item back into the array, since we are not using
		// pointers to structs
		e.Listeners[i] = listener
	}

	e.EnterpriseMeta.Normalize()

	return nil
}

func (e *IngressGatewayConfigEntry) Validate() error {
	validProtocols := map[string]bool{
		"http": true,
		"tcp":  true,
	}
	declaredPorts := make(map[int]bool)

	for _, listener := range e.Listeners {
		if _, ok := declaredPorts[listener.Port]; ok {
			return fmt.Errorf("port %d declared on two listeners", listener.Port)
		}
		declaredPorts[listener.Port] = true

		if _, ok := validProtocols[listener.Protocol]; !ok {
			return fmt.Errorf("Protocol must be either 'http' or 'tcp', '%s' is an unsupported protocol.", listener.Protocol)
		}

		if len(listener.Services) == 0 {
			return fmt.Errorf("No service declared for listener with port %d", listener.Port)
		}

		// Validate that http features aren't being used with tcp or another non-supported protocol.
		if listener.Protocol != "http" && len(listener.Services) > 1 {
			return fmt.Errorf("Multiple services per listener are only supported for protocol = 'http' (listener on port %d)",
				listener.Port)
		}

		declaredHosts := make(map[string]bool)
		for _, s := range listener.Services {
			if listener.Protocol == "tcp" {
				if s.Name == WildcardSpecifier {
					return fmt.Errorf("Wildcard service name is only valid for protocol = 'http' (listener on port %d)", listener.Port)
				}
				if len(s.Hosts) != 0 {
					return fmt.Errorf("Associating hosts to a service is not supported for the %s protocol (listener on port %d)", listener.Protocol, listener.Port)
				}
			}
			if s.Name == "" {
				return fmt.Errorf("Service name cannot be blank (listener on port %d)", listener.Port)
			}
			if s.Name == WildcardSpecifier && len(s.Hosts) != 0 {
				return fmt.Errorf("Associating hosts to a wildcard service is not supported (listener on port %d)", listener.Port)
			}
			if s.NamespaceOrDefault() == WildcardSpecifier {
				return fmt.Errorf("Wildcard namespace is not supported for ingress services (listener on port %d)", listener.Port)
			}

			for _, h := range s.Hosts {
				if declaredHosts[h] {
					return fmt.Errorf("Hosts must be unique within a specific listener (listener on port %d)", listener.Port)
				}
				declaredHosts[h] = true
				if err := validateHost(h); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func validateHost(host string) error {
	wildcardPrefix := "*."
	if _, ok := dns.IsDomainName(host); !ok {
		return fmt.Errorf("Host %q must be a valid DNS hostname", host)
	}

	if strings.ContainsRune(strings.TrimPrefix(host, wildcardPrefix), '*') {
		return fmt.Errorf("Host %q is not valid, a wildcard specifier is only allowed as the leftmost label", host)
	}

	return nil
}

func (e *IngressGatewayConfigEntry) CanRead(authz acl.Authorizer) bool {
	var authzContext acl.AuthorizerContext
	e.FillAuthzContext(&authzContext)
	return authz.ServiceRead(e.Name, &authzContext) == acl.Allow
}

func (e *IngressGatewayConfigEntry) CanWrite(authz acl.Authorizer) bool {
	var authzContext acl.AuthorizerContext
	e.FillAuthzContext(&authzContext)
	return authz.OperatorWrite(&authzContext) == acl.Allow
}

func (e *IngressGatewayConfigEntry) GetRaftIndex() *RaftIndex {
	if e == nil {
		return &RaftIndex{}
	}

	return &e.RaftIndex
}

func (e *IngressGatewayConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
	if e == nil {
		return nil
	}

	return &e.EnterpriseMeta
}

func (s *IngressService) ToServiceID() ServiceID {
	return NewServiceID(s.Name, &s.EnterpriseMeta)
}

// TerminatingGatewayConfigEntry manages the configuration for a terminating service
// with the given name.
type TerminatingGatewayConfigEntry struct {
	Kind     string
	Name     string
	Services []LinkedService

	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
	RaftIndex
}

// A LinkedService is a service represented by a terminating gateway
type LinkedService struct {
	// Name is the name of the service, as defined in Consul's catalog
	Name string `json:",omitempty"`

	// CAFile is the optional path to a CA certificate to use for TLS connections
	// from the gateway to the linked service
	CAFile string `json:",omitempty"`

	// CertFile is the optional path to a client certificate to use for TLS connections
	// from the gateway to the linked service
	CertFile string `json:",omitempty"`

	// KeyFile is the optional path to a private key to use for TLS connections
	// from the gateway to the linked service
	KeyFile string `json:",omitempty"`

	// SNI is the optional name to specify during the TLS handshake with a linked service
	SNI string `json:",omitempty"`

	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
}

func (e *TerminatingGatewayConfigEntry) GetKind() string {
	return TerminatingGateway
}

func (e *TerminatingGatewayConfigEntry) GetName() string {
	if e == nil {
		return ""
	}

	return e.Name
}

func (e *TerminatingGatewayConfigEntry) Normalize() error {
	if e == nil {
		return fmt.Errorf("config entry is nil")
	}

	e.Kind = TerminatingGateway

	for i := range e.Services {
		e.Services[i].EnterpriseMeta.Normalize()
	}
	e.EnterpriseMeta.Normalize()

	return nil
}

func (e *TerminatingGatewayConfigEntry) Validate() error {
	seen := make(map[ServiceID]bool)

	for _, svc := range e.Services {
		if svc.Name == "" {
			return fmt.Errorf("Service name cannot be blank.")
		}

		ns := svc.NamespaceOrDefault()
		if ns == WildcardSpecifier {
			return fmt.Errorf("Wildcard namespace is not supported for terminating gateway services")
		}

		// Check for duplicates within the entry
		cid := NewServiceID(svc.Name, &svc.EnterpriseMeta)
		if ok := seen[cid]; ok {
			return fmt.Errorf("Service %q was specified more than once within a namespace", cid.String())
		}
		seen[cid] = true

		// If either client cert config file was specified then the CA file, client cert, and key file must be specified
		// Specifying only a CAFile is allowed for one-way TLS
		if (svc.CertFile != "" || svc.KeyFile != "") &&
			!(svc.CAFile != "" && svc.CertFile != "" && svc.KeyFile != "") {

			return fmt.Errorf("Service %q must have a CertFile, CAFile, and KeyFile specified for TLS origination", svc.Name)
		}
	}
	return nil
}

func (e *TerminatingGatewayConfigEntry) CanRead(authz acl.Authorizer) bool {
	var authzContext acl.AuthorizerContext
	e.FillAuthzContext(&authzContext)

	return authz.ServiceRead(e.Name, &authzContext) == acl.Allow
}

func (e *TerminatingGatewayConfigEntry) CanWrite(authz acl.Authorizer) bool {
	var authzContext acl.AuthorizerContext
	e.FillAuthzContext(&authzContext)

	return authz.OperatorWrite(&authzContext) == acl.Allow
}

func (e *TerminatingGatewayConfigEntry) GetRaftIndex() *RaftIndex {
	if e == nil {
		return &RaftIndex{}
	}

	return &e.RaftIndex
}

func (e *TerminatingGatewayConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
	if e == nil {
		return nil
	}

	return &e.EnterpriseMeta
}

// GatewayService is used to associate gateways with their linked services.
type GatewayService struct {
	Gateway      ServiceID
	Service      ServiceID
	GatewayKind  ServiceKind
	Port         int
	Protocol     string
	Hosts        []string
	CAFile       string
	CertFile     string
	KeyFile      string
	SNI          string
	FromWildcard bool
	RaftIndex
}

type GatewayServices []*GatewayService

func (g *GatewayService) IsSame(o *GatewayService) bool {
	return g.Gateway.Matches(&o.Gateway) &&
		g.Service.Matches(&o.Service) &&
		g.GatewayKind == o.GatewayKind &&
		g.Port == o.Port &&
		g.Protocol == o.Protocol &&
		lib.StringSliceEqual(g.Hosts, o.Hosts) &&
		g.CAFile == o.CAFile &&
		g.CertFile == o.CertFile &&
		g.KeyFile == o.KeyFile &&
		g.SNI == o.SNI &&
		g.FromWildcard == o.FromWildcard
}

func (g *GatewayService) Clone() *GatewayService {
	return &GatewayService{
		Gateway:     g.Gateway,
		Service:     g.Service,
		GatewayKind: g.GatewayKind,
		Port:        g.Port,
		Protocol:    g.Protocol,
		// See https://github.com/go101/go101/wiki/How-to-efficiently-clone-a-slice%3F
		Hosts:        append(g.Hosts[:0:0], g.Hosts...),
		CAFile:       g.CAFile,
		CertFile:     g.CertFile,
		KeyFile:      g.KeyFile,
		SNI:          g.SNI,
		FromWildcard: g.FromWildcard,
		RaftIndex:    g.RaftIndex,
	}
}
<commit_msg>Disallow the blanket wildcard prefix from being used as custom host<commit_after>package structs

import (
	"fmt"
	"strings"

	"github.com/hashicorp/consul/acl"
	"github.com/hashicorp/consul/lib"
	"github.com/miekg/dns"
)

// IngressGatewayConfigEntry manages the configuration for an ingress service
// with the given name.
type IngressGatewayConfigEntry struct {
	// Kind of the config entry. This will be set to structs.IngressGateway.
	Kind string

	// Name is used to match the config entry with its associated ingress gateway
	// service. This should match the name provided in the service definition.
	Name string

	// TLS holds the TLS configuration for this gateway.
	TLS GatewayTLSConfig

	// Listeners declares what ports the ingress gateway should listen on, and
	// what services to associated to those ports.
	Listeners []IngressListener

	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
	RaftIndex
}

type IngressListener struct {
	// Port declares the port on which the ingress gateway should listen for traffic.
	Port int

	// Protocol declares what type of traffic this listener is expected to
	// receive. Depending on the protocol, a listener might support multiplexing
	// services over a single port, or additional discovery chain features. The
	// current supported values are: (tcp | http).
	Protocol string

	// Services declares the set of services to which the listener forwards
	// traffic.
	//
	// For "tcp" protocol listeners, only a single service is allowed.
	// For "http" listeners, multiple services can be declared.
	Services []IngressService
}

type IngressService struct {
	// Name declares the service to which traffic should be forwarded.
	//
	// This can either be a specific service, or the wildcard specifier,
	// "*". If the wildcard specifier is provided, the listener must be of "http"
	// protocol and means that the listener will forward traffic to all services.
	//
	// A name can be specified on multiple listeners, and will be exposed on both
	// of the listeners
	Name string

	// Hosts is a list of hostnames which should be associated to this service on
	// the defined listener. Only allowed on layer 7 protocols, this will be used
	// to route traffic to the service by matching the Host header of the HTTP
	// request.
	//
	// If a host is provided for a service that also has a wildcard specifier
	// defined, the host will override the wildcard-specifier-provided
	// "<service-name>.*" domain for that listener.
	//
	// This cannot be specified when using the wildcard specifier, "*", or when
	// using a "tcp" listener.
	Hosts []string

	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
}

type GatewayTLSConfig struct {
	// Indicates that TLS should be enabled for this gateway service
	Enabled bool
}

func (e *IngressGatewayConfigEntry) GetKind() string {
	return IngressGateway
}

func (e *IngressGatewayConfigEntry) GetName() string {
	if e == nil {
		return ""
	}

	return e.Name
}

func (e *IngressGatewayConfigEntry) Normalize() error {
	if e == nil {
		return fmt.Errorf("config entry is nil")
	}

	e.Kind = IngressGateway
	for i, listener := range e.Listeners {
		if listener.Protocol == "" {
			listener.Protocol = "tcp"
		}

		listener.Protocol = strings.ToLower(listener.Protocol)
		for i := range listener.Services {
			listener.Services[i].EnterpriseMeta.Normalize()
		}

		// Make sure to set the item back into the array, since we are not using
		// pointers to structs
		e.Listeners[i] = listener
	}

	e.EnterpriseMeta.Normalize()

	return nil
}

func (e *IngressGatewayConfigEntry) Validate() error {
	validProtocols := map[string]bool{
		"http": true,
		"tcp":  true,
	}
	declaredPorts := make(map[int]bool)

	for _, listener := range e.Listeners {
		if _, ok := declaredPorts[listener.Port]; ok {
			return fmt.Errorf("port %d declared on two listeners", listener.Port)
		}
		declaredPorts[listener.Port] = true

		if _, ok := validProtocols[listener.Protocol]; !ok {
			return fmt.Errorf("Protocol must be either 'http' or 'tcp', '%s' is an unsupported protocol.", listener.Protocol)
		}

		if len(listener.Services) == 0 {
			return fmt.Errorf("No service declared for listener with port %d", listener.Port)
		}

		// Validate that http features aren't being used with tcp or another non-supported protocol.
		if listener.Protocol != "http" && len(listener.Services) > 1 {
			return fmt.Errorf("Multiple services per listener are only supported for protocol = 'http' (listener on port %d)",
				listener.Port)
		}

		declaredHosts := make(map[string]bool)
		for _, s := range listener.Services {
			if listener.Protocol == "tcp" {
				if s.Name == WildcardSpecifier {
					return fmt.Errorf("Wildcard service name is only valid for protocol = 'http' (listener on port %d)", listener.Port)
				}
				if len(s.Hosts) != 0 {
					return fmt.Errorf("Associating hosts to a service is not supported for the %s protocol (listener on port %d)", listener.Protocol, listener.Port)
				}
			}
			if s.Name == "" {
				return fmt.Errorf("Service name cannot be blank (listener on port %d)", listener.Port)
			}
			if s.Name == WildcardSpecifier && len(s.Hosts) != 0 {
				return fmt.Errorf("Associating hosts to a wildcard service is not supported (listener on port %d)", listener.Port)
			}
			if s.NamespaceOrDefault() == WildcardSpecifier {
				return fmt.Errorf("Wildcard namespace is not supported for ingress services (listener on port %d)", listener.Port)
			}

			for _, h := range s.Hosts {
				if declaredHosts[h] {
					return fmt.Errorf("Hosts must be unique within a specific listener (listener on port %d)", listener.Port)
				}
				declaredHosts[h] = true
				if err := validateHost(h); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func validateHost(host string) error {
	wildcardPrefix := "*."
	if _, ok := dns.IsDomainName(host); !ok {
		return fmt.Errorf("Host %q must be a valid DNS hostname", host)
	}

	if strings.ContainsRune(strings.TrimPrefix(host, wildcardPrefix), '*') {
		return fmt.Errorf("Host %q is not valid, a wildcard specifier is only allowed as the leftmost label", host)
	}

	if host == "*" {
		return fmt.Errorf("Host '*' is not allowed, wildcards can only be used as a prefix/suffix")
	}

	return nil
}

func (e *IngressGatewayConfigEntry) CanRead(authz acl.Authorizer) bool {
	var authzContext acl.AuthorizerContext
	e.FillAuthzContext(&authzContext)
	return authz.ServiceRead(e.Name, &authzContext) == acl.Allow
}

func (e *IngressGatewayConfigEntry) CanWrite(authz acl.Authorizer) bool {
	var authzContext acl.AuthorizerContext
	e.FillAuthzContext(&authzContext)
	return authz.OperatorWrite(&authzContext) == acl.Allow
}

func (e *IngressGatewayConfigEntry) GetRaftIndex() *RaftIndex {
	if e == nil {
		return &RaftIndex{}
	}

	return &e.RaftIndex
}

func (e *IngressGatewayConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
	if e == nil {
		return nil
	}

	return &e.EnterpriseMeta
}

func (s *IngressService) ToServiceID() ServiceID {
	return NewServiceID(s.Name, &s.EnterpriseMeta)
}

// TerminatingGatewayConfigEntry manages the configuration for a terminating service
// with the given name.
type TerminatingGatewayConfigEntry struct {
	Kind     string
	Name     string
	Services []LinkedService

	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
	RaftIndex
}

// A LinkedService is a service represented by a terminating gateway
type LinkedService struct {
	// Name is the name of the service, as defined in Consul's catalog
	Name string `json:",omitempty"`

	// CAFile is the optional path to a CA certificate to use for TLS connections
	// from the gateway to the linked service
	CAFile string `json:",omitempty"`

	// CertFile is the optional path to a client certificate to use for TLS connections
	// from the gateway to the linked service
	CertFile string `json:",omitempty"`

	// KeyFile is the optional path to a private key to use for TLS connections
	// from the gateway to the linked service
	KeyFile string `json:",omitempty"`

	// SNI is the optional name to specify during the TLS handshake with a linked service
	SNI string `json:",omitempty"`

	EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
}

func (e *TerminatingGatewayConfigEntry) GetKind() string {
	return TerminatingGateway
}

func (e *TerminatingGatewayConfigEntry) GetName() string {
	if e == nil {
		return ""
	}

	return e.Name
}

func (e *TerminatingGatewayConfigEntry) Normalize() error {
	if e == nil {
		return fmt.Errorf("config entry is nil")
	}

	e.Kind = TerminatingGateway

	for i := range e.Services {
		e.Services[i].EnterpriseMeta.Normalize()
	}
	e.EnterpriseMeta.Normalize()

	return nil
}

func (e *TerminatingGatewayConfigEntry) Validate() error {
	seen := make(map[ServiceID]bool)

	for _, svc := range e.Services {
		if svc.Name == "" {
			return fmt.Errorf("Service name cannot be blank.")
		}

		ns := svc.NamespaceOrDefault()
		if ns == WildcardSpecifier {
			return fmt.Errorf("Wildcard namespace is not supported for terminating gateway services")
		}

		// Check for duplicates within the entry
		cid := NewServiceID(svc.Name, &svc.EnterpriseMeta)
		if ok := seen[cid]; ok {
			return fmt.Errorf("Service %q was specified more than once within a namespace", cid.String())
		}
		seen[cid] = true

		// If either client cert config file was specified then the CA file, client cert, and key file must be specified
		// Specifying only a CAFile is allowed for one-way TLS
		if (svc.CertFile != "" || svc.KeyFile != "") &&
			!(svc.CAFile != "" && svc.CertFile != "" && svc.KeyFile != "") {

			return fmt.Errorf("Service %q must have a CertFile, CAFile, and KeyFile specified for TLS origination", svc.Name)
		}
	}
	return nil
}

func (e *TerminatingGatewayConfigEntry) CanRead(authz acl.Authorizer) bool {
	var authzContext acl.AuthorizerContext
	e.FillAuthzContext(&authzContext)

	return authz.ServiceRead(e.Name, &authzContext) == acl.Allow
}

func (e *TerminatingGatewayConfigEntry) CanWrite(authz acl.Authorizer) bool {
	var authzContext acl.AuthorizerContext
	e.FillAuthzContext(&authzContext)

	return authz.OperatorWrite(&authzContext) == acl.Allow
}

func (e *TerminatingGatewayConfigEntry) GetRaftIndex() *RaftIndex {
	if e == nil {
		return &RaftIndex{}
	}

	return &e.RaftIndex
}

func (e *TerminatingGatewayConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
	if e == nil {
		return nil
	}

	return &e.EnterpriseMeta
}

// GatewayService is used to associate gateways with their linked services.
type GatewayService struct {
	Gateway      ServiceID
	Service      ServiceID
	GatewayKind  ServiceKind
	Port         int
	Protocol     string
	Hosts        []string
	CAFile       string
	CertFile     string
	KeyFile      string
	SNI          string
	FromWildcard bool
	RaftIndex
}

type GatewayServices []*GatewayService

func (g *GatewayService) IsSame(o *GatewayService) bool {
	return g.Gateway.Matches(&o.Gateway) &&
		g.Service.Matches(&o.Service) &&
		g.GatewayKind == o.GatewayKind &&
		g.Port == o.Port &&
		g.Protocol == o.Protocol &&
		lib.StringSliceEqual(g.Hosts, o.Hosts) &&
		g.CAFile == o.CAFile &&
		g.CertFile == o.CertFile &&
		g.KeyFile == o.KeyFile &&
		g.SNI == o.SNI &&
		g.FromWildcard == o.FromWildcard
}

func (g *GatewayService) Clone() *GatewayService {
	return &GatewayService{
		Gateway:     g.Gateway,
		Service:     g.Service,
		GatewayKind: g.GatewayKind,
		Port:        g.Port,
		Protocol:    g.Protocol,
		// See https://github.com/go101/go101/wiki/How-to-efficiently-clone-a-slice%3F
		Hosts:        append(g.Hosts[:0:0], g.Hosts...),
		CAFile:       g.CAFile,
		CertFile:     g.CertFile,
		KeyFile:      g.KeyFile,
		SNI:          g.SNI,
		FromWildcard: g.FromWildcard,
		RaftIndex:    g.RaftIndex,
	}
}
<|endoftext|>


<commit_before>package jval

import (
	"bytes"
	"encoding/json"
	"github.com/thwd/jsem"
	"math/rand"
	"regexp"
	"strconv"
	"time"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type Error struct {
	Label   string
	Field   string
	Context interface{}
}

// Error implements native "error" interface
func (e Error) Error() string {
	return e.Label
}

var noErrors = []Error{}

type Validator interface {
	Validate(value *jsem.Value, field string) []Error
	json.Marshaler
}

type lambda func(v *jsem.Value, f string) []Error

func (l lambda) Validate(v *jsem.Value, f string) []Error {
	return l(v, f)
}

func (l lambda) MarshalJSON() ([]byte, error) {
	return []byte(``), nil // dummy, never exposed
}

type AnythingValidator struct{}

func Anything() Validator {
	return AnythingValidator{}
}

func (a AnythingValidator) Validate(v *jsem.Value, f string) []Error {
	return noErrors
}

func (a AnythingValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["anything",[]]`), nil
}

type StringValidator struct{}

func String() Validator {
	return StringValidator{}
}

func (a StringValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsString() {
		return noErrors
	}
	return []Error{Error{"value_must_be_string", f, nil}}
}

func (a StringValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["string",[]]`), nil
}

type NumberValidator struct{}

func Number() Validator {
	return NumberValidator{}
}

func (a NumberValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNumber() {
		return noErrors
	}
	return []Error{Error{"value_must_be_number", f, nil}}
}

func (a NumberValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["number",[]]`), nil
}

type BooleanValidator struct{}

func Boolean() Validator {
	return BooleanValidator{}
}

func (a BooleanValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsBoolean() {
		return noErrors
	}
	return []Error{Error{"value_must_be_boolean", f, nil}}
}

func (a BooleanValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["boolean",[]]`), nil
}

type NullValidator struct{}

func Null() Validator {
	return NullValidator{}
}

func (a NullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return noErrors
	}
	return []Error{Error{"value_must_be_null", f, nil}}
}

func (a NullValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["null",[]]`), nil
}

type NotNullValidator struct{}

func NotNull() Validator {
	return NotNullValidator{}
}

func (a NotNullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return []Error{Error{"value_must_not_be_null", f, nil}}
	}
	return noErrors
}

func (a NotNullValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["notNull",[]]`), nil
}

type AndValidator []Validator

func And(vs ...Validator) Validator {
	return AndValidator(vs)
}

func (a AndValidator) Validate(v *jsem.Value, f string) []Error {
	for _, b := range a {
		es := b.Validate(v, f)
		if len(es) != 0 {
			return es
		}
	}
	return noErrors
}

func (a AndValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["and",[`)
	for i, l := 0, len(a); i < l; i++ {
		bs, _ := a[i].MarshalJSON()
		bf.Write(bs)
		if i < (l - 1) {
			bf.WriteString(`,`)
		}
	}
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type TupleValidator []Validator

func Tuple(vs ...Validator) Validator {
	return TupleValidator(vs)
}

func (a TupleValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Array(Anything()), Length(len(a)), lambda(func(v *jsem.Value, f string) []Error {
		for i, b := range a {
			u, _ := v.ArrayIndex(i)
			es := b.Validate(u, f+"."+strconv.Itoa(i))
			if len(es) != 0 {
				return es
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a TupleValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["tuple",[`)
	for i, l := 0, len(a); i < l; i++ {
		bs, _ := a[i].MarshalJSON()
		bf.Write(bs)
		if i < (l - 1) {
			bf.WriteString(`,`)
		}
	}
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type OrValidator []Validator

func Or(vs ...Validator) Validator {
	return OrValidator(vs)
}

func (b OrValidator) Validate(v *jsem.Value, f string) []Error {
	ae := make([]Error, 0, len(b))
	for _, a := range b {
		es := a.Validate(v, f)
		if len(es) == 0 {
			return noErrors
		}
		ae = append(ae, es...)
	}
	return ae // TODO(thwd): merge errors into one?
}

func (a OrValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["or",[`)
	for i, l := 0, len(a); i < l; i++ {
		bs, _ := a[i].MarshalJSON()
		bf.Write(bs)
		if i < (l - 1) {
			bf.WriteString(`,`)
		}
	}
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type ObjectValidator map[string]Validator

func Object(d map[string]Validator) Validator {
	return ObjectValidator(d)
}

func (d ObjectValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, len(d))
	v.ObjectForEach(func(k string, u *jsem.Value) {
		if _, ok := d[k]; !ok {
			ae = append(ae, Error{"unexpected_object_key", f + "." + k, nil})
		}
	})
	for k, a := range d {
		u, _ := v.ObjectKey(k)
		ae = append(ae, a.Validate(u, f+"."+k)...)
	}
	return ae
}

func (a ObjectValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["object",[`)
	json.NewEncoder(bf).Encode(map[string]Validator(a)) // TODO(thwd): TEST THIS!
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type MapValidator struct {
	e Validator
}

func Map(e Validator) Validator {
	return MapValidator{e}
}

func (a MapValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ObjectForEach(func(k string, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, f+"."+k)...)
	})
	return ae
}

func (a MapValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["map",[`)
	bs, _ := a.e.MarshalJSON()
	bf.Write(bs)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type ArrayValidator struct {
	e Validator
}

func Array(e Validator) Validator {
	return ArrayValidator{e}
}

func (a ArrayValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsArray() {
		return []Error{Error{"value_must_be_array", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ArrayForEach(func(i int, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, f+"."+strconv.Itoa(i))...)
	})
	return ae
}

func (a ArrayValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["array",[`)
	bs, _ := a.e.MarshalJSON()
	bf.Write(bs)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type RegexValidator struct {
	r *regexp.Regexp
}

func Regex(r *regexp.Regexp) Validator {
	return RegexValidator{r}
}

func (a RegexValidator) Validate(v *jsem.Value, f string) []Error {
	return And(String(), lambda(func(v *jsem.Value, f string) []Error {
		s, _ := v.String()
		if a.r.Match([]byte(s)) {
			return noErrors
		}
		return []Error{
			Error{"value_must_match_regex", f, map[string]string{"regex": a.r.String()}},
		}
	})).Validate(v, f)
}

func (a RegexValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["regex",[`)
	json.NewEncoder(bf).Encode(a.r.String())
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type OptionalValidator struct {
	e Validator
}

func Optional(e Validator) Validator {
	return OptionalValidator{e}
}

func (a OptionalValidator) Validate(v *jsem.Value, f string) []Error {
	if v == nil {
		return noErrors
	}
	return a.e.Validate(v, f)
}

func (a OptionalValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["optional",[`)
	bs, _ := a.e.MarshalJSON()
	bf.Write(bs)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type LengthBetweenValidator struct {
	x, y int
}

func LengthBetween(x, y int) Validator {
	if y < x {
		panic("LengthBetween: y < x")
	}
	return LengthBetweenValidator{x, y}
}

func (a LengthBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Or(String(), Array(Anything())), lambda(func(v *jsem.Value, f string) []Error {
		l := -1
		if v.IsArray() {
			l, _ = v.ArrayLength()
		} else {
			l, _ = v.StringLength()
		}
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_length_between", f, map[string]int{"min": a.x, "max": a.y}},
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a LengthBetweenValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["lengthBetween",[`)
	json.NewEncoder(bf).Encode(a.x)
	bf.WriteString(`,`)
	json.NewEncoder(bf).Encode(a.y)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

func Length(x int) Validator {
	return LengthBetween(x, x)
}

type NumberBetweenValidator struct {
	x, y float64
}

func NumberBetween(x, y float64) Validator {
	if y < x {
		panic("NumberBetween: y < x")
	}
	return NumberBetweenValidator{x, y}
}

func (a NumberBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Number(), lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Float64()
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]float64{"min": a.x, "max": a.y}},
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a NumberBetweenValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["numberBetween",[`)
	json.NewEncoder(bf).Encode(a.x)
	bf.WriteString(`,`)
	json.NewEncoder(bf).Encode(a.y)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

type ExactlyValidator struct {
	j *jsem.Value
}

func Exactly(j *jsem.Value) Validator {
	return ExactlyValidator{j}
}

func (a ExactlyValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.Equals(a.j) {
		return []Error{Error{"value_not_matched_exactly", f, nil}}
	}
	return noErrors
}

func (a ExactlyValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	bf.WriteString(`["exactly",[`)
	bs, _ := a.j.MarshalJSON()
	bf.Write(bs)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}

// here be dragons! use only if you know exactly what you're doing

type RecursiveValidator struct {
	v Validator
	l int
}

func (r *RecursiveValidator) Validate(v *jsem.Value, f string) []Error {
	return r.v.Validate(v, f)
}

func (r *RecursiveValidator) Define(v Validator) {
	r.v = v
}

func (a *RecursiveValidator) MarshalJSON() ([]byte, error) {
	bf := bytes.NewBuffer(nil)
	if a.l == 0 {
		bf := bytes.NewBuffer(nil)
		a.l = rand.Int()
		bf.WriteString(`["recursion",[`)
		json.NewEncoder(bf).Encode(a.l)
		bf.WriteString(`,`)
		bs, _ := a.v.MarshalJSON()
		bf.Write(bs)
		a.l = 0
		bf.WriteString(`]]`)
		return bf.Bytes(), nil
	}
	bf.WriteString(`["recurse",[`)
	json.NewEncoder(bf).Encode(a.l)
	bf.WriteString(`]]`)
	return bf.Bytes(), nil
}
<commit_msg>cleaned up MarshalJSON methods<commit_after>package jval

import (
	"encoding/json"
	"github.com/thwd/jsem"
	"math"
	"math/rand"
	"regexp"
	"strconv"
	"time"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type Error struct {
	Label   string      `json:"label"`
	Field   string      `json:"field"`
	Context interface{} `json:"context"`
}

// Error implements native "error" interface
func (e Error) Error() string {
	return e.Label
}

var noErrors = []Error{}

type Validator interface {
	Validate(value *jsem.Value, field string) []Error
	json.Marshaler
}

type lambda func(v *jsem.Value, f string) []Error

func (l lambda) Validate(v *jsem.Value, f string) []Error {
	return l(v, f)
}

func (l lambda) MarshalJSON() ([]byte, error) {
	return []byte(``), nil // dummy, never exposed
}

type AnythingValidator struct{}

func Anything() Validator {
	return AnythingValidator{}
}

func (a AnythingValidator) Validate(v *jsem.Value, f string) []Error {
	return noErrors
}

func (a AnythingValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["anything",[]]`), nil
}

type StringValidator struct{}

func String() Validator {
	return StringValidator{}
}

func (a StringValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsString() {
		return noErrors
	}
	return []Error{Error{"value_must_be_string", f, nil}}
}

func (a StringValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["string",[]]`), nil
}

type NumberValidator struct{}

func Number() Validator {
	return NumberValidator{}
}

func (a NumberValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNumber() {
		return noErrors
	}
	return []Error{Error{"value_must_be_number", f, nil}}
}

func (a NumberValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["number",[]]`), nil
}

type BooleanValidator struct{}

func Boolean() Validator {
	return BooleanValidator{}
}

func (a BooleanValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsBoolean() {
		return noErrors
	}
	return []Error{Error{"value_must_be_boolean", f, nil}}
}

func (a BooleanValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["boolean",[]]`), nil
}

type NullValidator struct{}

func Null() Validator {
	return NullValidator{}
}

func (a NullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return noErrors
	}
	return []Error{Error{"value_must_be_null", f, nil}}
}

func (a NullValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["null",[]]`), nil
}

type NotNullValidator struct{}

func NotNull() Validator {
	return NotNullValidator{}
}

func (a NotNullValidator) Validate(v *jsem.Value, f string) []Error {
	if v.IsNull() {
		return []Error{Error{"value_must_not_be_null", f, nil}}
	}
	return noErrors
}

func (a NotNullValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["notNull",[]]`), nil
}

type AndValidator []Validator

func And(vs ...Validator) Validator {
	return AndValidator(vs)
}

func (a AndValidator) Validate(v *jsem.Value, f string) []Error {
	for _, b := range a {
		es := b.Validate(v, f)
		if len(es) != 0 {
			return es
		}
	}
	return noErrors
}

func (a AndValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"and", []Validator(a)})
}

type TupleValidator []Validator

func Tuple(vs ...Validator) Validator {
	return TupleValidator(vs)
}

func (a TupleValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Array(Anything()), Length(len(a)), lambda(func(v *jsem.Value, f string) []Error {
		for i, b := range a {
			u, _ := v.ArrayIndex(i)
			es := b.Validate(u, f+"."+strconv.Itoa(i))
			if len(es) != 0 {
				return es
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a TupleValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"tuple", []Validator(a)})
}

type OrValidator []Validator

func Or(vs ...Validator) Validator {
	return OrValidator(vs)
}

func (b OrValidator) Validate(v *jsem.Value, f string) []Error {
	ae := make([]Error, 0, len(b))
	for _, a := range b {
		es := a.Validate(v, f)
		if len(es) == 0 {
			return noErrors
		}
		ae = append(ae, es...)
	}
	return ae // TODO(thwd): merge errors into one?
}

func (a OrValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"or", []Validator(a)})
}

type ObjectValidator map[string]Validator

func Object(d map[string]Validator) Validator {
	return ObjectValidator(d)
}

func (d ObjectValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, len(d))
	v.ObjectForEach(func(k string, u *jsem.Value) {
		if _, ok := d[k]; !ok {
			ae = append(ae, Error{"unexpected_object_key", f + "." + k, nil})
		}
	})
	for k, a := range d {
		u, _ := v.ObjectKey(k)
		ae = append(ae, a.Validate(u, f+"."+k)...)
	}
	return ae
}

func (a ObjectValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"object", []map[string]Validator{map[string]Validator(a)}})
}

type MapValidator struct {
	e Validator
}

func Map(e Validator) Validator {
	return MapValidator{e}
}

func (a MapValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsObject() {
		return []Error{Error{"value_must_be_object", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ObjectForEach(func(k string, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, f+"."+k)...)
	})
	return ae
}

func (a MapValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"map", []Validator{a.e}})
}

type ArrayValidator struct {
	e Validator
}

func Array(e Validator) Validator {
	return ArrayValidator{e}
}

func (a ArrayValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.IsArray() {
		return []Error{Error{"value_must_be_array", f, nil}}
	}
	ae := make([]Error, 0, 8)
	v.ArrayForEach(func(i int, u *jsem.Value) {
		ae = append(ae, a.e.Validate(u, f+"."+strconv.Itoa(i))...)
	})
	return ae
}

func (a ArrayValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"array", []Validator{a.e}})
}

type RegexValidator struct {
	r *regexp.Regexp
}

func Regex(r *regexp.Regexp) Validator {
	return RegexValidator{r}
}

func (a RegexValidator) Validate(v *jsem.Value, f string) []Error {
	return And(String(), lambda(func(v *jsem.Value, f string) []Error {
		s, _ := v.String()
		if a.r.Match([]byte(s)) {
			return noErrors
		}
		return []Error{
			Error{"value_must_match_regex", f, map[string]string{"regex": a.r.String()}},
		}
	})).Validate(v, f)
}

func (a RegexValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"regex", []string{a.r.String()}})
}

type OptionalValidator struct {
	e Validator
}

func Optional(e Validator) Validator {
	return OptionalValidator{e}
}

func (a OptionalValidator) Validate(v *jsem.Value, f string) []Error {
	if v == nil {
		return noErrors
	}
	return a.e.Validate(v, f)
}

func (a OptionalValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"optional", []Validator{a.e}})
}

type LengthBetweenValidator struct {
	x, y int
}

func LengthBetween(x, y int) Validator {
	if y < x {
		panic("LengthBetween: y < x")
	}
	return LengthBetweenValidator{x, y}
}

func (a LengthBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Or(String(), Array(Anything())), lambda(func(v *jsem.Value, f string) []Error {
		l := -1
		if v.IsArray() {
			l, _ = v.ArrayLength()
		} else {
			l, _ = v.StringLength()
		}
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_length_between", f, map[string]int{"min": a.x, "max": a.y}},
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a LengthBetweenValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"lengthBetween", []int{a.x, a.y}})
}

func Length(x int) Validator {
	return LengthBetween(x, x)
}

type NumberBetweenValidator struct {
	x, y float64
}

func NumberBetween(x, y float64) Validator {
	if y < x {
		panic("NumberBetween: y < x")
	}
	return NumberBetweenValidator{x, y}
}

func (a NumberBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Number(), lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Float64()
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]float64{"min": a.x, "max": a.y}},
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a NumberBetweenValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"numberBetween", []float64{a.x, a.y}})
}

type WholeNumberValidator struct{}

func WholeNumber() Validator {
	return WholeNumberValidator{}
}

func (a WholeNumberValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Number(), lambda(func(v *jsem.Value, f string) []Error {
		n, _ := v.Float64()
		_, r := math.Modf(n)
		if r != 0 {
			return []Error{Error{"value_must_be_whole_number", f, nil}}
		}
		return noErrors
	})).Validate(v, f)
}

func (a WholeNumberValidator) MarshalJSON() ([]byte, error) {
	return []byte(`["wholeNumber",[]]`), nil
}

type WholeNumberBetweenValidator struct {
	x, y int
}

func WholeNumberBetween(x, y int) Validator {
	if y < x {
		panic("WholeNumberBetween: y < x")
	}
	return WholeNumberBetweenValidator{x, y}
}

func (a WholeNumberBetweenValidator) Validate(v *jsem.Value, f string) []Error {
	return And(WholeNumber(), lambda(func(v *jsem.Value, f string) []Error {
		l, _ := v.Int()
		if l < a.x || l > a.y {
			return []Error{
				Error{"value_must_have_value_between", f, map[string]int{"min": a.x, "max": a.y}},
			}
		}
		return noErrors
	})).Validate(v, f)
}

func (a WholeNumberBetweenValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"wholeNumberBetween", []int{a.x, a.y}})
}

type ExactlyValidator struct {
	j *jsem.Value
}

func Exactly(j *jsem.Value) Validator {
	return ExactlyValidator{j}
}

func (a ExactlyValidator) Validate(v *jsem.Value, f string) []Error {
	if !v.Equals(a.j) {
		return []Error{Error{"value_not_matched_exactly", f, nil}}
	}
	return noErrors
}

func (a ExactlyValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"exactly", []*jsem.Value{a.j}})
}

type CaseValidator []Validator

func Case(c []Validator) Validator {
	return CaseValidator(c)
}

func (a CaseValidator) Validate(v *jsem.Value, f string) []Error {
	return And(Tuple(WholeNumberBetween(0, len(a)-1), Anything()), lambda(func(v *jsem.Value, f string) []Error {
		cv, _ := v.ArrayIndex(0)
		vv, _ := v.ArrayIndex(1)
		ix, _ := cv.Int()
		return a[ix].Validate(vv, f)
	})).Validate(v, f)
}

func (a CaseValidator) MarshalJSON() ([]byte, error) {
	return json.Marshal([]interface{}{"case", []Validator(a)})
}

// here be dragons! use only if you know exactly what you're doing

type RecursiveValidator struct {
	v Validator
	l int
}

func (r *RecursiveValidator) Validate(v *jsem.Value, f string) []Error {
	return r.v.Validate(v, f)
}

func (r *RecursiveValidator) Define(v Validator) {
	r.v = v
}

func (a *RecursiveValidator) MarshalJSON() ([]byte, error) {
	if a.l == 0 {
		a.l = 1 + rand.Int()
		bs, e := json.Marshal([]interface{}{"recursion", []interface{}{a.l, a.v}})
		a.l = 0
		return bs, e
	}
	return json.Marshal([]interface{}{"recurse", []interface{}{a.l}})
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 Bowery, Inc.

package prompt

// Line ending in raw mode.
var crlf = []byte("\r\n")

const (
	tabKey    = '\t'
	backKey   = '\u007f'
	returnKey = '\r'
	escKey    = '\u001B'
	spaceKey  = '\u0020'
)

const (
	ctrlA = iota + 1
	ctrlB
	ctrlC
	ctrlD
	ctrlE
	ctrlF
	ctrlG
	ctrlH
	_ // Same as tabKey.
	ctrlJ
	ctrlK
	ctrlL
	_ // Same as returnKey.
	ctrlN
	ctrlO
	ctrlP
	ctrlQ
	ctrlR
	ctrlS
	ctrlT
	ctrlU
	ctrlV
	ctrlW
	ctrlX
	ctrlY
	ctrlZ
)
<commit_msg>Remove blank identifiers from const declaration.<commit_after>// Copyright 2013-2014 Bowery, Inc.

package prompt

// Line ending in raw mode.
var crlf = []byte("\r\n")

const (
	backKey  = '\u007f'
	escKey   = '\u001B'
	spaceKey = '\u0020'
)

const (
	ctrlA = iota + 1
	ctrlB
	ctrlC
	ctrlD
	ctrlE
	ctrlF
	ctrlG
	ctrlH
	tabKey
	ctrlJ
	ctrlK
	ctrlL
	returnKey
	ctrlN
	ctrlO
	ctrlP
	ctrlQ
	ctrlR
	ctrlS
	ctrlT
	ctrlU
	ctrlV
	ctrlW
	ctrlX
	ctrlY
	ctrlZ
)
<|endoftext|>


<commit_before>package redis

import

// Key commands http://redis.io/commands#generic
"strconv"

func (c *Client) Del(key string, otherKeys ...string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Dump(key string) (bool, error) {
	// TODO(ttacon): ✔
	return false, nil
}

func (c *Client) Expire(key string, seconds int) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) ExpireAt(key string, timestamp int64) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Keys(pattern string) ([]string, error) {
	// TODO(ttacon): ✔
	return nil, nil
}

// TODO(ttacon): add correct function signature
func (c *Client) Migrate(key string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Move(key string, db int) error {
	// TODO(ttacon): ✔
	return nil
}

// TODO(ttacon): add correct function signature
func (c *Client) Object(key string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Persist(key string) (bool, error) {
	resp, err := c.exec("PERSIST", key)
	if err != nil {
		return false, err
	}

	val, err := c.intResp(resp)
	return val == 1, err
}

func (c *Client) PExpire(key string, milliseconds int64) (bool, error) {
	resp, err := c.exec("PEXPIRE", key, strconv.FormatInt(milliseconds, 10))
	if err != nil {
		return false, err
	}
	val, err := c.intResp(resp)
	return val == 1, err
}

func (c *Client) PExpireAt(key string, timestamp int64) (bool, error) {
	resp, err := c.exec("PEXPIREAT", key, strconv.FormatInt(timestamp, 10))
	if err != nil {
		return false, err
	}
	v, err := c.intResp(resp)
	return v == 1, err
}

func (c *Client) PTTL(key string) (int64, error) {
	resp, err := c.exec("PTTL", key)
	if err != nil {
		return 0, err
	}
	return c.intResp(resp)
}

func (c *Client) RandomKey() (string, error) {
	resp, err := c.exec("RANDOMKEY")
	if err != nil {
		return "", err
	}
	return c.bulkString(resp)
}

func (c *Client) Rename(key, newkey string) error {
	_, err := c.exec("RENAME", key, newkey)
	return err
}

func (c *Client) Restore(key string, ttl int, serialized string) error {
	_, err := c.exec("RESTORE", key, strconv.Itoa(ttl), serialized)
	return err
}

func (c *Client) Sort(key string, modifiers ...string) ([]string, error) {
	resp, err := c.exec("SORT", append([]string{key}, modifiers...)...)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) TTL(key string) (int64, error) {
	resp, err := c.exec("TTL", key)
	if err != nil {
		return 0, err
	}
	return c.intResp(resp)
}

// TODO(ttacon): add type enum and change return types
func (c *Client) Type(key string) (string, error) {
	resp, err := c.exec("TYPE", key)
	if err != nil {
		return "", err
	}
	return c.stringResp(resp)
}

func (c *Client) Scan(key string, cursor int) ([]string, int, error) {
	resp, err := c.exec("SCAN", strconv.Itoa(cursor))
	if err != nil {
		return nil, 0, err
	}
	vals, err := c.stringSlice(resp)

	// the first val in vals (if no error) is the cursor val
	if err != nil {
		return nil, 0, err
	}

	// TODO(ttacon): this should probably be 64, shouldn't it?
	val, err := strconv.ParseInt(vals[0], 10, 32)
	return vals[1:], int(val), err
}
<commit_msg>Add move<commit_after>package redis

import

// Key commands http://redis.io/commands#generic
"strconv"

func (c *Client) Del(key string, otherKeys ...string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Dump(key string) (bool, error) {
	// TODO(ttacon): ✔
	return false, nil
}

func (c *Client) Expire(key string, seconds int) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) ExpireAt(key string, timestamp int64) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Keys(pattern string) ([]string, error) {
	// TODO(ttacon): ✔
	return nil, nil
}

// TODO(ttacon): add correct function signature
func (c *Client) Migrate(key string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Move(key string, db int) (bool, error) {
	resp, err := c.exec("MOVE", key, strconv.Itoa(db))
	if err != nil {
		return false, err
	}

	val, err := c.intResp(resp)
	return val == 1, err
}

// TODO(ttacon): add correct function signature
func (c *Client) Object(key string) error {
	// TODO(ttacon): ✔
	return nil
}

func (c *Client) Persist(key string) (bool, error) {
	resp, err := c.exec("PERSIST", key)
	if err != nil {
		return false, err
	}

	val, err := c.intResp(resp)
	return val == 1, err
}

func (c *Client) PExpire(key string, milliseconds int64) (bool, error) {
	resp, err := c.exec("PEXPIRE", key, strconv.FormatInt(milliseconds, 10))
	if err != nil {
		return false, err
	}
	val, err := c.intResp(resp)
	return val == 1, err
}

func (c *Client) PExpireAt(key string, timestamp int64) (bool, error) {
	resp, err := c.exec("PEXPIREAT", key, strconv.FormatInt(timestamp, 10))
	if err != nil {
		return false, err
	}
	v, err := c.intResp(resp)
	return v == 1, err
}

func (c *Client) PTTL(key string) (int64, error) {
	resp, err := c.exec("PTTL", key)
	if err != nil {
		return 0, err
	}
	return c.intResp(resp)
}

func (c *Client) RandomKey() (string, error) {
	resp, err := c.exec("RANDOMKEY")
	if err != nil {
		return "", err
	}
	return c.bulkString(resp)
}

func (c *Client) Rename(key, newkey string) error {
	_, err := c.exec("RENAME", key, newkey)
	return err
}

func (c *Client) Restore(key string, ttl int, serialized string) error {
	_, err := c.exec("RESTORE", key, strconv.Itoa(ttl), serialized)
	return err
}

func (c *Client) Sort(key string, modifiers ...string) ([]string, error) {
	resp, err := c.exec("SORT", append([]string{key}, modifiers...)...)
	if err != nil {
		return nil, err
	}
	return c.stringSlice(resp)
}

func (c *Client) TTL(key string) (int64, error) {
	resp, err := c.exec("TTL", key)
	if err != nil {
		return 0, err
	}
	return c.intResp(resp)
}

// TODO(ttacon): add type enum and change return types
func (c *Client) Type(key string) (string, error) {
	resp, err := c.exec("TYPE", key)
	if err != nil {
		return "", err
	}
	return c.stringResp(resp)
}

func (c *Client) Scan(key string, cursor int) ([]string, int, error) {
	resp, err := c.exec("SCAN", strconv.Itoa(cursor))
	if err != nil {
		return nil, 0, err
	}
	vals, err := c.stringSlice(resp)

	// the first val in vals (if no error) is the cursor val
	if err != nil {
		return nil, 0, err
	}

	// TODO(ttacon): this should probably be 64, shouldn't it?
	val, err := strconv.ParseInt(vals[0], 10, 32)
	return vals[1:], int(val), err
}
<|endoftext|>


<commit_before>package migrate

import (
	"fmt"
	"log"
	"path/filepath"

	"github.com/codegangsta/cli"
	"github.com/marcosnils/cmt/cmd"
	"github.com/marcosnils/cmt/validate"
)

var Command = cli.Command{
	Name:  "migrate",
	Usage: "Migrate running container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "src",
			Usage: "Source host where the container is running",
		},
		cli.StringFlag{
			Name:  "dst",
			Usage: "Target host to migrate the container",
		},
	},
	Action: func(c *cli.Context) {
		srcUrl := validate.ParseURL(c.String("src"))
		dstUrl := validate.ParseURL(c.String("dst"))

		log.Println("Performing validations")
		src, dst := validate.Validate(srcUrl, dstUrl)

		log.Println("Preparing everything to do a checkpoint")
		imagesPath := fmt.Sprintf("%s/images", srcUrl.Path)
		containerId := getContainerId(srcUrl.Path)

		_, _, err := src.Run("mkdir", "-p", imagesPath)
		if err != nil {
			log.Fatal("Error preparing images dir:", err)
		}

		log.Println("Performing the checkpoint")
		_, _, err = src.Run("sudo", "runc", "--id", containerId, "checkpoint", "--image-path", imagesPath)
		if err != nil {
			log.Fatal("Error performing checkpoint:",err)
		}

		srcTarFile := fmt.Sprintf("%s/dump.tar.gz", srcUrl.Path)
		dstTarFile := fmt.Sprintf("%s/images/dump.tar.gz", dstUrl.Path)
		_, _, err = src.Run("sudo", "tar", "-czf", srcTarFile, "-C", fmt.Sprintf("%s/", imagesPath), ".")
		if err != nil {
			log.Fatal("Error compressing image in source:", err)
		}

		log.Println("Copying checkpoint image to dst")
		_, _, err = dst.Run("mkdir", "-p", fmt.Sprintf("%s/images", dstUrl.Path))
		if err != nil {
			log.Fatal("Error preparing image dir in dst", err)
		}

		err = cmd.Scp(src.URL(srcTarFile), dst.URL(fmt.Sprintf("%s/images", dstUrl.Path)))
		if err != nil {
			log.Fatal("Error copying image files to dst", err)
		}

		log.Println("Preparing image at destination host")
		_, _, err = dst.Run("sudo", "tar", "-C", fmt.Sprintf("%s/images", dstUrl.Path), "-xvzf", dstTarFile)
		if err != nil {
			log.Fatal("Error uncompressing image in destination:", err)
		}

		log.Println("Performing the restore")
		configFilePath := fmt.Sprintf("%s/config.json", dstUrl.Path)
		runtimeFilePath := fmt.Sprintf("%s/runtime.json", dstUrl.Path)
		dstImagesPath := fmt.Sprintf("%s/images", dstUrl.Path)
		_, _, err = dst.Run("sudo", "runc", "--id", containerId, "restore", "--image-path", dstImagesPath, "--config-file", configFilePath, "--runtime-file", runtimeFilePath)
		if err != nil {
			log.Fatal("Error performing restore:", err)
		}

	},
}

func getContainerId(path string) string {
	_, id := filepath.Split(path)
	return id
}
<commit_msg>Add pre-dump support<commit_after>package migrate

import (
	"fmt"
	"log"
	"path/filepath"

	"github.com/codegangsta/cli"
	"github.com/marcosnils/cmt/cmd"
	"github.com/marcosnils/cmt/validate"
)

var Command = cli.Command{
	Name:  "migrate",
	Usage: "Migrate running container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "src",
			Usage: "Source host where the container is running",
		},
		cli.StringFlag{
			Name:  "dst",
			Usage: "Target host to migrate the container",
		},
		cli.BoolFlag{
			Name:  "pre-dump",
			Usage: "Perform a pre-dump to minimize downtime",
		},
	},
	Action: func(c *cli.Context) {
		srcUrl := validate.ParseURL(c.String("src"))
		dstUrl := validate.ParseURL(c.String("dst"))

		log.Println("Performing validations")
		src, dst := validate.Validate(srcUrl, dstUrl)

		log.Println("Preparing everything to do a checkpoint")
		containerId := getContainerId(srcUrl.Path)
		var imagesPath string

		if c.Bool("pre-dump") {
			// Process pre-dump
			predumpPath := fmt.Sprintf("%s/images/0", srcUrl.Path)
			prepareDir(src, predumpPath)

			checkpoint(src, containerId, predumpPath, true)

			srcTarFile := fmt.Sprintf("%s/predump.tar.gz", srcUrl.Path)
			prepareTar(src, srcTarFile, predumpPath)

			prepareDir(dst, fmt.Sprintf("%s/images/0", dstUrl.Path))

			log.Println("Copying predump image to dst")
			err := cmd.Scp(src.URL(srcTarFile), dst.URL(fmt.Sprintf("%s/images/0", dstUrl.Path)))
			if err != nil {
				log.Fatal("Error copying predump image files to dst", err)
			}

			dstTarFile := fmt.Sprintf("%s/images/0/predump.tar.gz", dstUrl.Path)
			unpackTar(dst, dstTarFile, fmt.Sprintf("%s/images/0", dstUrl.Path))

			// Process final image
			imagesPath = fmt.Sprintf("%s/images/1", srcUrl.Path)
			log.Println("Performing the checkpoint")
			_, _, err = src.Run("sudo", "runc", "--id", containerId, "checkpoint", "--image-path", imagesPath, "--prev-images-dir", predumpPath)
			if err != nil {
				log.Fatal("Error performing checkpoint:", err)
			}

			srcTarFile = fmt.Sprintf("%s/dump.tar.gz", srcUrl.Path)
			prepareTar(src, srcTarFile, imagesPath)
			prepareDir(dst, fmt.Sprintf("%s/images/1", dstUrl.Path))

			log.Println("Copying predump image to dst")
			err = cmd.Scp(src.URL(srcTarFile), dst.URL(fmt.Sprintf("%s/images/1", dstUrl.Path)))
			if err != nil {
				log.Fatal("Error copying predump image files to dst", err)
			}

			dstTarFile = fmt.Sprintf("%s/images/1/predump.tar.gz", dstUrl.Path)
			unpackTar(dst, dstTarFile, fmt.Sprintf("%s/images/1", dstUrl.Path))

			log.Println("Performing the restore")
			configFilePath := fmt.Sprintf("%s/config.json", dstUrl.Path)
			runtimeFilePath := fmt.Sprintf("%s/runtime.json", dstUrl.Path)
			dstImagesPath := fmt.Sprintf("%s/images/1", dstUrl.Path)
			_, _, err = dst.Output("sudo", "runc", "--id", containerId, "restore", "--image-path", dstImagesPath, "--config-file", configFilePath, "--runtime-file", runtimeFilePath)
			if err != nil {
				log.Fatal("Error performing restore:", err)
			}

		} else {
			imagesPath = fmt.Sprintf("%s/images", srcUrl.Path)
			prepareDir(src, imagesPath)

			checkpoint(src, containerId, imagesPath, false)

			srcTarFile := fmt.Sprintf("%s/dump.tar.gz", srcUrl.Path)
			prepareTar(src, srcTarFile, imagesPath)

			prepareDir(dst, fmt.Sprintf("%s/images", dstUrl.Path))

			log.Println("Copying checkpoint image to dst")
			err := cmd.Scp(src.URL(srcTarFile), dst.URL(fmt.Sprintf("%s/images", dstUrl.Path)))
			if err != nil {
				log.Fatal("Error copying image files to dst", err)
			}

			dstTarFile := fmt.Sprintf("%s/images/dump.tar.gz", dstUrl.Path)
			unpackTar(dst, dstTarFile, fmt.Sprintf("%s/images", dstUrl.Path))

			log.Println("Performing the restore")
			configFilePath := fmt.Sprintf("%s/config.json", dstUrl.Path)
			runtimeFilePath := fmt.Sprintf("%s/runtime.json", dstUrl.Path)
			dstImagesPath := fmt.Sprintf("%s/images", dstUrl.Path)
			_, _, err = dst.Output("sudo", "runc", "--id", containerId, "restore", "--image-path", dstImagesPath, "--config-file", configFilePath, "--runtime-file", runtimeFilePath)
			if err != nil {
				log.Fatal("Error performing restore:", err)
			}

		}

	},
}

func unpackTar(cmd cmd.Cmd, tarFile, workDir string) {
	log.Println("Preparing image at destination host")
	_, _, err := cmd.Run("sudo", "tar", "-C", workDir, "-xvzf", tarFile)
	if err != nil {
		log.Fatal("Error uncompressing image in destination:", err)
	}
}

func prepareTar(cmd cmd.Cmd, tarFile, workDir string) {
	_, _, err := cmd.Run("sudo", "tar", "-czf", tarFile, "-C", fmt.Sprintf("%s/", workDir), ".")
	if err != nil {
		log.Fatal("Error compressing image in source:", err)
	}
}

func checkpoint(cmd cmd.Cmd, containerId, imagesPath string, predump bool) {
	log.Printf("Performing the checkpoint predump =%s\n", predump)
	args := []string{"runc", "--id", containerId, "checkpoint", "--image-path", imagesPath}
	if predump {
		args = append(args, "--pre-dump")
	}
	_, _, err := cmd.Run("sudo", args...)
	if err != nil {
		log.Fatal("Error performing checkpoint:", err)
	}
}

func prepareDir(cmd cmd.Cmd, path string) {
	_, _, err := cmd.Run("mkdir", "-p", path)
	if err != nil {
		log.Fatal("Error preparing pre-dump dir:", err)
	}
}

func getContainerId(path string) string {
	_, id := filepath.Split(path)
	return id
}
<|endoftext|>


<commit_before>package api_test

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/pivotal-cf/om/api"
	"github.com/pivotal-cf/om/api/fakes"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("InstallationAssetService", func() {
	Describe("Export", func() {
		var (
			client     *fakes.HttpClient
			outputFile *os.File
			bar        *fakes.Progress
			liveWriter *fakes.LiveWriter
		)

		BeforeEach(func() {
			var err error
			client = &fakes.HttpClient{}
			liveWriter = &fakes.LiveWriter{}
			outputFile, err = ioutil.TempFile("", "")
			Expect(err).NotTo(HaveOccurred())
			bar = &fakes.Progress{}
		})

		AfterEach(func() {
			err := os.Remove(outputFile.Name())
			Expect(err).NotTo(HaveOccurred())
		})

		It("makes a request to export the current Ops Manager installation", func() {
			client.DoReturns(&http.Response{
				StatusCode:    http.StatusOK,
				ContentLength: 22,
				Body:          ioutil.NopCloser(strings.NewReader("some-installation")),
			}, nil)

			bar.NewBarReaderReturns(strings.NewReader("some-fake-installation"))
			service := api.NewInstallationAssetService(client, bar, liveWriter)

			err := service.Export(outputFile.Name())
			Expect(err).NotTo(HaveOccurred())

			By("posting to the installation_asset_collection endpoint")
			request := client.DoArgsForCall(0)
			Expect(request.Method).To(Equal("GET"))
			Expect(request.URL.Path).To(Equal("/api/v0/installation_asset_collection"))

			By("writing the installation to a local file")
			ins, err := ioutil.ReadFile(outputFile.Name())
			Expect(err).NotTo(HaveOccurred())
			Expect(string(ins)).To(Equal("some-fake-installation"))

			newReaderContent, err := ioutil.ReadAll(bar.NewBarReaderArgsForCall(0))
			Expect(err).NotTo(HaveOccurred())

			Expect(string(newReaderContent)).To(Equal("some-installation"))
			Expect(bar.SetTotalArgsForCall(0)).To(BeNumerically("==", 22))
			Expect(bar.KickoffCallCount()).To(Equal(1))
			Expect(bar.EndCallCount()).To(Equal(1))
		})

		It("logs while waiting for a response from the Ops Manager", func() {
			client.DoStub = func(req *http.Request) (*http.Response, error) {
				if req.URL.Path == "/api/v0/installation_asset_collection" {
					time.Sleep(5 * time.Second)
					return &http.Response{
						StatusCode: http.StatusOK,
						Body:       ioutil.NopCloser(strings.NewReader("some-installation")),
					}, nil
				}
				return nil, nil
			}

			bar.NewBarReaderReturns(strings.NewReader("some-fake-installation"))
			service := api.NewInstallationAssetService(client, bar, liveWriter)

			err := service.Export(outputFile.Name())
			Expect(err).NotTo(HaveOccurred())

			By("starting the live log writer")
			Expect(liveWriter.StartCallCount()).To(Equal(1))

			By("writing to the live log writer")
			Expect(liveWriter.WriteCallCount()).To(Equal(5))
			for i := 0; i < 5; i++ {
				Expect(string(liveWriter.WriteArgsForCall(i))).To(ContainSubstring(fmt.Sprintf("%ds elapsed", i+1)))
			}

			By("flushing the live log writer")
			Expect(liveWriter.StopCallCount()).To(Equal(1))
		})

		Context("when an error occurs", func() {
			Context("when the client errors before the request", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{}, errors.New("some client error"))
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Export("fake-file")
					Expect(err).To(MatchError("could not make api request to installation_asset_collection endpoint: some client error"))
				})
			})

			Context("when the api returns a non-200 status code", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{
						StatusCode: http.StatusInternalServerError,
						Body:       ioutil.NopCloser(strings.NewReader("")),
					}, nil)
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Export("fake-file")
					Expect(err).To(MatchError(ContainSubstring("request failed: unexpected response")))
				})
			})

			Context("when the output file cannot be written", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{
						StatusCode: http.StatusOK,
						Body:       ioutil.NopCloser(strings.NewReader("{}")),
					}, nil)
					bar.NewBarReaderReturns(strings.NewReader("some-fake-installation"))
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Export("fake-dir/fake-file")
					Expect(err).To(MatchError(ContainSubstring("no such file")))
				})
			})
		})
	})

	Describe("Import", func() {
		var (
			client     *fakes.HttpClient
			bar        *fakes.Progress
			liveWriter *fakes.LiveWriter
		)

		BeforeEach(func() {
			client = &fakes.HttpClient{}
			liveWriter = &fakes.LiveWriter{}
			bar = &fakes.Progress{}
		})

		It("makes a request to import the installation to the Ops Manager", func() {
			client.DoReturns(&http.Response{
				StatusCode: http.StatusOK,
				Body:       ioutil.NopCloser(strings.NewReader("{}")),
			}, nil)

			bar.NewBarReaderReturns(strings.NewReader("some other installation"))
			service := api.NewInstallationAssetService(client, bar, liveWriter)

			err := service.Import(api.ImportInstallationInput{
				ContentLength: 10,
				Installation:  strings.NewReader("some installation"),
				ContentType:   "some content-type",
			})
			Expect(err).NotTo(HaveOccurred())

			request := client.DoArgsForCall(0)
			Expect(request.Method).To(Equal("POST"))
			Expect(request.URL.Path).To(Equal("/api/v0/installation_asset_collection"))
			Expect(request.ContentLength).To(Equal(int64(10)))
			Expect(request.Header.Get("Content-Type")).To(Equal("some content-type"))

			body, err := ioutil.ReadAll(request.Body)
			Expect(err).NotTo(HaveOccurred())

			Expect(string(body)).To(Equal("some other installation"))

			newReaderContent, err := ioutil.ReadAll(bar.NewBarReaderArgsForCall(0))
			Expect(err).NotTo(HaveOccurred())

			Expect(string(newReaderContent)).To(Equal("some installation"))
			Expect(bar.SetTotalArgsForCall(0)).To(BeNumerically("==", 10))
			Expect(bar.KickoffCallCount()).To(Equal(1))
			Expect(bar.EndCallCount()).To(Equal(1))
		})

		It("logs while waiting for a response from the Ops Manager", func() {
			client.DoStub = func(req *http.Request) (*http.Response, error) {
				if req.URL.Path == "/api/v0/installation_asset_collection" {
					time.Sleep(5 * time.Second)
					return &http.Response{
						StatusCode: http.StatusOK,
						Body:       ioutil.NopCloser(strings.NewReader("{}")),
					}, nil
				}
				return nil, nil
			}

			bar.NewBarReaderReturns(strings.NewReader("some-fake-installation"))
			service := api.NewInstallationAssetService(client, bar, liveWriter)

			err := service.Import(api.ImportInstallationInput{
				ContentLength: 10,
				Installation:  strings.NewReader("some installation"),
				ContentType:   "some content-type",
			})
			Expect(err).NotTo(HaveOccurred())

			By("starting the live log writer")
			Expect(liveWriter.StartCallCount()).To(Equal(1))

			By("writing to the live log writer")
			Expect(liveWriter.WriteCallCount()).To(Equal(5))
			for i := 0; i < 5; i++ {
				Expect(string(liveWriter.WriteArgsForCall(i))).To(ContainSubstring(fmt.Sprintf("%ds elapsed", i+1)))
			}

			By("flushing the live log writer")
			Expect(liveWriter.StopCallCount()).To(Equal(1))
		})

		Context("when an error occurs", func() {
			Context("when the client errors before the request", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{}, errors.New("some client error"))
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Import(api.ImportInstallationInput{})
					Expect(err).To(MatchError("could not make api request to installation_asset_collection endpoint: some client error"))
				})
			})

			Context("when the api returns a non-200 status code", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{
						StatusCode: http.StatusInternalServerError,
						Body:       ioutil.NopCloser(strings.NewReader("{}")),
					}, nil)
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Import(api.ImportInstallationInput{})
					Expect(err).To(MatchError(ContainSubstring("request failed: unexpected response")))
				})
			})
		})
	})
})
<commit_msg>corrects test that had timing issues<commit_after>package api_test

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/pivotal-cf/om/api"
	"github.com/pivotal-cf/om/api/fakes"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("InstallationAssetService", func() {
	Describe("Export", func() {
		var (
			client     *fakes.HttpClient
			outputFile *os.File
			bar        *fakes.Progress
			liveWriter *fakes.LiveWriter
		)

		BeforeEach(func() {
			var err error
			client = &fakes.HttpClient{}
			liveWriter = &fakes.LiveWriter{}
			outputFile, err = ioutil.TempFile("", "")
			Expect(err).NotTo(HaveOccurred())
			bar = &fakes.Progress{}
		})

		AfterEach(func() {
			err := os.Remove(outputFile.Name())
			Expect(err).NotTo(HaveOccurred())
		})

		It("makes a request to export the current Ops Manager installation", func() {
			client.DoReturns(&http.Response{
				StatusCode:    http.StatusOK,
				ContentLength: 22,
				Body:          ioutil.NopCloser(strings.NewReader("some-installation")),
			}, nil)

			bar.NewBarReaderReturns(strings.NewReader("some-fake-installation"))
			service := api.NewInstallationAssetService(client, bar, liveWriter)

			err := service.Export(outputFile.Name())
			Expect(err).NotTo(HaveOccurred())

			By("posting to the installation_asset_collection endpoint")
			request := client.DoArgsForCall(0)
			Expect(request.Method).To(Equal("GET"))
			Expect(request.URL.Path).To(Equal("/api/v0/installation_asset_collection"))

			By("writing the installation to a local file")
			ins, err := ioutil.ReadFile(outputFile.Name())
			Expect(err).NotTo(HaveOccurred())
			Expect(string(ins)).To(Equal("some-fake-installation"))

			newReaderContent, err := ioutil.ReadAll(bar.NewBarReaderArgsForCall(0))
			Expect(err).NotTo(HaveOccurred())

			Expect(string(newReaderContent)).To(Equal("some-installation"))
			Expect(bar.SetTotalArgsForCall(0)).To(BeNumerically("==", 22))
			Expect(bar.KickoffCallCount()).To(Equal(1))
			Expect(bar.EndCallCount()).To(Equal(1))
		})

		It("logs while waiting for a response from the Ops Manager", func() {
			client.DoStub = func(req *http.Request) (*http.Response, error) {
				if req.URL.Path == "/api/v0/installation_asset_collection" {
					time.Sleep(5 * time.Second)
					return &http.Response{
						StatusCode: http.StatusOK,
						Body:       ioutil.NopCloser(strings.NewReader("some-installation")),
					}, nil
				}
				return nil, nil
			}

			bar.NewBarReaderReturns(strings.NewReader("some-fake-installation"))
			service := api.NewInstallationAssetService(client, bar, liveWriter)

			err := service.Export(outputFile.Name())
			Expect(err).NotTo(HaveOccurred())

			By("starting the live log writer")
			Expect(liveWriter.StartCallCount()).To(Equal(1))

			By("writing to the live log writer")
			Expect(liveWriter.WriteCallCount()).To(Equal(5))
			for i := 0; i < 5; i++ {
				Expect(string(liveWriter.WriteArgsForCall(i))).To(ContainSubstring(fmt.Sprintf("%ds elapsed", i+1)))
			}

			By("flushing the live log writer")
			Expect(liveWriter.StopCallCount()).To(Equal(1))
		})

		Context("when an error occurs", func() {
			Context("when the client errors before the request", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{}, errors.New("some client error"))
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Export("fake-file")
					Expect(err).To(MatchError("could not make api request to installation_asset_collection endpoint: some client error"))
				})
			})

			Context("when the api returns a non-200 status code", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{
						StatusCode: http.StatusInternalServerError,
						Body:       ioutil.NopCloser(strings.NewReader("")),
					}, nil)
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Export("fake-file")
					Expect(err).To(MatchError(ContainSubstring("request failed: unexpected response")))
				})
			})

			Context("when the output file cannot be written", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{
						StatusCode: http.StatusOK,
						Body:       ioutil.NopCloser(strings.NewReader("{}")),
					}, nil)
					bar.NewBarReaderReturns(strings.NewReader("some-fake-installation"))
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Export("fake-dir/fake-file")
					Expect(err).To(MatchError(ContainSubstring("no such file")))
				})
			})
		})
	})

	Describe("Import", func() {
		var (
			client     *fakes.HttpClient
			bar        *fakes.Progress
			liveWriter *fakes.LiveWriter
		)

		BeforeEach(func() {
			client = &fakes.HttpClient{}
			liveWriter = &fakes.LiveWriter{}
			bar = &fakes.Progress{}
		})

		It("makes a request to import the installation to the Ops Manager", func() {
			client.DoStub = func(req *http.Request) (*http.Response, error) {
				time.Sleep(1 * time.Second)
				return &http.Response{StatusCode: http.StatusOK,
					Body: ioutil.NopCloser(strings.NewReader("{}")),
				}, nil
			}

			bar.NewBarReaderReturns(strings.NewReader("some other installation"))
			service := api.NewInstallationAssetService(client, bar, liveWriter)

			err := service.Import(api.ImportInstallationInput{
				ContentLength: 10,
				Installation:  strings.NewReader("some installation"),
				ContentType:   "some content-type",
			})
			Expect(err).NotTo(HaveOccurred())

			request := client.DoArgsForCall(0)
			Expect(request.Method).To(Equal("POST"))
			Expect(request.URL.Path).To(Equal("/api/v0/installation_asset_collection"))
			Expect(request.ContentLength).To(Equal(int64(10)))
			Expect(request.Header.Get("Content-Type")).To(Equal("some content-type"))

			body, err := ioutil.ReadAll(request.Body)
			Expect(err).NotTo(HaveOccurred())

			Expect(string(body)).To(Equal("some other installation"))

			newReaderContent, err := ioutil.ReadAll(bar.NewBarReaderArgsForCall(0))
			Expect(err).NotTo(HaveOccurred())

			Expect(string(newReaderContent)).To(Equal("some installation"))
			Expect(bar.SetTotalArgsForCall(0)).To(BeNumerically("==", 10))
			Expect(bar.KickoffCallCount()).To(Equal(1))
			Expect(bar.EndCallCount()).To(Equal(1))
		})

		It("logs while waiting for a response from the Ops Manager", func() {
			client.DoStub = func(req *http.Request) (*http.Response, error) {
				if req.URL.Path == "/api/v0/installation_asset_collection" {
					time.Sleep(5 * time.Second)
					return &http.Response{
						StatusCode: http.StatusOK,
						Body:       ioutil.NopCloser(strings.NewReader("{}")),
					}, nil
				}
				return nil, nil
			}

			bar.NewBarReaderReturns(strings.NewReader("some-fake-installation"))
			service := api.NewInstallationAssetService(client, bar, liveWriter)

			err := service.Import(api.ImportInstallationInput{
				ContentLength: 10,
				Installation:  strings.NewReader("some installation"),
				ContentType:   "some content-type",
			})
			Expect(err).NotTo(HaveOccurred())

			By("starting the live log writer")
			Expect(liveWriter.StartCallCount()).To(Equal(1))

			By("writing to the live log writer")
			Expect(liveWriter.WriteCallCount()).To(Equal(5))
			for i := 0; i < 5; i++ {
				Expect(string(liveWriter.WriteArgsForCall(i))).To(ContainSubstring(fmt.Sprintf("%ds elapsed", i+1)))
			}

			By("flushing the live log writer")
			Expect(liveWriter.StopCallCount()).To(Equal(1))
		})

		Context("when an error occurs", func() {
			Context("when the client errors before the request", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{}, errors.New("some client error"))
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Import(api.ImportInstallationInput{})
					Expect(err).To(MatchError("could not make api request to installation_asset_collection endpoint: some client error"))
				})
			})

			Context("when the api returns a non-200 status code", func() {
				It("returns an error", func() {
					client.DoReturns(&http.Response{
						StatusCode: http.StatusInternalServerError,
						Body:       ioutil.NopCloser(strings.NewReader("{}")),
					}, nil)
					service := api.NewInstallationAssetService(client, bar, liveWriter)

					err := service.Import(api.ImportInstallationInput{})
					Expect(err).To(MatchError(ContainSubstring("request failed: unexpected response")))
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2018 The LUCI 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 git

import (
	"net/http"

	"go.chromium.org/luci/common/api/gitiles"
	"go.chromium.org/luci/common/data/stringset"
	"go.chromium.org/luci/common/errors"
	gitilespb "go.chromium.org/luci/common/proto/gitiles"
	"go.chromium.org/luci/server/auth"
	"golang.org/x/net/context"
)

// ClientFactory creates a Gitiles client.
type ClientFactory func(ctx context.Context, host string) (gitilespb.GitilesClient, error)

var factoryKey = "gitiles client factory key"

// UseFactory installs f into c.
func UseFactory(c context.Context, f ClientFactory) context.Context {
	return context.WithValue(c, &factoryKey, f)
}

// TODO(tandrii): remove the following per https://crbug.com/796317.
// Until Milo properly supports ACLs for blamelists, we have a hack; if the git
// repo being log'd is in this list, use `auth.AsSelf`. Otherwise use
// `auth.Anonymous`.
//
// The reason to do this is that we currently do blamelist calculation in the
// backend, so we can't accurately determine if the requesting user has access
// to these repos or not. For now, we use this whitelist to indicate domains
// that we know have full public read-access so that we can use milo's
// credentials (instead of anonymous) in order to avoid hitting gitiles'
// anonymous quota limits.
var whitelistPublicDomains = stringset.NewFromSlice(
	"chromium.googlesource.com",
)

// AuthenticatedProdClient returns a production Gitiles client.
//
// Currently, it is authenticated as self only for a whitelistPublicDomains.
// For all other repos, the client will not use authentication to avoid
// information leaks.
// TODO(tandrii): fix this per https://crbug.com/796317.
//
// Implements ClientFactory.
func AuthenticatedProdClient(c context.Context, host string) (gitilespb.GitilesClient, error) {
	var t http.RoundTripper
	var err error
	if whitelistPublicDomains.Has(host) {
		t, err = auth.GetRPCTransport(c, auth.AsSelf, auth.WithScopes(gitiles.OAuthScope))
	} else {
		// No scopes if we aren't authenticating.
		t, err = auth.GetRPCTransport(c, auth.NoAuth)
	}
	if err != nil {
		return nil, errors.Annotate(err, "getting RPC Transport").Err()
	}
	return gitiles.NewRESTClient(&http.Client{Transport: t}, host, true)
}

// Client creates a new Gitiles client using the ClientFactory installed in c.
// See also UseFactory.
func Client(c context.Context, host string) (gitilespb.GitilesClient, error) {
	f, ok := c.Value(&factoryKey).(ClientFactory)
	if !ok {
		return nil, errors.New("gitiles client factory is not installed in context")
	}
	return f(c, host)
}
<commit_msg>milo: derp, don't use /a when not authenticating.<commit_after>// Copyright 2018 The LUCI 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 git

import (
	"net/http"

	"go.chromium.org/luci/common/api/gitiles"
	"go.chromium.org/luci/common/data/stringset"
	"go.chromium.org/luci/common/errors"
	gitilespb "go.chromium.org/luci/common/proto/gitiles"
	"go.chromium.org/luci/server/auth"
	"golang.org/x/net/context"
)

// ClientFactory creates a Gitiles client.
type ClientFactory func(ctx context.Context, host string) (gitilespb.GitilesClient, error)

var factoryKey = "gitiles client factory key"

// UseFactory installs f into c.
func UseFactory(c context.Context, f ClientFactory) context.Context {
	return context.WithValue(c, &factoryKey, f)
}

// TODO(tandrii): remove the following per https://crbug.com/796317.
// Until Milo properly supports ACLs for blamelists, we have a hack; if the git
// repo being log'd is in this list, use `auth.AsSelf`. Otherwise use
// `auth.Anonymous`.
//
// The reason to do this is that we currently do blamelist calculation in the
// backend, so we can't accurately determine if the requesting user has access
// to these repos or not. For now, we use this whitelist to indicate domains
// that we know have full public read-access so that we can use milo's
// credentials (instead of anonymous) in order to avoid hitting gitiles'
// anonymous quota limits.
var whitelistPublicDomains = stringset.NewFromSlice(
	"chromium.googlesource.com",
)

// AuthenticatedProdClient returns a production Gitiles client.
//
// Currently, it is authenticated as self only for a whitelistPublicDomains.
// For all other repos, the client will not use authentication to avoid
// information leaks.
// TODO(tandrii): fix this per https://crbug.com/796317.
//
// Implements ClientFactory.
func AuthenticatedProdClient(c context.Context, host string) (gitilespb.GitilesClient, error) {
	var t http.RoundTripper
	var err error
	authenticate := true
	if whitelistPublicDomains.Has(host) {
		t, err = auth.GetRPCTransport(c, auth.AsSelf, auth.WithScopes(gitiles.OAuthScope))
	} else {
		authenticate = false
		// No scopes if we aren't authenticating.
		t, err = auth.GetRPCTransport(c, auth.NoAuth)
	}
	if err != nil {
		return nil, errors.Annotate(err, "getting RPC Transport").Err()
	}
	return gitiles.NewRESTClient(&http.Client{Transport: t}, host, authenticate)
}

// Client creates a new Gitiles client using the ClientFactory installed in c.
// See also UseFactory.
func Client(c context.Context, host string) (gitilespb.GitilesClient, error) {
	f, ok := c.Value(&factoryKey).(ClientFactory)
	if !ok {
		return nil, errors.New("gitiles client factory is not installed in context")
	}
	return f(c, host)
}
<|endoftext|>


<commit_before>package citrixadc

import (
	"github.com/chiradeep/go-nitro/config/cs"

	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/schema"

	"fmt"
	"log"
)

func resourceCitrixAdcCsvserver_appfwpolicy_binding() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createCsvserver_appfwpolicy_bindingFunc,
		Read:          readCsvserver_appfwpolicy_bindingFunc,
		Update:        updateCsvserver_appfwpolicy_bindingFunc,
		Delete:        deleteCsvserver_appfwpolicy_bindingFunc,
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"policyname": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"bindpoint": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"gotopriorityexpression": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"invoke": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
			"labelname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"labeltype": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"priority": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},
			"targetlbvserver": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func createCsvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In createCsvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	csvserver_appfwpolicy_bindingName := d.Get("name").(string)
	csvserver_appfwpolicy_binding := cs.Csvserverappfwpolicybinding{
		Bindpoint:              d.Get("bindpoint").(string),
		Gotopriorityexpression: d.Get("gotopriorityexpression").(string),
		Invoke:                 d.Get("invoke").(bool),
		Labelname:              d.Get("labelname").(string),
		Labeltype:              d.Get("labeltype").(string),
		Name:                   d.Get("name").(string),
		Policyname:             d.Get("policyname").(string),
		Priority:               d.Get("priority").(int),
		Targetlbvserver:        d.Get("targetlbvserver").(string),
	}

	_, err := client.AddResource(netscaler.Csvserver_appfwpolicy_binding.Type(), csvserver_appfwpolicy_bindingName, &csvserver_appfwpolicy_binding)
	if err != nil {
		return err
	}

	d.SetId(csvserver_appfwpolicy_bindingName)

	err = readCsvserver_appfwpolicy_bindingFunc(d, meta)
	if err != nil {
		log.Printf("[ERROR] netscaler-provider: ?? we just created this csvserver_appfwpolicy_binding but we can't read it ?? %s", csvserver_appfwpolicy_bindingName)
		return nil
	}
	return nil
}

func readCsvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] citrixadc-provider:  In readCsvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	csvserver_appfwpolicy_bindingName := d.Id()
	log.Printf("[DEBUG] citrixadc-provider: Reading csvserver_appfwpolicy_binding state %s", csvserver_appfwpolicy_bindingName)
	data, err := client.FindResource(netscaler.Csvserver_appfwpolicy_binding.Type(), csvserver_appfwpolicy_bindingName)
	if err != nil {
		log.Printf("[WARN] citrixadc-provider: Clearing csvserver_appfwpolicy_binding state %s", csvserver_appfwpolicy_bindingName)
		d.SetId("")
		return nil
	}
	d.Set("name", data["name"])
	d.Set("bindpoint", data["bindpoint"])
	d.Set("gotopriorityexpression", data["gotopriorityexpression"])
	d.Set("invoke", data["invoke"])
	d.Set("labelname", data["labelname"])
	d.Set("labeltype", data["labeltype"])
	d.Set("name", data["name"])
	d.Set("policyname", data["policyname"])
	d.Set("priority", data["priority"])
	d.Set("targetlbvserver", data["targetlbvserver"])

	return nil

}

func updateCsvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In updateCsvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	csvserver_appfwpolicy_bindingName := d.Get("name").(string)

	csvserver_appfwpolicy_binding := cs.Csvserverappfwpolicybinding{
		Name: d.Get("name").(string),
	}
	hasChange := false
	if d.HasChange("bindpoint") {
		log.Printf("[DEBUG]  citrixadc-provider: Bindpoint has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Bindpoint = d.Get("bindpoint").(string)
		hasChange = true
	}
	if d.HasChange("gotopriorityexpression") {
		log.Printf("[DEBUG]  citrixadc-provider: Gotopriorityexpression has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Gotopriorityexpression = d.Get("gotopriorityexpression").(string)
		hasChange = true
	}
	if d.HasChange("invoke") {
		log.Printf("[DEBUG]  citrixadc-provider: Invoke has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Invoke = d.Get("invoke").(bool)
		hasChange = true
	}
	if d.HasChange("labelname") {
		log.Printf("[DEBUG]  citrixadc-provider: Labelname has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Labelname = d.Get("labelname").(string)
		hasChange = true
	}
	if d.HasChange("labeltype") {
		log.Printf("[DEBUG]  citrixadc-provider: Labeltype has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Labeltype = d.Get("labeltype").(string)
		hasChange = true
	}
	if d.HasChange("name") {
		log.Printf("[DEBUG]  citrixadc-provider: Name has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Name = d.Get("name").(string)
		hasChange = true
	}
	if d.HasChange("policyname") {
		log.Printf("[DEBUG]  citrixadc-provider: Policyname has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Policyname = d.Get("policyname").(string)
		hasChange = true
	}
	if d.HasChange("priority") {
		log.Printf("[DEBUG]  citrixadc-provider: Priority has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Priority = d.Get("priority").(int)
		hasChange = true
	}
	if d.HasChange("targetlbvserver") {
		log.Printf("[DEBUG]  citrixadc-provider: Targetlbvserver has changed for csvserver_appfwpolicy_binding %s, starting update", csvserver_appfwpolicy_bindingName)
		csvserver_appfwpolicy_binding.Targetlbvserver = d.Get("targetlbvserver").(string)
		hasChange = true
	}

	if hasChange {
		_, err := client.UpdateResource(netscaler.Csvserver_appfwpolicy_binding.Type(), csvserver_appfwpolicy_bindingName, &csvserver_appfwpolicy_binding)
		if err != nil {
			return fmt.Errorf("Error updating csvserver_appfwpolicy_binding %s", csvserver_appfwpolicy_bindingName)
		}
	}
	return readCsvserver_appfwpolicy_bindingFunc(d, meta)
}

func deleteCsvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In deleteCsvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	args := make(map[string]string)
	args["policyname"] = d.Get("policyname").(string)
	err := client.DeleteResourceWithArgsMap(netscaler.Csvserver_appfwpolicy_binding.Type(), d.Get("name").(string), args)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}
<commit_msg>removed unwanted update function<commit_after>package citrixadc

import (
	"github.com/chiradeep/go-nitro/config/cs"

	"github.com/chiradeep/go-nitro/netscaler"
	"github.com/hashicorp/terraform/helper/schema"

	"log"
)

func resourceCitrixAdcCsvserver_appfwpolicy_binding() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,
		Create:        createCsvserver_appfwpolicy_bindingFunc,
		Read:          readCsvserver_appfwpolicy_bindingFunc,
		Delete:        deleteCsvserver_appfwpolicy_bindingFunc,
		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"policyname": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"bindpoint": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"gotopriorityexpression": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"invoke": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},
			"labelname": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"labeltype": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"priority": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
				Computed: true,
			},
			"targetlbvserver": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
		},
	}
}

func createCsvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In createCsvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	csvserver_appfwpolicy_bindingName := d.Get("name").(string)
	csvserver_appfwpolicy_binding := cs.Csvserverappfwpolicybinding{
		Bindpoint:              d.Get("bindpoint").(string),
		Gotopriorityexpression: d.Get("gotopriorityexpression").(string),
		Invoke:                 d.Get("invoke").(bool),
		Labelname:              d.Get("labelname").(string),
		Labeltype:              d.Get("labeltype").(string),
		Name:                   d.Get("name").(string),
		Policyname:             d.Get("policyname").(string),
		Priority:               d.Get("priority").(int),
		Targetlbvserver:        d.Get("targetlbvserver").(string),
	}

	_, err := client.AddResource(netscaler.Csvserver_appfwpolicy_binding.Type(), csvserver_appfwpolicy_bindingName, &csvserver_appfwpolicy_binding)
	if err != nil {
		return err
	}

	d.SetId(csvserver_appfwpolicy_bindingName)

	err = readCsvserver_appfwpolicy_bindingFunc(d, meta)
	if err != nil {
		log.Printf("[ERROR] netscaler-provider: ?? we just created this csvserver_appfwpolicy_binding but we can't read it ?? %s", csvserver_appfwpolicy_bindingName)
		return nil
	}
	return nil
}

func readCsvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG] citrixadc-provider:  In readCsvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	csvserver_appfwpolicy_bindingName := d.Id()
	log.Printf("[DEBUG] citrixadc-provider: Reading csvserver_appfwpolicy_binding state %s", csvserver_appfwpolicy_bindingName)
	data, err := client.FindResource(netscaler.Csvserver_appfwpolicy_binding.Type(), csvserver_appfwpolicy_bindingName)
	if err != nil {
		log.Printf("[WARN] citrixadc-provider: Clearing csvserver_appfwpolicy_binding state %s", csvserver_appfwpolicy_bindingName)
		d.SetId("")
		return nil
	}
	d.Set("name", data["name"])
	d.Set("bindpoint", data["bindpoint"])
	d.Set("gotopriorityexpression", data["gotopriorityexpression"])
	d.Set("invoke", data["invoke"])
	d.Set("labelname", data["labelname"])
	d.Set("labeltype", data["labeltype"])
	d.Set("name", data["name"])
	d.Set("policyname", data["policyname"])
	d.Set("priority", data["priority"])
	d.Set("targetlbvserver", data["targetlbvserver"])

	return nil

}

func deleteCsvserver_appfwpolicy_bindingFunc(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[DEBUG]  citrixadc-provider: In deleteCsvserver_appfwpolicy_bindingFunc")
	client := meta.(*NetScalerNitroClient).client
	args := make(map[string]string)
	args["policyname"] = d.Get("policyname").(string)
	err := client.DeleteResourceWithArgsMap(netscaler.Csvserver_appfwpolicy_binding.Type(), d.Get("name").(string), args)
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}
<|endoftext|>