<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/config/configschema"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/gocty"
)

// EvalValidateCount is an EvalNode implementation that validates
// the count of a resource.
type EvalValidateCount struct {
	Resource *configs.Resource
}

// TODO: test
func (n *EvalValidateCount) Eval(ctx EvalContext) (interface{}, error) {
	var diags tfdiags.Diagnostics
	var count int
	var err error

	val, valDiags := ctx.EvaluateExpr(n.Resource.Count, cty.Number, nil)
	diags = diags.Append(valDiags)
	if valDiags.HasErrors() {
		goto RETURN
	}
	if val.IsNull() || !val.IsKnown() {
		goto RETURN
	}

	err = gocty.FromCtyValue(val, &count)
	if err != nil {
		// The EvaluateExpr call above already guaranteed us a number value,
		// so if we end up here then we have something that is out of range
		// for an int, and the error message will include a description of
		// the valid range.
		rawVal := val.AsBigFloat()
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid count value",
			Detail:   fmt.Sprintf("The number %s is not a valid count value: %s.", rawVal, err),
			Subject:  n.Resource.Count.Range().Ptr(),
		})
	} else if count < 0 {
		rawVal := val.AsBigFloat()
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid count value",
			Detail:   fmt.Sprintf("The number %s is not a valid count value: count must not be negative.", rawVal),
			Subject:  n.Resource.Count.Range().Ptr(),
		})
	}

RETURN:
	return nil, diags.NonFatalErr()
}

// EvalValidateProvider is an EvalNode implementation that validates
// a provider configuration.
type EvalValidateProvider struct {
	Addr     addrs.ProviderConfig
	Provider *ResourceProvider
	Config   *configs.Provider
}

func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
	var diags tfdiags.Diagnostics
	provider := *n.Provider
	config := n.Config

	if n.Config == nil {
		// Nothing to validate, then.
		return nil, nil
	}

	schema, err := provider.GetSchema(&ProviderSchemaRequest{})
	if err != nil {
		diags = diags.Append(err)
		return nil, diags.NonFatalErr()
	}
	if schema == nil {
		return nil, fmt.Errorf("no schema is available for %s", n.Addr)
	}

	configSchema := schema.Provider
	configBody := buildProviderConfig(ctx, n.Addr, config.Config)
	configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, addrs.NoKey)
	diags = diags.Append(evalDiags)
	if evalDiags.HasErrors() {
		return nil, diags.NonFatalErr()
	}

	// The provider API expects our legacy ResourceConfig type, so we'll need
	// to shim here.
	rc := NewResourceConfigShimmed(configVal, configSchema)

	warns, errs := provider.Validate(rc)
	if len(warns) == 0 && len(errs) == 0 {
		return nil, nil
	}

	// FIXME: Once provider.Validate itself returns diagnostics, just
	// return diags.NonFatalErr() immediately here.
	for _, warn := range warns {
		diags = diags.Append(tfdiags.SimpleWarning(warn))
	}
	for _, err := range errs {
		diags = diags.Append(err)
	}

	return nil, diags.NonFatalErr()
}

// EvalValidateProvisioner is an EvalNode implementation that validates
// the configuration of a provisioner belonging to a resource.
type EvalValidateProvisioner struct {
	ResourceAddr addrs.ResourceInstance
	Provisioner  *ResourceProvisioner
	Schema       **configschema.Block
	Config       *configs.Provisioner
	ConnConfig   *configs.Connection
}

func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
	provisioner := *n.Provisioner
	config := *n.Config
	schema := *n.Schema

	var warns []string
	var errs []error

	var diags tfdiags.Diagnostics

	{
		// Validate the provisioner's own config first

		configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, n.ResourceAddr, n.ResourceAddr.Key)
		diags = diags.Append(configDiags)
		if configDiags.HasErrors() {
			return nil, diags.Err()
		}

		if configVal == cty.NilVal {
			// Should never happen for a well-behaved EvaluateBlock implementation
			return nil, fmt.Errorf("EvaluateBlock returned nil value")
		}

		// The provisioner API still uses our legacy ResourceConfig type, so
		// we need to shim it.
		legacyRC := NewResourceConfigShimmed(configVal, schema)

		w, e := provisioner.Validate(legacyRC)
		warns = append(warns, w...)
		errs = append(errs, e...)

		// FIXME: Once the provisioner API itself returns diagnostics, just
		// return diags.NonFatalErr() here.
		for _, warn := range warns {
			diags = diags.Append(tfdiags.SimpleWarning(warn))
		}
		for _, err := range errs {
			diags = diags.Append(err)
		}
	}

	{
		// Now validate the connection config, which might either be from
		// the provisioner block itself or inherited from the resource's
		// shared connection info.
		connDiags := n.validateConnConfig(ctx, n.ConnConfig, n.ResourceAddr)
		diags = diags.Append(connDiags)
	}

	return nil, diags.NonFatalErr()
}

func (n *EvalValidateProvisioner) validateConnConfig(ctx EvalContext, config *configs.Connection, self addrs.Referenceable) tfdiags.Diagnostics {
	// We can't comprehensively validate the connection config since its
	// final structure is decided by the communicator and we can't instantiate
	// that until we have a complete instance state. However, we *can* catch
	// configuration keys that are not valid for *any* communicator, catching
	// typos early rather than waiting until we actually try to run one of
	// the resource's provisioners.

	var diags tfdiags.Diagnostics

	if config == nil || config.Config == nil {
		// No block to validate
		return diags
	}

	// We evaluate here just by evaluating the block and returning any
	// diagnostics we get, since evaluation alone is enough to check for
	// extraneous arguments and incorrectly-typed arguments.
	_, _, configDiags := ctx.EvaluateBlock(config.Config, connectionBlockSupersetSchema, self, n.ResourceAddr.Key)
	diags = diags.Append(configDiags)

	return diags
}

// connectionBlockSupersetSchema is a schema representing the superset of all
// possible arguments for "connection" blocks across all supported connection
// types.
//
// This currently lives here because we've not yet updated our communicator
// subsystem to be aware of schema itself. Once that is done, we can remove
// this and use a type-specific schema from the communicator to validate
// exactly what is expected for a given connection type.
var connectionBlockSupersetSchema = &configschema.Block{
	Attributes: map[string]*configschema.Attribute{
		// NOTE: "type" is not included here because it's treated special
		// by the config loader and stored away in a separate field.

		// Common attributes for both connection types
		"user": {
			Type:     cty.String,
			Required: false,
		},
		"password": {
			Type:     cty.String,
			Required: false,
		},
		"host": {
			Type:     cty.String,
			Required: false,
		},
		"port": {
			Type:     cty.Number,
			Required: false,
		},
		"timeout": {
			Type:     cty.String,
			Required: false,
		},
		"script_path": {
			Type:     cty.String,
			Required: false,
		},

		// For type=ssh only (enforced in ssh communicator)
		"private_key": {
			Type:     cty.String,
			Required: false,
		},
		"host_key": {
			Type:     cty.String,
			Required: false,
		},
		"agent": {
			Type:     cty.Bool,
			Required: false,
		},
		"agent_identity": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_host": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_host_key": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_port": {
			Type:     cty.Number,
			Required: false,
		},
		"bastion_user": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_password": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_private_key": {
			Type:     cty.String,
			Required: false,
		},

		// For type=winrm only (enforced in winrm communicator)
		"https": {
			Type:     cty.Bool,
			Required: false,
		},
		"insecure": {
			Type:     cty.Bool,
			Required: false,
		},
		"cacert": {
			Type:     cty.String,
			Required: false,
		},
		"use_ntlm": {
			Type:     cty.Bool,
			Required: false,
		},
	},
}

// EvalValidateResource is an EvalNode implementation that validates
// the configuration of a resource.
type EvalValidateResource struct {
	Addr           addrs.ResourceInstance
	Provider       *ResourceProvider
	ProviderSchema **ProviderSchema
	Config         *configs.Resource

	// IgnoreWarnings means that warnings will not be passed through. This allows
	// "just-in-time" passes of validation to continue execution through warnings.
	IgnoreWarnings bool

	// ConfigVal, if non-nil, will be updated with the value resulting from
	// evaluating the given configuration body. Since validation is performed
	// very early, this value is likely to contain lots of unknown values,
	// but its type will conform to the schema of the resource type associated
	// with the resource instance being validated.
	ConfigVal *cty.Value
}

func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
	if n.ProviderSchema == nil || *n.ProviderSchema == nil {
		return nil, fmt.Errorf("EvalValidateResource has nil schema for %s", n.Addr)
	}

	var diags tfdiags.Diagnostics
	provider := *n.Provider
	cfg := *n.Config
	schema := *n.ProviderSchema
	mode := cfg.Mode

	var warns []string
	var errs []error

	// Provider entry point varies depending on resource mode, because
	// managed resources and data resources are two distinct concepts
	// in the provider abstraction.
	switch mode {
	case addrs.ManagedResourceMode:
		schema, exists := schema.ResourceTypes[cfg.Type]
		if !exists {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid resource type",
				Detail:   fmt.Sprintf("The provider %s does not support resource type %q.", cfg.ProviderConfigAddr(), cfg.Type),
				Subject:  &cfg.TypeRange,
			})
			return nil, diags.Err()
		}

		configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, n.Addr.Key)
		diags = diags.Append(valDiags)
		if valDiags.HasErrors() {
			return nil, diags.Err()
		}

		// The provider API still expects our legacy types, so we must do some
		// shimming here.
		legacyCfg := NewResourceConfigShimmed(configVal, schema)
		warns, errs = provider.ValidateResource(cfg.Type, legacyCfg)

		if n.ConfigVal != nil {
			*n.ConfigVal = configVal
		}

	case addrs.DataResourceMode:
		schema, exists := schema.DataSources[cfg.Type]
		if !exists {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid data source",
				Detail:   fmt.Sprintf("The provider %s does not support data source %q.", cfg.ProviderConfigAddr(), cfg.Type),
				Subject:  &cfg.TypeRange,
			})
			return nil, diags.Err()
		}

		configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, n.Addr.Key)
		diags = diags.Append(valDiags)
		if valDiags.HasErrors() {
			return nil, diags.Err()
		}

		// The provider API still expects our legacy types, so we must do some
		// shimming here.
		legacyCfg := NewResourceConfigShimmed(configVal, schema)
		warns, errs = provider.ValidateDataSource(cfg.Type, legacyCfg)

		if n.ConfigVal != nil {
			*n.ConfigVal = configVal
		}
	}

	// FIXME: Update the provider API to actually return diagnostics here,
	// and then we can remove all this shimming and use its diagnostics
	// directly.
	for _, warn := range warns {
		diags = diags.Append(tfdiags.SimpleWarning(warn))
	}
	for _, err := range errs {
		diags = diags.Append(err)
	}

	if n.IgnoreWarnings {
		// If we _only_ have warnings then we'll return nil.
		if diags.HasErrors() {
			return nil, diags.NonFatalErr()
		}
		return nil, nil
	} else {
		// We'll return an error if there are any diagnostics at all, even if
		// some of them are warnings.
		return nil, diags.NonFatalErr()
	}
}
<commit_msg>core: EvalValidateProvider must always call provider.Validate<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/config/configschema"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/tfdiags"
	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/gocty"
)

// EvalValidateCount is an EvalNode implementation that validates
// the count of a resource.
type EvalValidateCount struct {
	Resource *configs.Resource
}

// TODO: test
func (n *EvalValidateCount) Eval(ctx EvalContext) (interface{}, error) {
	var diags tfdiags.Diagnostics
	var count int
	var err error

	val, valDiags := ctx.EvaluateExpr(n.Resource.Count, cty.Number, nil)
	diags = diags.Append(valDiags)
	if valDiags.HasErrors() {
		goto RETURN
	}
	if val.IsNull() || !val.IsKnown() {
		goto RETURN
	}

	err = gocty.FromCtyValue(val, &count)
	if err != nil {
		// The EvaluateExpr call above already guaranteed us a number value,
		// so if we end up here then we have something that is out of range
		// for an int, and the error message will include a description of
		// the valid range.
		rawVal := val.AsBigFloat()
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid count value",
			Detail:   fmt.Sprintf("The number %s is not a valid count value: %s.", rawVal, err),
			Subject:  n.Resource.Count.Range().Ptr(),
		})
	} else if count < 0 {
		rawVal := val.AsBigFloat()
		diags = diags.Append(&hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Invalid count value",
			Detail:   fmt.Sprintf("The number %s is not a valid count value: count must not be negative.", rawVal),
			Subject:  n.Resource.Count.Range().Ptr(),
		})
	}

RETURN:
	return nil, diags.NonFatalErr()
}

// EvalValidateProvider is an EvalNode implementation that validates
// a provider configuration.
type EvalValidateProvider struct {
	Addr     addrs.ProviderConfig
	Provider *ResourceProvider
	Config   *configs.Provider
}

func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) {
	var diags tfdiags.Diagnostics
	provider := *n.Provider

	var sourceBody hcl.Body
	if n.Config != nil && n.Config.Config != nil {
		sourceBody = n.Config.Config
	} else {
		// If the provider configuration is implicit (no block in configuration
		// but referred to by resources) then we'll assume an empty body
		// as a placeholder.
		sourceBody = hcl.EmptyBody()
	}

	schema, err := provider.GetSchema(&ProviderSchemaRequest{})
	if err != nil {
		diags = diags.Append(err)
		return nil, diags.NonFatalErr()
	}
	if schema == nil {
		return nil, fmt.Errorf("no schema is available for %s", n.Addr)
	}

	configSchema := schema.Provider
	configBody := buildProviderConfig(ctx, n.Addr, sourceBody)
	configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, addrs.NoKey)
	diags = diags.Append(evalDiags)
	if evalDiags.HasErrors() {
		return nil, diags.NonFatalErr()
	}

	// The provider API expects our legacy ResourceConfig type, so we'll need
	// to shim here.
	rc := NewResourceConfigShimmed(configVal, configSchema)

	warns, errs := provider.Validate(rc)
	if len(warns) == 0 && len(errs) == 0 {
		return nil, nil
	}

	// FIXME: Once provider.Validate itself returns diagnostics, just
	// return diags.NonFatalErr() immediately here.
	for _, warn := range warns {
		diags = diags.Append(tfdiags.SimpleWarning(warn))
	}
	for _, err := range errs {
		diags = diags.Append(err)
	}

	return nil, diags.NonFatalErr()
}

// EvalValidateProvisioner is an EvalNode implementation that validates
// the configuration of a provisioner belonging to a resource.
type EvalValidateProvisioner struct {
	ResourceAddr addrs.ResourceInstance
	Provisioner  *ResourceProvisioner
	Schema       **configschema.Block
	Config       *configs.Provisioner
	ConnConfig   *configs.Connection
}

func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) {
	provisioner := *n.Provisioner
	config := *n.Config
	schema := *n.Schema

	var warns []string
	var errs []error

	var diags tfdiags.Diagnostics

	{
		// Validate the provisioner's own config first

		configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, n.ResourceAddr, n.ResourceAddr.Key)
		diags = diags.Append(configDiags)
		if configDiags.HasErrors() {
			return nil, diags.Err()
		}

		if configVal == cty.NilVal {
			// Should never happen for a well-behaved EvaluateBlock implementation
			return nil, fmt.Errorf("EvaluateBlock returned nil value")
		}

		// The provisioner API still uses our legacy ResourceConfig type, so
		// we need to shim it.
		legacyRC := NewResourceConfigShimmed(configVal, schema)

		w, e := provisioner.Validate(legacyRC)
		warns = append(warns, w...)
		errs = append(errs, e...)

		// FIXME: Once the provisioner API itself returns diagnostics, just
		// return diags.NonFatalErr() here.
		for _, warn := range warns {
			diags = diags.Append(tfdiags.SimpleWarning(warn))
		}
		for _, err := range errs {
			diags = diags.Append(err)
		}
	}

	{
		// Now validate the connection config, which might either be from
		// the provisioner block itself or inherited from the resource's
		// shared connection info.
		connDiags := n.validateConnConfig(ctx, n.ConnConfig, n.ResourceAddr)
		diags = diags.Append(connDiags)
	}

	return nil, diags.NonFatalErr()
}

func (n *EvalValidateProvisioner) validateConnConfig(ctx EvalContext, config *configs.Connection, self addrs.Referenceable) tfdiags.Diagnostics {
	// We can't comprehensively validate the connection config since its
	// final structure is decided by the communicator and we can't instantiate
	// that until we have a complete instance state. However, we *can* catch
	// configuration keys that are not valid for *any* communicator, catching
	// typos early rather than waiting until we actually try to run one of
	// the resource's provisioners.

	var diags tfdiags.Diagnostics

	if config == nil || config.Config == nil {
		// No block to validate
		return diags
	}

	// We evaluate here just by evaluating the block and returning any
	// diagnostics we get, since evaluation alone is enough to check for
	// extraneous arguments and incorrectly-typed arguments.
	_, _, configDiags := ctx.EvaluateBlock(config.Config, connectionBlockSupersetSchema, self, n.ResourceAddr.Key)
	diags = diags.Append(configDiags)

	return diags
}

// connectionBlockSupersetSchema is a schema representing the superset of all
// possible arguments for "connection" blocks across all supported connection
// types.
//
// This currently lives here because we've not yet updated our communicator
// subsystem to be aware of schema itself. Once that is done, we can remove
// this and use a type-specific schema from the communicator to validate
// exactly what is expected for a given connection type.
var connectionBlockSupersetSchema = &configschema.Block{
	Attributes: map[string]*configschema.Attribute{
		// NOTE: "type" is not included here because it's treated special
		// by the config loader and stored away in a separate field.

		// Common attributes for both connection types
		"user": {
			Type:     cty.String,
			Required: false,
		},
		"password": {
			Type:     cty.String,
			Required: false,
		},
		"host": {
			Type:     cty.String,
			Required: false,
		},
		"port": {
			Type:     cty.Number,
			Required: false,
		},
		"timeout": {
			Type:     cty.String,
			Required: false,
		},
		"script_path": {
			Type:     cty.String,
			Required: false,
		},

		// For type=ssh only (enforced in ssh communicator)
		"private_key": {
			Type:     cty.String,
			Required: false,
		},
		"host_key": {
			Type:     cty.String,
			Required: false,
		},
		"agent": {
			Type:     cty.Bool,
			Required: false,
		},
		"agent_identity": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_host": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_host_key": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_port": {
			Type:     cty.Number,
			Required: false,
		},
		"bastion_user": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_password": {
			Type:     cty.String,
			Required: false,
		},
		"bastion_private_key": {
			Type:     cty.String,
			Required: false,
		},

		// For type=winrm only (enforced in winrm communicator)
		"https": {
			Type:     cty.Bool,
			Required: false,
		},
		"insecure": {
			Type:     cty.Bool,
			Required: false,
		},
		"cacert": {
			Type:     cty.String,
			Required: false,
		},
		"use_ntlm": {
			Type:     cty.Bool,
			Required: false,
		},
	},
}

// EvalValidateResource is an EvalNode implementation that validates
// the configuration of a resource.
type EvalValidateResource struct {
	Addr           addrs.ResourceInstance
	Provider       *ResourceProvider
	ProviderSchema **ProviderSchema
	Config         *configs.Resource

	// IgnoreWarnings means that warnings will not be passed through. This allows
	// "just-in-time" passes of validation to continue execution through warnings.
	IgnoreWarnings bool

	// ConfigVal, if non-nil, will be updated with the value resulting from
	// evaluating the given configuration body. Since validation is performed
	// very early, this value is likely to contain lots of unknown values,
	// but its type will conform to the schema of the resource type associated
	// with the resource instance being validated.
	ConfigVal *cty.Value
}

func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) {
	if n.ProviderSchema == nil || *n.ProviderSchema == nil {
		return nil, fmt.Errorf("EvalValidateResource has nil schema for %s", n.Addr)
	}

	var diags tfdiags.Diagnostics
	provider := *n.Provider
	cfg := *n.Config
	schema := *n.ProviderSchema
	mode := cfg.Mode

	var warns []string
	var errs []error

	// Provider entry point varies depending on resource mode, because
	// managed resources and data resources are two distinct concepts
	// in the provider abstraction.
	switch mode {
	case addrs.ManagedResourceMode:
		schema, exists := schema.ResourceTypes[cfg.Type]
		if !exists {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid resource type",
				Detail:   fmt.Sprintf("The provider %s does not support resource type %q.", cfg.ProviderConfigAddr(), cfg.Type),
				Subject:  &cfg.TypeRange,
			})
			return nil, diags.Err()
		}

		configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, n.Addr.Key)
		diags = diags.Append(valDiags)
		if valDiags.HasErrors() {
			return nil, diags.Err()
		}

		// The provider API still expects our legacy types, so we must do some
		// shimming here.
		legacyCfg := NewResourceConfigShimmed(configVal, schema)
		warns, errs = provider.ValidateResource(cfg.Type, legacyCfg)

		if n.ConfigVal != nil {
			*n.ConfigVal = configVal
		}

	case addrs.DataResourceMode:
		schema, exists := schema.DataSources[cfg.Type]
		if !exists {
			diags = diags.Append(&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Invalid data source",
				Detail:   fmt.Sprintf("The provider %s does not support data source %q.", cfg.ProviderConfigAddr(), cfg.Type),
				Subject:  &cfg.TypeRange,
			})
			return nil, diags.Err()
		}

		configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, n.Addr.Key)
		diags = diags.Append(valDiags)
		if valDiags.HasErrors() {
			return nil, diags.Err()
		}

		// The provider API still expects our legacy types, so we must do some
		// shimming here.
		legacyCfg := NewResourceConfigShimmed(configVal, schema)
		warns, errs = provider.ValidateDataSource(cfg.Type, legacyCfg)

		if n.ConfigVal != nil {
			*n.ConfigVal = configVal
		}
	}

	// FIXME: Update the provider API to actually return diagnostics here,
	// and then we can remove all this shimming and use its diagnostics
	// directly.
	for _, warn := range warns {
		diags = diags.Append(tfdiags.SimpleWarning(warn))
	}
	for _, err := range errs {
		diags = diags.Append(err)
	}

	if n.IgnoreWarnings {
		// If we _only_ have warnings then we'll return nil.
		if diags.HasErrors() {
			return nil, diags.NonFatalErr()
		}
		return nil, nil
	} else {
		// We'll return an error if there are any diagnostics at all, even if
		// some of them are warnings.
		return nil, diags.NonFatalErr()
	}
}
<|endoftext|>


<commit_before>package bigip

const (
	uriClientSSL       = "client-ssl"
	uriDatagroup       = "data-group"
	uriHttp            = "http"
	uriHttpCompression = "http-compression"
	uriIRule           = "rule"
	uriInternal        = "internal"
	uriLtm             = "ltm"
	uriMonitor         = "monitor"
	uriNode            = "node"
	uriOneConnect      = "one-connect"
	uriPolicy          = "policy"
	uriPool            = "pool"
	uriPoolMember      = "members"
	uriProfile         = "profile"
	uriServerSSL       = "server-ssl"
	uriSnatPool        = "snatpool"
	uriTcp             = "tcp"
	uriUdp             = "udp"
	uriVirtual         = "virtual"
	uriVirtualAddress  = "virtual-address"
	uriGtm             = "gtm"
	uriWideIp          = "wideip"
	uriARecord         = "a"
	uriAAAARecord      = "aaaa"
	uriCNameRecord     = "cname"
	uriMXRecord        = "mx"
	uriNaptrRecord     = "naptr"
	uriSrvRecord       = "srv"
	ENABLED            = "enable"
	DISABLED           = "disable"
	CONTEXT_SERVER     = "serverside"
	CONTEXT_CLIENT     = "clientside"
	CONTEXT_ALL        = "all"

	// Newer policy APIs have a draft-publish workflow that this library does not support.
	policyVersionSuffix = "?ver=11.5.1"
)

var cidr = map[string]string{
	"0":  "0.0.0.0",
	"1":  "128.0.0.0",
	"2":  "192.0.0.0",
	"3":  "224.0.0.0",
	"4":  "240.0.0.0",
	"5":  "248.0.0.0",
	"6":  "252.0.0.0",
	"7":  "254.0.0.0",
	"8":  "255.0.0.0",
	"9":  "255.128.0.0",
	"10": "255.192.0.0",
	"11": "255.224.0.0",
	"12": "255.240.0.0",
	"13": "255.248.0.0",
	"14": "255.252.0.0",
	"15": "255.254.0.0",
	"16": "255.255.0.0",
	"17": "255.255.128.0",
	"18": "255.255.192.0",
	"19": "255.255.224.0",
	"20": "255.255.240.0",
	"21": "255.255.248.0",
	"22": "255.255.252.0",
	"23": "255.255.254.0",
	"24": "255.255.255.0",
	"25": "255.255.255.128",
	"26": "255.255.255.192",
	"27": "255.255.255.224",
	"28": "255.255.255.240",
	"29": "255.255.255.248",
	"30": "255.255.255.252",
	"31": "255.255.255.254",
	"32": "255.255.255.255",
}

type gtmType string

const (
	aRecord     gtmType = uriARecord
	aaaaRecord  gtmType = uriAAAARecord
	cnameRecord gtmType = uriCNameRecord
	mxRecord    gtmType = uriMXRecord
	naptrRecord gtmType = uriNaptrRecord
	srvRecord   gtmType = uriSrvRecord
)
<commit_msg>Updates record types 'enum' to be Public<commit_after>package bigip

const (
	uriClientSSL       = "client-ssl"
	uriDatagroup       = "data-group"
	uriHttp            = "http"
	uriHttpCompression = "http-compression"
	uriIRule           = "rule"
	uriInternal        = "internal"
	uriLtm             = "ltm"
	uriMonitor         = "monitor"
	uriNode            = "node"
	uriOneConnect      = "one-connect"
	uriPolicy          = "policy"
	uriPool            = "pool"
	uriPoolMember      = "members"
	uriProfile         = "profile"
	uriServerSSL       = "server-ssl"
	uriSnatPool        = "snatpool"
	uriTcp             = "tcp"
	uriUdp             = "udp"
	uriVirtual         = "virtual"
	uriVirtualAddress  = "virtual-address"
	uriGtm             = "gtm"
	uriWideIp          = "wideip"
	uriARecord         = "a"
	uriAAAARecord      = "aaaa"
	uriCNameRecord     = "cname"
	uriMXRecord        = "mx"
	uriNaptrRecord     = "naptr"
	uriSrvRecord       = "srv"
	ENABLED            = "enable"
	DISABLED           = "disable"
	CONTEXT_SERVER     = "serverside"
	CONTEXT_CLIENT     = "clientside"
	CONTEXT_ALL        = "all"

	// Newer policy APIs have a draft-publish workflow that this library does not support.
	policyVersionSuffix = "?ver=11.5.1"
)

var cidr = map[string]string{
	"0":  "0.0.0.0",
	"1":  "128.0.0.0",
	"2":  "192.0.0.0",
	"3":  "224.0.0.0",
	"4":  "240.0.0.0",
	"5":  "248.0.0.0",
	"6":  "252.0.0.0",
	"7":  "254.0.0.0",
	"8":  "255.0.0.0",
	"9":  "255.128.0.0",
	"10": "255.192.0.0",
	"11": "255.224.0.0",
	"12": "255.240.0.0",
	"13": "255.248.0.0",
	"14": "255.252.0.0",
	"15": "255.254.0.0",
	"16": "255.255.0.0",
	"17": "255.255.128.0",
	"18": "255.255.192.0",
	"19": "255.255.224.0",
	"20": "255.255.240.0",
	"21": "255.255.248.0",
	"22": "255.255.252.0",
	"23": "255.255.254.0",
	"24": "255.255.255.0",
	"25": "255.255.255.128",
	"26": "255.255.255.192",
	"27": "255.255.255.224",
	"28": "255.255.255.240",
	"29": "255.255.255.248",
	"30": "255.255.255.252",
	"31": "255.255.255.254",
	"32": "255.255.255.255",
}

type gtmType string

// Record Types
const (
	ARecord     gtmType = uriARecord
	AAAARecord  gtmType = uriAAAARecord
	CNAMERecord gtmType = uriCNameRecord
	MXRecord    gtmType = uriMXRecord
	NAPTRRecord gtmType = uriNaptrRecord
	SRVRecord   gtmType = uriSrvRecord
)
<|endoftext|>


<commit_before>// Package main ...
package main

import (
	"encoding/json"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/microplatform-io/platform"
)

func getDefaultPublisher(connectionManager *platform.AmqpConnectionManager) platform.Publisher {
	publisher, err := platform.NewAmqpPublisher(connectionManager)
	if err != nil {
		log.Fatalf("Could not create publisher. %s", err)
	}

	return publisher
}

func getDefaultSubscriber(connectionManager *platform.AmqpConnectionManager, queue string) platform.Subscriber {
	subscriber, err := platform.NewAmqpSubscriber(connectionManager, queue)
	if err != nil {
		log.Fatalf("Could not create subscriber. %s", err)
	}

	return subscriber
}

func manageRouterState(routerConfigList *platform.RouterConfigList) {
	//routerConfigListBytes, _ := platform.Marshal(routerConfigList)

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	// Emit a router offline signal if we catch an interrupt
	go func() {
		select {
		case <-sigc:
			//publisher.Publish("router.offline", routerConfigListBytes)
			os.Exit(0)
		}
	}()

	// Wait for the servers to come online, and then repeat the router.online every 30 seconds
	time.AfterFunc(10*time.Second, func() {
		//publisher.Publish("router.online", routerConfigListBytes)

		for {
			time.Sleep(30 * time.Second)
			//publisher.Publish("router.online", routerConfigListBytes)
		}
	})
}

// ErrorResponse - Used by handlers so we can return back errors
func ErrorResponse(msg string, opts ...map[string]interface{}) (resp []byte) {
	resp, _ = json.Marshal(map[string]string{
		"message": msg,
	})
	return
}
<commit_msg>Adding better error handling. We're now passing proto error (hex) back instead of json<commit_after>// Package main ...
package main

import (
	"encoding/hex"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/microplatform-io/platform"
)

func getDefaultPublisher(connectionManager *platform.AmqpConnectionManager) platform.Publisher {
	publisher, err := platform.NewAmqpPublisher(connectionManager)
	if err != nil {
		log.Fatalf("Could not create publisher. %s", err)
	}

	return publisher
}

func getDefaultSubscriber(connectionManager *platform.AmqpConnectionManager, queue string) platform.Subscriber {
	subscriber, err := platform.NewAmqpSubscriber(connectionManager, queue)
	if err != nil {
		log.Fatalf("Could not create subscriber. %s", err)
	}

	return subscriber
}

func manageRouterState(routerConfigList *platform.RouterConfigList) {
	//routerConfigListBytes, _ := platform.Marshal(routerConfigList)

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	// Emit a router offline signal if we catch an interrupt
	go func() {
		select {
		case <-sigc:
			//publisher.Publish("router.offline", routerConfigListBytes)
			os.Exit(0)
		}
	}()

	// Wait for the servers to come online, and then repeat the router.online every 30 seconds
	time.AfterFunc(10*time.Second, func() {
		//publisher.Publish("router.online", routerConfigListBytes)

		for {
			time.Sleep(30 * time.Second)
			//publisher.Publish("router.online", routerConfigListBytes)
		}
	})
}

// GetProtoBytes -
func GetProtoBytes(message platform.Message) []byte {
	protoBytes, _ := platform.Marshal(message)
	return protoBytes
}

// ErrorResponse - Used by handlers so we can return back errors
func ErrorResponse(msg string) []byte {
	return []byte(hex.EncodeToString(GetProtoBytes(&platform.Request{
		Routing:   platform.RouteToUri("resource:///router-http/reply/error"),
		Payload:   GetProtoBytes(&platform.Error{Message: platform.String(msg)}),
		Completed: platform.Bool(true),
	})))
}
<|endoftext|>


<commit_before>// Package parse contains a collection of parsers for various formats in its subpackages.
package parse // import "github.com/tdewolff/parse"

import (
	"encoding/base64"
	"errors"
	"math"
	"net/url"
)

// Returned by DataURI when the byte slice does not start with 'data:' or is too short.
var ErrBadDataURI = errors.New("not a data URI")

// Number returns the number of bytes that parse as a number of the regex format (+|-)?([0-9]+(\.[0-9]+)?|\.[0-9]+)((e|E)(+|-)?[0-9]+)?.
func Number(b []byte) int {
	if len(b) == 0 {
		return 0
	}
	i := 0
	if b[i] == '+' || b[i] == '-' {
		i++
		if i >= len(b) {
			return 0
		}
	}
	firstDigit := (b[i] >= '0' && b[i] <= '9')
	if firstDigit {
		i++
		for i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
		}
	}
	if i < len(b) && b[i] == '.' {
		i++
		if i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
			for i < len(b) && b[i] >= '0' && b[i] <= '9' {
				i++
			}
		} else if firstDigit {
			// . could belong to the next token
			i--
			return i
		} else {
			return 0
		}
	} else if !firstDigit {
		return 0
	}
	iOld := i
	if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
		i++
		if i < len(b) && (b[i] == '+' || b[i] == '-') {
			i++
		}
		if i >= len(b) || b[i] < '0' || b[i] > '9' {
			// e could belong to next token
			return iOld
		}
		for i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
		}
	}
	return i
}

func Dimension(b []byte) (int, int) {
	num := Number(b)
	if num == 0 || num == len(b) {
		return num, 0
	} else if b[num] == '%' {
		return num, 1
	} else if b[num] >= 'a' && b[num] <= 'z' || b[num] >= 'A' && b[num] <= 'Z' {
		i := num + 1
		for i < len(b) && (b[i] >= 'a' && b[i] <= 'z' || b[i] >= 'A' && b[i] <= 'Z') {
			i++
		}
		return num, i - num
	}
	return num, 0
}

// Int parses a byte-slice and returns the integer it represents
func Int(b []byte) (int64, bool) {
	n := uint64(0)
	neg := false
	if len(b) > 0 && (b[0] == '+' || b[0] == '-') {
		neg = b[0] == '-'
		b = b[1:]
	}
	for _, c := range b {
		if n > math.MaxUint64/10 {
			return 0, false
		} else if c >= '0' && c <= '9' {
			n *= 10
			n += uint64(c - '0')
		} else {
			return 0, false
		}
	}
	if !neg && n > uint64(math.MaxInt64) || n > uint64(math.MaxInt64)+1 {
		return 0, false
	} else if neg {
		return -int64(n), true
	}
	return int64(n), true
}

// DataURI parses the given data URI and returns the mediatype, data and ok.
func DataURI(dataURI []byte) ([]byte, []byte, error) {
	if len(dataURI) > 5 && Equal(dataURI[:5], []byte("data:")) {
		dataURI = dataURI[5:]
		inBase64 := false
		mediatype := []byte{}
		i := 0
		for j, c := range dataURI {
			if c == '=' || c == ';' || c == ',' {
				if c != '=' && Equal(Trim(dataURI[i:j], IsWhitespace), []byte("base64")) {
					if len(mediatype) > 0 {
						mediatype = mediatype[:len(mediatype)-1]
					}
					inBase64 = true
					i = j
				} else if c != ',' {
					mediatype = append(append(mediatype, Trim(dataURI[i:j], IsWhitespace)...), c)
					i = j + 1
				} else {
					mediatype = append(mediatype, Trim(dataURI[i:j], IsWhitespace)...)
				}
				if c == ',' {
					if len(mediatype) == 0 || mediatype[0] == ';' {
						mediatype = []byte("text/plain")
					}
					data := dataURI[j+1:]
					if inBase64 {
						decoded := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
						n, err := base64.StdEncoding.Decode(decoded, data)
						if err != nil {
							return []byte{}, []byte{}, err
						}
						data = decoded[:n]
					} else if unescaped, err := url.QueryUnescape(string(data)); err == nil {
						data = []byte(unescaped)
					}
					return mediatype, data, nil
				}
			}
		}
	}
	return []byte{}, []byte{}, ErrBadDataURI
}

// QuoteEntity parses the given byte slice and returns the quote that got matched (' or "), its entity length and ok.
func QuoteEntity(b []byte) (quote byte, n int) {
	if len(b) < 5 || b[0] != '&' {
		return 0, 0
	}
	if b[1] == '#' {
		if b[2] == 'x' {
			i := 3
			for i < len(b) && b[i] == '0' {
				i++
			}
			if i+2 < len(b) && b[i] == '2' && b[i+2] == ';' {
				if b[i+1] == '2' {
					return '"', i + 3 // "
				} else if b[i+1] == '7' {
					return '\'', i + 3 // '
				}
			}
		} else {
			i := 2
			for i < len(b) && b[i] == '0' {
				i++
			}
			if i+2 < len(b) && b[i] == '3' && b[i+2] == ';' {
				if b[i+1] == '4' {
					return '"', i + 3 // "
				} else if b[i+1] == '9' {
					return '\'', i + 3 // '
				}
			}
		}
	} else if len(b) >= 6 && b[5] == ';' {
		if EqualFold(b[1:5], []byte{'q', 'u', 'o', 't'}) {
			return '"', 6 // "
		} else if EqualFold(b[1:5], []byte{'a', 'p', 'o', 's'}) {
			return '\'', 6 // '
		}
	}
	return 0, 0
}
<commit_msg>Comment fix<commit_after>// Package parse contains a collection of parsers for various formats in its subpackages.
package parse // import "github.com/tdewolff/parse"

import (
	"encoding/base64"
	"errors"
	"math"
	"net/url"
)

// Returned by DataURI when the byte slice does not start with 'data:' or is too short.
var ErrBadDataURI = errors.New("not a data URI")

// Number returns the number of bytes that parse as a number of the regex format (+|-)?([0-9]+(\.[0-9]+)?|\.[0-9]+)((e|E)(+|-)?[0-9]+)?.
func Number(b []byte) int {
	if len(b) == 0 {
		return 0
	}
	i := 0
	if b[i] == '+' || b[i] == '-' {
		i++
		if i >= len(b) {
			return 0
		}
	}
	firstDigit := (b[i] >= '0' && b[i] <= '9')
	if firstDigit {
		i++
		for i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
		}
	}
	if i < len(b) && b[i] == '.' {
		i++
		if i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
			for i < len(b) && b[i] >= '0' && b[i] <= '9' {
				i++
			}
		} else if firstDigit {
			// . could belong to the next token
			i--
			return i
		} else {
			return 0
		}
	} else if !firstDigit {
		return 0
	}
	iOld := i
	if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
		i++
		if i < len(b) && (b[i] == '+' || b[i] == '-') {
			i++
		}
		if i >= len(b) || b[i] < '0' || b[i] > '9' {
			// e could belong to next token
			return iOld
		}
		for i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
		}
	}
	return i
}

func Dimension(b []byte) (int, int) {
	num := Number(b)
	if num == 0 || num == len(b) {
		return num, 0
	} else if b[num] == '%' {
		return num, 1
	} else if b[num] >= 'a' && b[num] <= 'z' || b[num] >= 'A' && b[num] <= 'Z' {
		i := num + 1
		for i < len(b) && (b[i] >= 'a' && b[i] <= 'z' || b[i] >= 'A' && b[i] <= 'Z') {
			i++
		}
		return num, i - num
	}
	return num, 0
}

// Int parses a byte-slice and returns the integer it represents
func Int(b []byte) (int64, bool) {
	n := uint64(0)
	neg := false
	if len(b) > 0 && (b[0] == '+' || b[0] == '-') {
		neg = b[0] == '-'
		b = b[1:]
	}
	for _, c := range b {
		if n > math.MaxUint64/10 {
			return 0, false
		} else if c >= '0' && c <= '9' {
			n *= 10
			n += uint64(c - '0')
		} else {
			return 0, false
		}
	}
	if !neg && n > uint64(math.MaxInt64) || n > uint64(math.MaxInt64)+1 {
		return 0, false
	} else if neg {
		return -int64(n), true
	}
	return int64(n), true
}

// DataURI parses the given data URI and returns the mediatype, data and ok.
func DataURI(dataURI []byte) ([]byte, []byte, error) {
	if len(dataURI) > 5 && Equal(dataURI[:5], []byte("data:")) {
		dataURI = dataURI[5:]
		inBase64 := false
		mediatype := []byte{}
		i := 0
		for j, c := range dataURI {
			if c == '=' || c == ';' || c == ',' {
				if c != '=' && Equal(Trim(dataURI[i:j], IsWhitespace), []byte("base64")) {
					if len(mediatype) > 0 {
						mediatype = mediatype[:len(mediatype)-1]
					}
					inBase64 = true
					i = j
				} else if c != ',' {
					mediatype = append(append(mediatype, Trim(dataURI[i:j], IsWhitespace)...), c)
					i = j + 1
				} else {
					mediatype = append(mediatype, Trim(dataURI[i:j], IsWhitespace)...)
				}
				if c == ',' {
					if len(mediatype) == 0 || mediatype[0] == ';' {
						mediatype = []byte("text/plain")
					}
					data := dataURI[j+1:]
					if inBase64 {
						decoded := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
						n, err := base64.StdEncoding.Decode(decoded, data)
						if err != nil {
							return []byte{}, []byte{}, err
						}
						data = decoded[:n]
					} else if unescaped, err := url.QueryUnescape(string(data)); err == nil {
						data = []byte(unescaped)
					}
					return mediatype, data, nil
				}
			}
		}
	}
	return []byte{}, []byte{}, ErrBadDataURI
}

// QuoteEntity parses the given byte slice and returns the quote that got matched (' or ") and its entity length.
func QuoteEntity(b []byte) (quote byte, n int) {
	if len(b) < 5 || b[0] != '&' {
		return 0, 0
	}
	if b[1] == '#' {
		if b[2] == 'x' {
			i := 3
			for i < len(b) && b[i] == '0' {
				i++
			}
			if i+2 < len(b) && b[i] == '2' && b[i+2] == ';' {
				if b[i+1] == '2' {
					return '"', i + 3 // "
				} else if b[i+1] == '7' {
					return '\'', i + 3 // '
				}
			}
		} else {
			i := 2
			for i < len(b) && b[i] == '0' {
				i++
			}
			if i+2 < len(b) && b[i] == '3' && b[i+2] == ';' {
				if b[i+1] == '4' {
					return '"', i + 3 // "
				} else if b[i+1] == '9' {
					return '\'', i + 3 // '
				}
			}
		}
	} else if len(b) >= 6 && b[5] == ';' {
		if EqualFold(b[1:5], []byte{'q', 'u', 'o', 't'}) {
			return '"', 6 // "
		} else if EqualFold(b[1:5], []byte{'a', 'p', 'o', 's'}) {
			return '\'', 6 // '
		}
	}
	return 0, 0
}
<|endoftext|>


<commit_before>package QesyGo

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type RandWeiht struct {
	Name   string
	Weight int
}

type RandWeihtArr []RandWeiht

func Substr(str string, start int, end int) string {
	var endNum int
	s := []byte(str)
	if end > 0 {
		endNum = start + end
	} else {
		endNum = len(str) + end
	}
	return string(s[start:endNum])
}

func Rate(num int) bool {
	rand := rand.Intn(100) + 1
	if rand <= num {
		return true
	} else {
		return false
	}
}

/*
* RandWeihtArr := &lib.RandWeihtArr{{"user1",8}, {"user2",1},{"user3",1}}
* who := RandWeihtArr.RandWeight()
 */
func (arr *RandWeihtArr) RandWeight() string {
	var all int
	for _, v := range *arr {
		all += v.Weight
	}
	plusNum := 0
	tempArr := make(map[string][2]int)
	for _, v := range *arr {
		plusNum += v.Weight
		tempArr[v.Name] = [2]int{plusNum - v.Weight, plusNum}
	}
	randNum := rand.Intn(all) + 1
	var ret string
	for k, v := range tempArr {
		if randNum > v[0] && randNum <= v[1] {
			ret = k
			break
		}
	}
	return ret
}

func InArray(Arr interface{}, str string) bool {
	strArr, ok := Arr.([]string)
	if ok {
		for _, v := range strArr {
			if v == str {
				return true
			}
		}
		return false
	}
	mapArr, ok := Arr.(map[string]string)
	if ok {
		for _, v := range mapArr {
			if v == str {
				return true
			}
		}
		return false
	}
	intArr, ok := Arr.([]int)
	if ok {
		for _, v := range intArr {
			if strconv.Itoa(v) == str {
				return true
			}
		}
		return false
	}
	return false

}

func ReadFile(str string) ([]byte, error) {
	return ioutil.ReadFile(str)
}

func JsonEncode(arr interface{}) ([]byte, error) {
	return json.Marshal(arr)
}

func JsonDecode(str []byte, jsonArr interface{}) error {
	err := json.Unmarshal(str, jsonArr)
	return err

}

func Printf(format string, a ...interface{}) {
	fmt.Printf(format, a)
}

func Fprintf(w http.ResponseWriter, str string) {
	fmt.Fprintf(w, str)
}

func Die(v interface{}) {
	log.Fatal(v)
}

func Implode(arr []string, sep string) string {
	return strings.Join(arr, sep)
}

func Explode(str string, sep string) []string {
	return strings.Split(str, sep)
}

func Err(str string) error {
	return errors.New(str)
}

func Println(str ...interface{}) {
	fmt.Println(str)
}

func Time(str string) int64 {
	now := time.Now()
	t := now.UnixNano()
	switch str {
	case "Millisecond":
		t = now.UnixNano() / 1000
	case "Microsecond":
		t = now.UnixNano() / 1000000
	case "Second":
		t = now.UnixNano() / 1000000000
	}
	return t
}

//-- format : "2006-01-02 03:04:05 PM" --
/*
月份 1,01,Jan,January
日　 2,02,_2
时　 3,03,15,PM,pm,AM,am
分　 4,04
秒　 5,05
年　 06,2006
周几 Mon,Monday
时区时差表示 -07,-0700,Z0700,Z07:00,-07:00,MST
时区字母缩写 MST
*/
func Date(timestamp int64, format string) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format(format)
}

//-- "01/02/2006", "02/08/2015" --
func StrToTime(format string, input string) int64 {
	tm2, _ := time.Parse(format, input)
	return tm2.Unix()
}

func Int64ToInt(num int64) (int, error) {
	str := strconv.FormatInt(num, 10)
	return strconv.Atoi(str)
}

func Unset(arr []string, num int) []string {
	return append(arr[:num], arr[num+1:]...)
}
<commit_msg>unset fix bug<commit_after>package QesyGo

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type RandWeiht struct {
	Name   string
	Weight int
}

type RandWeihtArr []RandWeiht

func Substr(str string, start int, end int) string {
	var endNum int
	s := []byte(str)
	if end > 0 {
		endNum = start + end
	} else {
		endNum = len(str) + end
	}
	return string(s[start:endNum])
}

func Rate(num int) bool {
	rand := rand.Intn(100) + 1
	if rand <= num {
		return true
	} else {
		return false
	}
}

/*
* RandWeihtArr := &lib.RandWeihtArr{{"user1",8}, {"user2",1},{"user3",1}}
* who := RandWeihtArr.RandWeight()
 */
func (arr *RandWeihtArr) RandWeight() string {
	var all int
	for _, v := range *arr {
		all += v.Weight
	}
	plusNum := 0
	tempArr := make(map[string][2]int)
	for _, v := range *arr {
		plusNum += v.Weight
		tempArr[v.Name] = [2]int{plusNum - v.Weight, plusNum}
	}
	randNum := rand.Intn(all) + 1
	var ret string
	for k, v := range tempArr {
		if randNum > v[0] && randNum <= v[1] {
			ret = k
			break
		}
	}
	return ret
}

func InArray(Arr interface{}, str string) bool {
	strArr, ok := Arr.([]string)
	if ok {
		for _, v := range strArr {
			if v == str {
				return true
			}
		}
		return false
	}
	mapArr, ok := Arr.(map[string]string)
	if ok {
		for _, v := range mapArr {
			if v == str {
				return true
			}
		}
		return false
	}
	intArr, ok := Arr.([]int)
	if ok {
		for _, v := range intArr {
			if strconv.Itoa(v) == str {
				return true
			}
		}
		return false
	}
	return false

}

func ReadFile(str string) ([]byte, error) {
	return ioutil.ReadFile(str)
}

func JsonEncode(arr interface{}) ([]byte, error) {
	return json.Marshal(arr)
}

func JsonDecode(str []byte, jsonArr interface{}) error {
	err := json.Unmarshal(str, jsonArr)
	return err

}

func Printf(format string, a ...interface{}) {
	fmt.Printf(format, a)
}

func Fprintf(w http.ResponseWriter, str string) {
	fmt.Fprintf(w, str)
}

func Die(v interface{}) {
	log.Fatal(v)
}

func Implode(arr []string, sep string) string {
	return strings.Join(arr, sep)
}

func Explode(str string, sep string) []string {
	return strings.Split(str, sep)
}

func Err(str string) error {
	return errors.New(str)
}

func Println(str ...interface{}) {
	fmt.Println(str)
}

func Time(str string) int64 {
	now := time.Now()
	t := now.UnixNano()
	switch str {
	case "Millisecond":
		t = now.UnixNano() / 1000
	case "Microsecond":
		t = now.UnixNano() / 1000000
	case "Second":
		t = now.UnixNano() / 1000000000
	}
	return t
}

//-- format : "2006-01-02 03:04:05 PM" --
/*
月份 1,01,Jan,January
日　 2,02,_2
时　 3,03,15,PM,pm,AM,am
分　 4,04
秒　 5,05
年　 06,2006
周几 Mon,Monday
时区时差表示 -07,-0700,Z0700,Z07:00,-07:00,MST
时区字母缩写 MST
*/
func Date(timestamp int64, format string) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format(format)
}

//-- "01/02/2006", "02/08/2015" --
func StrToTime(format string, input string) int64 {
	tm2, _ := time.Parse(format, input)
	return tm2.Unix()
}

func Int64ToInt(num int64) (int, error) {
	str := strconv.FormatInt(num, 10)
	return strconv.Atoi(str)
}

func Unset(arr []string, str string) []string {
	newArr := []string{}
	for _, v := range arr {
		if v != str {
			newArr = append(newArr, v)
		}
	}
	return newArr
}
<|endoftext|>


<commit_before>package QesyGo

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type RandWeiht struct {
	Name   string
	Weight int
}

type RandWeihtArr []RandWeiht

func Substr(str string, start int, end int) string {
	var endNum int
	s := []byte(str)
	if end > 0 {
		endNum = start + end
	} else {
		endNum = len(str) + end
	}
	return string(s[start:endNum])
}

func Rand(Min int, Max int) int {
	tempNum := Max - Min
	if tempNum <= 0 {
		return Min
	}
	randSeek := time.Now().UnixNano()
	rand.Seed(randSeek)
	return Min + rand.Intn(tempNum)
}

func Rate(num int) bool {
	rand := Rand(1, 100)
	if rand <= num {
		return true
	} else {
		return false
	}
}

/*
* RandWeihtArr := &lib.RandWeihtArr{{"user1",8}, {"user2",1},{"user3",1}}
* who := RandWeihtArr.RandWeight()
 */
func (arr *RandWeihtArr) RandWeight() string {
	var all int
	for _, v := range *arr {
		all += v.Weight
	}
	plusNum := 0
	tempArr := make(map[string][2]int)
	for _, v := range *arr {
		plusNum += v.Weight
		tempArr[v.Name] = [2]int{plusNum - v.Weight, plusNum}
	}
	randNum := Rand(0, all) + 1
	var ret string
	for k, v := range tempArr {
		if randNum > v[0] && randNum <= v[1] {
			ret = k
			break
		}
	}
	return ret
}

func ReadFile(str string) ([]byte, error) {
	return ioutil.ReadFile(str)
}

func JsonEncode(arr interface{}) ([]byte, error) {
	return json.Marshal(arr)
}

func JsonDecode(str []byte, jsonArr interface{}) error {
	strNew := string(str)
	if strNew == "null" || strNew == "" {
		return nil
	}
	err := json.Unmarshal(str, jsonArr)
	return err

}

func Printf(format string, a ...interface{}) {
	fmt.Printf(format, a)
}

func Fprintf(w http.ResponseWriter, str string) {
	fmt.Fprintf(w, str)
}

func Die(v interface{}) {
	log.Fatal(v)
}

func Implode(arr []string, sep string) string {
	return strings.Join(arr, sep)
}

func Explode(str string, sep string) []string {
	if str == "" {
		return []string{}
	}
	return strings.Split(str, sep)
}

func Err(str string) error {
	return errors.New(str)
}

func Println(str ...interface{}) {
	fmt.Println(str)
}

func Time(str string) int64 {
	now := time.Now()
	t := now.UnixNano()
	switch str {
	case "Microsecond":
		t = now.UnixNano() / 1000
	case "Millisecond":
		t = now.UnixNano() / 1000000
	case "Second":
		t = now.UnixNano() / 1000000000
	}
	return t
}

func TimeStr(str string) string {
	t := Time(str)
	return strconv.FormatInt(t, 10)
}

func TimeInt(str string) int {
	t := Time(str)
	ret, _ := Int64ToInt(t)
	return ret
}

//-- format : "2006-01-02 03:04:05 PM" --
/*
月份 1,01,Jan,January
日　 2,02,_2
时　 3,03,15,PM,pm,AM,am
分　 4,04
秒　 5,05
年　 06,2006
周几 Mon,Monday
时区时差表示 -07,-0700,Z0700,Z07:00,-07:00,MST
时区字母缩写 MST
*/
func Date(timestamp int64, format string) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format(format)
}

//-- "01/02/2006", "02/08/2015" --
func StrToTimeByDate(format string, input string) int64 {
	tm2, _ := time.Parse(format, input)
	return tm2.Unix()
}

func StrToTime(format string, input string) int64 {
	loc, _ := time.LoadLocation("Local")
	theTime, _ := time.ParseInLocation(format, input, loc)
	return theTime.Unix()
}

func Int64ToInt(num int64) (int, error) {
	str := strconv.FormatInt(num, 10)
	return strconv.Atoi(str)
}

func StrToInt(str string) int {
	ret, _ := strconv.Atoi(str)
	return ret
}

func StrToInt32(str string) int32 {
	return int32(StrToInt(str))
}

func Unset(arr []string, str string) []string {
	newArr := []string{}
	for _, v := range arr {
		if v != str && v != "" {
			newArr = append(newArr, v)
		}
	}
	return newArr
}

//-- kind 0:纯数字，1：小写，2：大写，3：数字+大小写字幕 --
func Krand(size int, kind int) []byte {
	ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
	is_all := kind > 2 || kind < 0
	for i := 0; i < size; i++ {
		if is_all { // random ikind
			ikind = Rand(1, 3)
		}
		scope, base := kinds[ikind][0], kinds[ikind][1]
		result[i] = uint8(base + Rand(1, scope))
	}
	return result
}

func Wordwrap(Str string, width int, breakStr string) string {
	breakStrByte := []byte(breakStr)
	strByte := []byte(Str)
	var tempByte []byte
	for k, v := range strByte {
		if (k+1)%width == 0 && k != len(strByte)-1 {
			tempByte = append(tempByte, v)
			tempByte = append(tempByte, breakStrByte...)
		} else {
			tempByte = append(tempByte, v)
		}
	}
	return string(tempByte)
}

func Base64Encode(json string) string {
	b64 := base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
	return b64.EncodeToString([]byte(json))
}

func Base64Decode(json string) ([]byte, error) {
	b64 := base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
	return b64.DecodeString(json)
}

func Abs(a int) (ret int) {
	ret = (a ^ a>>31) - a>>31
	return
}

func Md5(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

func IntToBytes(x int32) []byte {
	b_buf := bytes.NewBuffer([]byte{})
	binary.Write(b_buf, binary.LittleEndian, x)
	return b_buf.Bytes()
}

func BytesToInt(b []byte) int32 {
	var x int32
	b_buf := bytes.NewBuffer(b)
	binary.Read(b_buf, binary.LittleEndian, &x)
	return x
}
<commit_msg>add IntToString<commit_after>package QesyGo

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type RandWeiht struct {
	Name   string
	Weight int
}

type RandWeihtArr []RandWeiht

func Substr(str string, start int, end int) string {
	var endNum int
	s := []byte(str)
	if end > 0 {
		endNum = start + end
	} else {
		endNum = len(str) + end
	}
	return string(s[start:endNum])
}

func Rand(Min int, Max int) int {
	tempNum := Max - Min
	if tempNum <= 0 {
		return Min
	}
	randSeek := time.Now().UnixNano()
	rand.Seed(randSeek)
	return Min + rand.Intn(tempNum)
}

func Rate(num int) bool {
	rand := Rand(1, 100)
	if rand <= num {
		return true
	} else {
		return false
	}
}

/*
* RandWeihtArr := &lib.RandWeihtArr{{"user1",8}, {"user2",1},{"user3",1}}
* who := RandWeihtArr.RandWeight()
 */
func (arr *RandWeihtArr) RandWeight() string {
	var all int
	for _, v := range *arr {
		all += v.Weight
	}
	plusNum := 0
	tempArr := make(map[string][2]int)
	for _, v := range *arr {
		plusNum += v.Weight
		tempArr[v.Name] = [2]int{plusNum - v.Weight, plusNum}
	}
	randNum := Rand(0, all) + 1
	var ret string
	for k, v := range tempArr {
		if randNum > v[0] && randNum <= v[1] {
			ret = k
			break
		}
	}
	return ret
}

func ReadFile(str string) ([]byte, error) {
	return ioutil.ReadFile(str)
}

func JsonEncode(arr interface{}) ([]byte, error) {
	return json.Marshal(arr)
}

func JsonDecode(str []byte, jsonArr interface{}) error {
	strNew := string(str)
	if strNew == "null" || strNew == "" {
		return nil
	}
	err := json.Unmarshal(str, jsonArr)
	return err

}

func Printf(format string, a ...interface{}) {
	fmt.Printf(format, a)
}

func Fprintf(w http.ResponseWriter, str string) {
	fmt.Fprintf(w, str)
}

func Die(v interface{}) {
	log.Fatal(v)
}

func Implode(arr []string, sep string) string {
	return strings.Join(arr, sep)
}

func Explode(str string, sep string) []string {
	if str == "" {
		return []string{}
	}
	return strings.Split(str, sep)
}

func Err(str string) error {
	return errors.New(str)
}

func Println(str ...interface{}) {
	fmt.Println(str)
}

func Time(str string) int64 {
	now := time.Now()
	t := now.UnixNano()
	switch str {
	case "Microsecond":
		t = now.UnixNano() / 1000
	case "Millisecond":
		t = now.UnixNano() / 1000000
	case "Second":
		t = now.UnixNano() / 1000000000
	}
	return t
}

func TimeStr(str string) string {
	t := Time(str)
	return strconv.FormatInt(t, 10)
}

func TimeInt(str string) int {
	t := Time(str)
	ret, _ := Int64ToInt(t)
	return ret
}

//-- format : "2006-01-02 03:04:05 PM" --
/*
月份 1,01,Jan,January
日　 2,02,_2
时　 3,03,15,PM,pm,AM,am
分　 4,04
秒　 5,05
年　 06,2006
周几 Mon,Monday
时区时差表示 -07,-0700,Z0700,Z07:00,-07:00,MST
时区字母缩写 MST
*/
func Date(timestamp int64, format string) string {
	tm := time.Unix(timestamp, 0)
	return tm.Format(format)
}

//-- "01/02/2006", "02/08/2015" --
func StrToTimeByDate(format string, input string) int64 {
	tm2, _ := time.Parse(format, input)
	return tm2.Unix()
}

func StrToTime(format string, input string) int64 {
	loc, _ := time.LoadLocation("Local")
	theTime, _ := time.ParseInLocation(format, input, loc)
	return theTime.Unix()
}

func Int64ToInt(num int64) (int, error) {
	str := strconv.FormatInt(num, 10)
	return strconv.Atoi(str)
}

func IntToStr(a interface{}) string {
	return fmt.Sprintf("%d", a)
}

func StrToInt(str string) int {
	ret, _ := strconv.Atoi(str)
	return ret
}

func StrToInt32(str string) int32 {
	return int32(StrToInt(str))
}

func Unset(arr []string, str string) []string {
	newArr := []string{}
	for _, v := range arr {
		if v != str && v != "" {
			newArr = append(newArr, v)
		}
	}
	return newArr
}

//-- kind 0:纯数字，1：小写，2：大写，3：数字+大小写字幕 --
func Krand(size int, kind int) []byte {
	ikind, kinds, result := kind, [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}, make([]byte, size)
	is_all := kind > 2 || kind < 0
	for i := 0; i < size; i++ {
		if is_all { // random ikind
			ikind = Rand(1, 3)
		}
		scope, base := kinds[ikind][0], kinds[ikind][1]
		result[i] = uint8(base + Rand(1, scope))
	}
	return result
}

func Wordwrap(Str string, width int, breakStr string) string {
	breakStrByte := []byte(breakStr)
	strByte := []byte(Str)
	var tempByte []byte
	for k, v := range strByte {
		if (k+1)%width == 0 && k != len(strByte)-1 {
			tempByte = append(tempByte, v)
			tempByte = append(tempByte, breakStrByte...)
		} else {
			tempByte = append(tempByte, v)
		}
	}
	return string(tempByte)
}

func Base64Encode(json string) string {
	b64 := base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
	return b64.EncodeToString([]byte(json))
}

func Base64Decode(json string) ([]byte, error) {
	b64 := base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
	return b64.DecodeString(json)
}

func Abs(a int) (ret int) {
	ret = (a ^ a>>31) - a>>31
	return
}

func Md5(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

func IntToBytes(x int32) []byte {
	b_buf := bytes.NewBuffer([]byte{})
	binary.Write(b_buf, binary.LittleEndian, x)
	return b_buf.Bytes()
}

func BytesToInt(b []byte) int32 {
	var x int32
	b_buf := bytes.NewBuffer(b)
	binary.Read(b_buf, binary.LittleEndian, &x)
	return x
}
<|endoftext|>


<commit_before>// +build e2e

/*
Copyright 2018 The Knative 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 e2e

import (
	"fmt"
	"net/http"
	"strings"
	"sync/atomic"
	"testing"
	"time"

	_ "github.com/knative/pkg/system/testing"
	pkgTest "github.com/knative/pkg/test"
	"github.com/knative/serving/test"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"
	appsv1 "k8s.io/api/apps/v1"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	autoscaleExpectedOutput = "399989"
)

func isDeploymentScaledUp() func(d *appsv1.Deployment) (bool, error) {
	return func(d *appsv1.Deployment) (bool, error) {
		return d.Status.ReadyReplicas > 1, nil
	}
}

func tearDown(ctx *testContext) {
	test.TearDown(ctx.clients, ctx.names)
}

func generateTraffic(ctx *testContext, concurrency int, duration time.Duration, stopChan chan struct{}) error {
	var (
		totalRequests      int32
		successfulRequests int32
		group              errgroup.Group
	)

	ctx.t.Logf("Maintaining %d concurrent requests for %v.", concurrency, duration)
	client, err := pkgTest.NewSpoofingClient(ctx.clients.KubeClient, ctx.t.Logf, ctx.domain, test.ServingFlags.ResolvableDomain)
	if err != nil {
		return fmt.Errorf("error creating spoofing client: %v", err)
	}
	for i := 0; i < concurrency; i++ {
		group.Go(func() error {
			req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s", ctx.domain), nil)
			if err != nil {
				return fmt.Errorf("error creating HTTP request: %v", err)
			}
			done := time.After(duration)
			for {
				select {
				case <-stopChan:
					ctx.t.Log("Stopping generateTraffic")
					return nil
				case <-done:
					ctx.t.Log("Time is up; done")
					return nil
				default:
					atomic.AddInt32(&totalRequests, 1)
					res, err := client.Do(req)
					if err != nil {
						ctx.t.Logf("Error making request %v", err)
						continue
					}

					if res.StatusCode != http.StatusOK {
						ctx.t.Logf("Status = %d, want: %d", res.StatusCode, http.StatusOK)
						ctx.t.Logf("Response: %s", res)
						continue
					}
					atomic.AddInt32(&successfulRequests, 1)
				}
			}
		})
	}

	ctx.t.Log("Waiting for all requests to complete.")
	if err := group.Wait(); err != nil {
		return fmt.Errorf("error making requests for scale up: %v", err)
	}

	if successfulRequests != totalRequests {
		return fmt.Errorf("error making requests for scale up. Got %d successful requests, wanted: %d",
			successfulRequests, totalRequests)
	}
	return nil
}

type testContext struct {
	t              *testing.T
	clients        *test.Clients
	names          test.ResourceNames
	deploymentName string
	domain         string
}

func setup(t *testing.T) *testContext {
	clients := Setup(t)

	t.Log("Creating a new Route and Configuration")
	names, err := CreateRouteAndConfig(t, clients, "autoscale", &test.Options{
		ContainerConcurrency: 10,
	})
	if err != nil {
		t.Fatalf("Failed to create Route and Configuration: %v", err)
	}
	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })

	t.Log("When the Revision can have traffic routed to it, the Route is marked as Ready.")
	if err := test.WaitForRouteState(
		clients.ServingClient,
		names.Route,
		test.IsRouteReady,
		"RouteIsReady"); err != nil {
		t.Fatalf("The Route %s was not marked as Ready to serve traffic: %v", names.Route, err)
	}

	t.Log("Served the expected data at the endpoint")
	config, err := clients.ServingClient.Configs.Get(names.Config, metav1.GetOptions{})
	if err != nil {
		t.Fatalf("Configuration %s was not updated with the new revision: %v", names.Config, err)
	}
	names.Revision = config.Status.LatestCreatedRevisionName
	deploymentName := names.Revision + "-deployment"
	route, err := clients.ServingClient.Routes.Get(names.Route, metav1.GetOptions{})
	if err != nil {
		t.Fatalf("Error fetching Route %s: %v", names.Route, err)
	}
	domain := route.Status.Domain

	if _, err := pkgTest.WaitForEndpointState(
		clients.KubeClient,
		t.Logf,
		domain,
		// Istio doesn't expose a status for us here: https://github.com/istio/istio/issues/6082
		// TODO(tcnghia): Remove this when https://github.com/istio/istio/issues/882 is fixed.
		test.RetryingRouteInconsistency(pkgTest.MatchesAllOf(pkgTest.IsStatusOK, pkgTest.EventuallyMatchesBody(autoscaleExpectedOutput))),
		"CheckingEndpointAfterUpdating",
		test.ServingFlags.ResolvableDomain); err != nil {
		t.Fatalf("The endpoint for Route %s at domain %s didn't serve the expected text \"%v\": %v",
			names.Route, domain, autoscaleExpectedOutput, err)
	}

	t.Logf("Revision under test: %s", names.Revision)

	// verify that revision is consistent and is ready
	if err := test.CheckRevisionState(clients.ServingClient, names.Revision, test.IsRevisionReady); err != nil {
		t.Fatalf("The Revision %s is not in ready state: %v", names.Revision, err)
	}

	return &testContext{
		t:              t,
		clients:        clients,
		names:          names,
		deploymentName: deploymentName,
		domain:         domain,
	}
}

func assertScaleUp(ctx *testContext) {
	ctx.t.Log("The autoscaler spins up additional replicas when traffic increases.")
	if err := generateTraffic(ctx, 20, 20*time.Second, nil); err != nil {
		ctx.t.Fatalf("Error during initial scale up: %v", err)
	}
	ctx.t.Logf("Waiting for scale up revision %s", ctx.names.Revision)
	if err := pkgTest.WaitForDeploymentState(
		ctx.clients.KubeClient,
		ctx.deploymentName,
		isDeploymentScaledUp(),
		"DeploymentIsScaledUp",
		test.ServingNamespace,
		2*time.Minute); err != nil {
		ctx.t.Fatalf("Unable to observe the Deployment named %s scaling up. %s", ctx.deploymentName, err)
	}
}

func assertScaleDown(ctx *testContext) {
	if err := WaitForScaleToZero(ctx.t, ctx.deploymentName, ctx.clients); err != nil {
		ctx.t.Fatalf("Unable to observe the Deployment named %s scaling down: %v", ctx.deploymentName, err)
	}

	// Account for the case where scaling up uses all available pods.
	ctx.t.Log("Wait for all pods to terminate.")

	if err := pkgTest.WaitForPodListState(
		ctx.clients.KubeClient,
		func(p *v1.PodList) (bool, error) {
			for _, pod := range p.Items {
				if strings.Contains(pod.Name, ctx.deploymentName) &&
					!strings.Contains(pod.Status.Reason, "Evicted") {
					return false, nil
				}
			}
			return true, nil
		},
		"WaitForAvailablePods", test.ServingNamespace); err != nil {
		ctx.t.Fatalf("Waiting for Pod.List to have no non-Evicted pods of %q: %v", ctx.deploymentName, err)
	}

	ctx.t.Log("The Revision should remain ready after scaling to zero.")
	if err := test.CheckRevisionState(ctx.clients.ServingClient, ctx.names.Revision, test.IsRevisionReady); err != nil {
		ctx.t.Fatalf("The Revision %s did not stay Ready after scaling down to zero: %v", ctx.names.Revision, err)
	}

	ctx.t.Log("Scaled down.")
}

func TestAutoscaleUpDownUp(t *testing.T) {
	t.Parallel()
	ctx := setup(t)
	stopChan := DiagnoseMeEvery(t, 15*time.Second, ctx.clients)
	defer close(stopChan)
	defer test.TearDown(ctx.clients, ctx.names)

	assertScaleUp(ctx)
	assertScaleDown(ctx)
	assertScaleUp(ctx)
}

func assertNumberOfPods(ctx *testContext, numReplicasMin int32, numReplicasMax int32) error {
	deployment, err := ctx.clients.KubeClient.Kube.Apps().Deployments(test.ServingNamespace).Get(ctx.deploymentName, metav1.GetOptions{})
	if err != nil {
		return errors.Wrapf(err, "Failed to get deployment %q", deployment)
	}
	gotReplicas := deployment.Status.ReadyReplicas
	mes := fmt.Sprintf("got %d replicas, expected between [%d, %d] replicas for deployment %s", gotReplicas, numReplicasMin, numReplicasMax, ctx.deploymentName)
	ctx.t.Log(mes)
	if gotReplicas < numReplicasMin || gotReplicas > numReplicasMax {
		return errors.New(mes)
	}
	return nil
}

func assertAutoscaleUpToNumPods(ctx *testContext, numPods int32) {
	// Relaxing the pod count requirement a little bit to avoid being too flaky.
	minPods := numPods - 1
	maxPods := numPods + 1

	stopChan := make(chan struct{})
	var grp errgroup.Group
	grp.Go(func() error {
		return generateTraffic(ctx, int(numPods*10), 60*time.Second, stopChan)
	})
	grp.Go(func() error {
		// Short-circuit traffic generation once we exit from the check logic.
		defer close(stopChan)

		timer := time.Tick(2 * time.Second)
		for {
			select {
			case <-timer:
				if err := assertNumberOfPods(ctx, minPods, maxPods); err != nil {
					return err
				}
				if err := assertNumberOfPods(ctx, numPods, maxPods); err == nil {
					return nil
				}
			}
		}
	})

	if err := grp.Wait(); err != nil {
		ctx.t.Error(err)
	}
}

func TestAutoscaleUpCountPods(t *testing.T) {
	t.Parallel()
	ctx := setup(t)
	defer test.TearDown(ctx.clients, ctx.names)

	ctx.t.Log("The autoscaler spins up additional replicas when traffic increases.")
	// note: without the warm-up / gradual increase of load the test is retrieving a 503 (overload) from the envoy

	// Increase workload for 2 replicas for 60s
	// Assert the number of expected replicas is between n-1 and n+1, where n is the # of desired replicas for 60s.
	// Assert the number of expected replicas is n and n+1 at the end of 60s, where n is the # of desired replicas.
	assertAutoscaleUpToNumPods(ctx, 2)
	// Increase workload scale to 3 replicas, assert between [n-1, n+1] during scale up, assert between [n, n+1] after scaleup.
	assertAutoscaleUpToNumPods(ctx, 3)
	// Increase workload scale to 4 replicas, assert between [n-1, n+1] during scale up, assert between [n, n+1] after scaleup.
	assertAutoscaleUpToNumPods(ctx, 4)

}
<commit_msg>Simplify the autoscale test by using test helpers. (#3945)<commit_after>// +build e2e

/*
Copyright 2018 The Knative 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 e2e

import (
	"fmt"
	"net/http"
	"strings"
	"sync/atomic"
	"testing"
	"time"

	_ "github.com/knative/pkg/system/testing"
	pkgTest "github.com/knative/pkg/test"
	resourcenames "github.com/knative/serving/pkg/reconciler/revision/resources/names"
	"github.com/knative/serving/test"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"
	appsv1 "k8s.io/api/apps/v1"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	autoscaleExpectedOutput = "399989"
)

func isDeploymentScaledUp() func(d *appsv1.Deployment) (bool, error) {
	return func(d *appsv1.Deployment) (bool, error) {
		return d.Status.ReadyReplicas > 1, nil
	}
}

func tearDown(ctx *testContext) {
	test.TearDown(ctx.clients, ctx.names)
}

func generateTraffic(ctx *testContext, concurrency int, duration time.Duration, stopChan chan struct{}) error {
	var (
		totalRequests      int32
		successfulRequests int32
		group              errgroup.Group
	)

	ctx.t.Logf("Maintaining %d concurrent requests for %v.", concurrency, duration)
	client, err := pkgTest.NewSpoofingClient(ctx.clients.KubeClient, ctx.t.Logf, ctx.domain, test.ServingFlags.ResolvableDomain)
	if err != nil {
		return fmt.Errorf("error creating spoofing client: %v", err)
	}
	for i := 0; i < concurrency; i++ {
		group.Go(func() error {
			req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s", ctx.domain), nil)
			if err != nil {
				return fmt.Errorf("error creating HTTP request: %v", err)
			}
			done := time.After(duration)
			for {
				select {
				case <-stopChan:
					ctx.t.Log("Stopping generateTraffic")
					return nil
				case <-done:
					ctx.t.Log("Time is up; done")
					return nil
				default:
					atomic.AddInt32(&totalRequests, 1)
					res, err := client.Do(req)
					if err != nil {
						ctx.t.Logf("Error making request %v", err)
						continue
					}

					if res.StatusCode != http.StatusOK {
						ctx.t.Logf("Status = %d, want: %d", res.StatusCode, http.StatusOK)
						ctx.t.Logf("Response: %s", res)
						continue
					}
					atomic.AddInt32(&successfulRequests, 1)
				}
			}
		})
	}

	ctx.t.Log("Waiting for all requests to complete.")
	if err := group.Wait(); err != nil {
		return fmt.Errorf("error making requests for scale up: %v", err)
	}

	if successfulRequests != totalRequests {
		return fmt.Errorf("error making requests for scale up. Got %d successful requests, wanted: %d",
			successfulRequests, totalRequests)
	}
	return nil
}

type testContext struct {
	t              *testing.T
	clients        *test.Clients
	names          test.ResourceNames
	deploymentName string
	domain         string
}

func setup(t *testing.T) *testContext {
	clients := Setup(t)

	t.Log("Creating a new Route and Configuration")
	names := test.ResourceNames{
		Service: test.ObjectNameForTest(t),
		Image:   "autoscale",
	}
	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
	resources, err := test.CreateRunLatestServiceReady(t, clients, &names, &test.Options{
		ContainerConcurrency: 10,
	})
	if err != nil {
		t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err)
	}

	domain := resources.Route.Status.Domain
	if _, err := pkgTest.WaitForEndpointState(
		clients.KubeClient,
		t.Logf,
		domain,
		// Istio doesn't expose a status for us here: https://github.com/istio/istio/issues/6082
		// TODO(tcnghia): Remove this when https://github.com/istio/istio/issues/882 is fixed.
		test.RetryingRouteInconsistency(pkgTest.MatchesAllOf(pkgTest.IsStatusOK, pkgTest.EventuallyMatchesBody(autoscaleExpectedOutput))),
		"CheckingEndpointAfterUpdating",
		test.ServingFlags.ResolvableDomain); err != nil {
		t.Fatalf("The endpoint for Route %s at domain %s didn't serve the expected text \"%v\": %v",
			names.Route, domain, autoscaleExpectedOutput, err)
	}

	return &testContext{
		t:              t,
		clients:        clients,
		names:          names,
		deploymentName: resourcenames.Deployment(resources.Revision),
		domain:         domain,
	}
}

func assertScaleUp(ctx *testContext) {
	ctx.t.Log("The autoscaler spins up additional replicas when traffic increases.")
	if err := generateTraffic(ctx, 20, 20*time.Second, nil); err != nil {
		ctx.t.Fatalf("Error during initial scale up: %v", err)
	}
	ctx.t.Logf("Waiting for scale up revision %s", ctx.names.Revision)
	if err := pkgTest.WaitForDeploymentState(
		ctx.clients.KubeClient,
		ctx.deploymentName,
		isDeploymentScaledUp(),
		"DeploymentIsScaledUp",
		test.ServingNamespace,
		2*time.Minute); err != nil {
		ctx.t.Fatalf("Unable to observe the Deployment named %s scaling up. %s", ctx.deploymentName, err)
	}
}

func assertScaleDown(ctx *testContext) {
	if err := WaitForScaleToZero(ctx.t, ctx.deploymentName, ctx.clients); err != nil {
		ctx.t.Fatalf("Unable to observe the Deployment named %s scaling down: %v", ctx.deploymentName, err)
	}

	// Account for the case where scaling up uses all available pods.
	ctx.t.Log("Wait for all pods to terminate.")

	if err := pkgTest.WaitForPodListState(
		ctx.clients.KubeClient,
		func(p *v1.PodList) (bool, error) {
			for _, pod := range p.Items {
				if strings.Contains(pod.Name, ctx.deploymentName) &&
					!strings.Contains(pod.Status.Reason, "Evicted") {
					return false, nil
				}
			}
			return true, nil
		},
		"WaitForAvailablePods", test.ServingNamespace); err != nil {
		ctx.t.Fatalf("Waiting for Pod.List to have no non-Evicted pods of %q: %v", ctx.deploymentName, err)
	}

	ctx.t.Log("The Revision should remain ready after scaling to zero.")
	if err := test.CheckRevisionState(ctx.clients.ServingClient, ctx.names.Revision, test.IsRevisionReady); err != nil {
		ctx.t.Fatalf("The Revision %s did not stay Ready after scaling down to zero: %v", ctx.names.Revision, err)
	}

	ctx.t.Log("Scaled down.")
}

func TestAutoscaleUpDownUp(t *testing.T) {
	t.Parallel()
	ctx := setup(t)
	stopChan := DiagnoseMeEvery(t, 15*time.Second, ctx.clients)
	defer close(stopChan)
	defer test.TearDown(ctx.clients, ctx.names)

	assertScaleUp(ctx)
	assertScaleDown(ctx)
	assertScaleUp(ctx)
}

func assertNumberOfPods(ctx *testContext, numReplicasMin int32, numReplicasMax int32) error {
	deployment, err := ctx.clients.KubeClient.Kube.Apps().Deployments(test.ServingNamespace).Get(ctx.deploymentName, metav1.GetOptions{})
	if err != nil {
		return errors.Wrapf(err, "Failed to get deployment %q", deployment)
	}
	gotReplicas := deployment.Status.ReadyReplicas
	mes := fmt.Sprintf("got %d replicas, expected between [%d, %d] replicas for deployment %s", gotReplicas, numReplicasMin, numReplicasMax, ctx.deploymentName)
	ctx.t.Log(mes)
	if gotReplicas < numReplicasMin || gotReplicas > numReplicasMax {
		return errors.New(mes)
	}
	return nil
}

func assertAutoscaleUpToNumPods(ctx *testContext, numPods int32) {
	// Relaxing the pod count requirement a little bit to avoid being too flaky.
	minPods := numPods - 1
	maxPods := numPods + 1

	stopChan := make(chan struct{})
	var grp errgroup.Group
	grp.Go(func() error {
		return generateTraffic(ctx, int(numPods*10), 60*time.Second, stopChan)
	})
	grp.Go(func() error {
		// Short-circuit traffic generation once we exit from the check logic.
		defer close(stopChan)

		timer := time.Tick(2 * time.Second)
		for {
			select {
			case <-timer:
				if err := assertNumberOfPods(ctx, minPods, maxPods); err != nil {
					return err
				}
				if err := assertNumberOfPods(ctx, numPods, maxPods); err == nil {
					return nil
				}
			}
		}
	})

	if err := grp.Wait(); err != nil {
		ctx.t.Error(err)
	}
}

func TestAutoscaleUpCountPods(t *testing.T) {
	t.Parallel()
	ctx := setup(t)
	defer test.TearDown(ctx.clients, ctx.names)

	ctx.t.Log("The autoscaler spins up additional replicas when traffic increases.")
	// note: without the warm-up / gradual increase of load the test is retrieving a 503 (overload) from the envoy

	// Increase workload for 2 replicas for 60s
	// Assert the number of expected replicas is between n-1 and n+1, where n is the # of desired replicas for 60s.
	// Assert the number of expected replicas is n and n+1 at the end of 60s, where n is the # of desired replicas.
	assertAutoscaleUpToNumPods(ctx, 2)
	// Increase workload scale to 3 replicas, assert between [n-1, n+1] during scale up, assert between [n, n+1] after scaleup.
	assertAutoscaleUpToNumPods(ctx, 3)
	// Increase workload scale to 4 replicas, assert between [n-1, n+1] during scale up, assert between [n, n+1] after scaleup.
	assertAutoscaleUpToNumPods(ctx, 4)

}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"fmt"
	"os/exec"

	"k8s.io/kubernetes/test/e2e/framework"
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"

	"github.com/onsi/ginkgo"
)

var _ = framework.KubeDescribe("GKE node pools [Feature:GKENodePool]", func() {

	f := framework.NewDefaultFramework("node-pools")

	ginkgo.BeforeEach(func() {
		framework.SkipUnlessProviderIs("gke")
	})

	ginkgo.It("should create a cluster with multiple node pools [Feature:GKENodePool]", func() {
		e2elog.Logf("Start create node pool test")
		testCreateDeleteNodePool(f, "test-pool")
	})
})

func testCreateDeleteNodePool(f *framework.Framework, poolName string) {
	e2elog.Logf("Create node pool: %q in cluster: %q", poolName, framework.TestContext.CloudConfig.Cluster)

	clusterStr := fmt.Sprintf("--cluster=%s", framework.TestContext.CloudConfig.Cluster)

	out, err := exec.Command("gcloud", "container", "node-pools", "create",
		poolName,
		clusterStr,
		"--num-nodes=2").CombinedOutput()
	e2elog.Logf("\n%s", string(out))
	if err != nil {
		e2elog.Failf("Failed to create node pool %q. Err: %v\n%v", poolName, err, string(out))
	}
	e2elog.Logf("Successfully created node pool %q.", poolName)

	out, err = exec.Command("gcloud", "container", "node-pools", "list",
		clusterStr).CombinedOutput()
	if err != nil {
		e2elog.Failf("Failed to list node pools from cluster %q. Err: %v\n%v", framework.TestContext.CloudConfig.Cluster, err, string(out))
	}
	e2elog.Logf("Node pools:\n%s", string(out))

	e2elog.Logf("Checking that 2 nodes have the correct node pool label.")
	nodeCount := nodesWithPoolLabel(f, poolName)
	if nodeCount != 2 {
		e2elog.Failf("Wanted 2 nodes with node pool label, got: %v", nodeCount)
	}
	e2elog.Logf("Success, found 2 nodes with correct node pool labels.")

	e2elog.Logf("Deleting node pool: %q in cluster: %q", poolName, framework.TestContext.CloudConfig.Cluster)
	out, err = exec.Command("gcloud", "container", "node-pools", "delete",
		poolName,
		clusterStr,
		"-q").CombinedOutput()
	e2elog.Logf("\n%s", string(out))
	if err != nil {
		e2elog.Failf("Failed to delete node pool %q. Err: %v\n%v", poolName, err, string(out))
	}
	e2elog.Logf("Successfully deleted node pool %q.", poolName)

	out, err = exec.Command("gcloud", "container", "node-pools", "list",
		clusterStr).CombinedOutput()
	if err != nil {
		e2elog.Failf("\nFailed to list node pools from cluster %q. Err: %v\n%v", framework.TestContext.CloudConfig.Cluster, err, string(out))
	}
	e2elog.Logf("\nNode pools:\n%s", string(out))

	e2elog.Logf("Checking that no nodes have the deleted node pool's label.")
	nodeCount = nodesWithPoolLabel(f, poolName)
	if nodeCount != 0 {
		e2elog.Failf("Wanted 0 nodes with node pool label, got: %v", nodeCount)
	}
	e2elog.Logf("Success, found no nodes with the deleted node pool's label.")
}

// nodesWithPoolLabel returns the number of nodes that have the "gke-nodepool"
// label with the given node pool name.
func nodesWithPoolLabel(f *framework.Framework, poolName string) int {
	nodeCount := 0
	nodeList := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
	for _, node := range nodeList.Items {
		if poolLabel := node.Labels["cloud.google.com/gke-nodepool"]; poolLabel == poolName {
			nodeCount++
		}
	}
	return nodeCount
}
<commit_msg>fix test/e2e/gke_node_pools.go<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"fmt"
	"os/exec"

	"k8s.io/kubernetes/test/e2e/framework"

	"github.com/onsi/ginkgo"
)

var _ = framework.KubeDescribe("GKE node pools [Feature:GKENodePool]", func() {

	f := framework.NewDefaultFramework("node-pools")

	ginkgo.BeforeEach(func() {
		framework.SkipUnlessProviderIs("gke")
	})

	ginkgo.It("should create a cluster with multiple node pools [Feature:GKENodePool]", func() {
		framework.Logf("Start create node pool test")
		testCreateDeleteNodePool(f, "test-pool")
	})
})

func testCreateDeleteNodePool(f *framework.Framework, poolName string) {
	framework.Logf("Create node pool: %q in cluster: %q", poolName, framework.TestContext.CloudConfig.Cluster)

	clusterStr := fmt.Sprintf("--cluster=%s", framework.TestContext.CloudConfig.Cluster)

	out, err := exec.Command("gcloud", "container", "node-pools", "create",
		poolName,
		clusterStr,
		"--num-nodes=2").CombinedOutput()
	framework.Logf("\n%s", string(out))
	if err != nil {
		framework.Failf("Failed to create node pool %q. Err: %v\n%v", poolName, err, string(out))
	}
	framework.Logf("Successfully created node pool %q.", poolName)

	out, err = exec.Command("gcloud", "container", "node-pools", "list",
		clusterStr).CombinedOutput()
	if err != nil {
		framework.Failf("Failed to list node pools from cluster %q. Err: %v\n%v", framework.TestContext.CloudConfig.Cluster, err, string(out))
	}
	framework.Logf("Node pools:\n%s", string(out))

	framework.Logf("Checking that 2 nodes have the correct node pool label.")
	nodeCount := nodesWithPoolLabel(f, poolName)
	if nodeCount != 2 {
		framework.Failf("Wanted 2 nodes with node pool label, got: %v", nodeCount)
	}
	framework.Logf("Success, found 2 nodes with correct node pool labels.")

	framework.Logf("Deleting node pool: %q in cluster: %q", poolName, framework.TestContext.CloudConfig.Cluster)
	out, err = exec.Command("gcloud", "container", "node-pools", "delete",
		poolName,
		clusterStr,
		"-q").CombinedOutput()
	framework.Logf("\n%s", string(out))
	if err != nil {
		framework.Failf("Failed to delete node pool %q. Err: %v\n%v", poolName, err, string(out))
	}
	framework.Logf("Successfully deleted node pool %q.", poolName)

	out, err = exec.Command("gcloud", "container", "node-pools", "list",
		clusterStr).CombinedOutput()
	if err != nil {
		framework.Failf("\nFailed to list node pools from cluster %q. Err: %v\n%v", framework.TestContext.CloudConfig.Cluster, err, string(out))
	}
	framework.Logf("\nNode pools:\n%s", string(out))

	framework.Logf("Checking that no nodes have the deleted node pool's label.")
	nodeCount = nodesWithPoolLabel(f, poolName)
	if nodeCount != 0 {
		framework.Failf("Wanted 0 nodes with node pool label, got: %v", nodeCount)
	}
	framework.Logf("Success, found no nodes with the deleted node pool's label.")
}

// nodesWithPoolLabel returns the number of nodes that have the "gke-nodepool"
// label with the given node pool name.
func nodesWithPoolLabel(f *framework.Framework, poolName string) int {
	nodeCount := 0
	nodeList := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
	for _, node := range nodeList.Items {
		if poolLabel := node.Labels["cloud.google.com/gke-nodepool"]; poolLabel == poolName {
			nodeCount++
		}
	}
	return nodeCount
}
<|endoftext|>