fix vendoring
This commit is contained in:
parent
da0ee96cdd
commit
77c3c12244
|
@ -2685,6 +2685,14 @@ var awsPartition = partition{
|
|||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"emr-containers": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"entitlement.marketplace": service{
|
||||
Defaults: endpoint{
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -2843,6 +2851,18 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"fips-af-south-1": endpoint{
|
||||
Hostname: "fms-fips.af-south-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "af-south-1",
|
||||
},
|
||||
},
|
||||
"fips-ap-east-1": endpoint{
|
||||
Hostname: "fms-fips.ap-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-east-1",
|
||||
},
|
||||
},
|
||||
"fips-ap-northeast-1": endpoint{
|
||||
Hostname: "fms-fips.ap-northeast-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -2885,6 +2905,12 @@ var awsPartition = partition{
|
|||
Region: "eu-central-1",
|
||||
},
|
||||
},
|
||||
"fips-eu-south-1": endpoint{
|
||||
Hostname: "fms-fips.eu-south-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-south-1",
|
||||
},
|
||||
},
|
||||
"fips-eu-west-1": endpoint{
|
||||
Hostname: "fms-fips.eu-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -2903,6 +2929,12 @@ var awsPartition = partition{
|
|||
Region: "eu-west-3",
|
||||
},
|
||||
},
|
||||
"fips-me-south-1": endpoint{
|
||||
Hostname: "fms-fips.me-south-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "me-south-1",
|
||||
},
|
||||
},
|
||||
"fips-sa-east-1": endpoint{
|
||||
Hostname: "fms-fips.sa-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -3224,6 +3256,14 @@ var awsPartition = partition{
|
|||
},
|
||||
},
|
||||
},
|
||||
"healthlake": service{
|
||||
Defaults: endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"us-east-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"honeycode": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
@ -4701,6 +4741,18 @@ var awsPartition = partition{
|
|||
},
|
||||
},
|
||||
},
|
||||
"profile": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"projects.iot1click": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
|
@ -6786,12 +6838,36 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "xray-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "xray-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "xray-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "xray-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -9359,6 +9435,18 @@ var awsusgovPartition = partition{
|
|||
"xray": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"fips-us-gov-east-1": endpoint{
|
||||
Hostname: "xray-fips.us-gov-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-gov-west-1": endpoint{
|
||||
Hostname: "xray-fips.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
"us-gov-east-1": endpoint{},
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
// +build go1.7
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Transport that should be used when a custom CA bundle is specified with the
|
||||
// SDK.
|
||||
func getCABundleTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
// +build !go1.6,go1.5
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Transport that should be used when a custom CA bundle is specified with the
|
||||
// SDK.
|
||||
func getCABundleTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// +build !go1.7,go1.6
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Transport that should be used when a custom CA bundle is specified with the
|
||||
// SDK.
|
||||
func getCABundleTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
}
|
|
@ -208,6 +208,8 @@ env values as well.
|
|||
|
||||
AWS_SDK_LOAD_CONFIG=1
|
||||
|
||||
Custom Shared Config and Credential Files
|
||||
|
||||
Shared credentials file path can be set to instruct the SDK to use an alternative
|
||||
file for the shared credentials. If not set the file will be loaded from
|
||||
$HOME/.aws/credentials on Linux/Unix based systems, and
|
||||
|
@ -222,6 +224,8 @@ $HOME/.aws/config on Linux/Unix based systems, and
|
|||
|
||||
AWS_CONFIG_FILE=$HOME/my_shared_config
|
||||
|
||||
Custom CA Bundle
|
||||
|
||||
Path to a custom Credentials Authority (CA) bundle PEM file that the SDK
|
||||
will use instead of the default system's root CA bundle. Use this only
|
||||
if you want to replace the CA bundle the SDK uses for TLS requests.
|
||||
|
@ -242,6 +246,29 @@ Setting a custom HTTPClient in the aws.Config options will override this setting
|
|||
To use this option and custom HTTP client, the HTTP client needs to be provided
|
||||
when creating the session. Not the service client.
|
||||
|
||||
Custom Client TLS Certificate
|
||||
|
||||
The SDK supports the environment and session option being configured with
|
||||
Client TLS certificates that are sent as a part of the client's TLS handshake
|
||||
for client authentication. If used, both Cert and Key values are required. If
|
||||
one is missing, or either fail to load the contents of the file an error will
|
||||
be returned.
|
||||
|
||||
HTTP Client's Transport concrete implementation must be a http.Transport
|
||||
or creating the session will fail.
|
||||
|
||||
AWS_SDK_GO_CLIENT_TLS_KEY=$HOME/my_client_key
|
||||
AWS_SDK_GO_CLIENT_TLS_CERT=$HOME/my_client_cert
|
||||
|
||||
This can also be configured via the session.Options ClientTLSCert and ClientTLSKey.
|
||||
|
||||
sess, err := session.NewSessionWithOptions(session.Options{
|
||||
ClientTLSCert: myCertFile,
|
||||
ClientTLSKey: myKeyFile,
|
||||
})
|
||||
|
||||
Custom EC2 IMDS Endpoint
|
||||
|
||||
The endpoint of the EC2 IMDS client can be configured via the environment
|
||||
variable, AWS_EC2_METADATA_SERVICE_ENDPOINT when creating the client with a
|
||||
Session. See Options.EC2IMDSEndpoint for more details.
|
||||
|
|
|
@ -101,6 +101,18 @@ type envConfig struct {
|
|||
// AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
|
||||
CustomCABundle string
|
||||
|
||||
// Sets the TLC client certificate that should be used by the SDK's HTTP transport
|
||||
// when making requests. The certificate must be paired with a TLS client key file.
|
||||
//
|
||||
// AWS_SDK_GO_CLIENT_TLS_CERT=$HOME/my_client_cert
|
||||
ClientTLSCert string
|
||||
|
||||
// Sets the TLC client key that should be used by the SDK's HTTP transport
|
||||
// when making requests. The key must be paired with a TLS client certificate file.
|
||||
//
|
||||
// AWS_SDK_GO_CLIENT_TLS_KEY=$HOME/my_client_key
|
||||
ClientTLSKey string
|
||||
|
||||
csmEnabled string
|
||||
CSMEnabled *bool
|
||||
CSMPort string
|
||||
|
@ -219,6 +231,15 @@ var (
|
|||
ec2IMDSEndpointEnvKey = []string{
|
||||
"AWS_EC2_METADATA_SERVICE_ENDPOINT",
|
||||
}
|
||||
useCABundleKey = []string{
|
||||
"AWS_CA_BUNDLE",
|
||||
}
|
||||
useClientTLSCert = []string{
|
||||
"AWS_SDK_GO_CLIENT_TLS_CERT",
|
||||
}
|
||||
useClientTLSKey = []string{
|
||||
"AWS_SDK_GO_CLIENT_TLS_KEY",
|
||||
}
|
||||
)
|
||||
|
||||
// loadEnvConfig retrieves the SDK's environment configuration.
|
||||
|
@ -302,7 +323,9 @@ func envConfigLoad(enableSharedConfig bool) (envConfig, error) {
|
|||
cfg.SharedConfigFile = defaults.SharedConfigFilename()
|
||||
}
|
||||
|
||||
cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE")
|
||||
setFromEnvVal(&cfg.CustomCABundle, useCABundleKey)
|
||||
setFromEnvVal(&cfg.ClientTLSCert, useClientTLSCert)
|
||||
setFromEnvVal(&cfg.ClientTLSKey, useClientTLSKey)
|
||||
|
||||
var err error
|
||||
// STS Regional Endpoint variable
|
||||
|
|
|
@ -25,6 +25,13 @@ const (
|
|||
// ErrCodeSharedConfig represents an error that occurs in the shared
|
||||
// configuration logic
|
||||
ErrCodeSharedConfig = "SharedConfigErr"
|
||||
|
||||
// ErrCodeLoadCustomCABundle error code for unable to load custom CA bundle.
|
||||
ErrCodeLoadCustomCABundle = "LoadCustomCABundleError"
|
||||
|
||||
// ErrCodeLoadClientTLSCert error code for unable to load client TLS
|
||||
// certificate or key
|
||||
ErrCodeLoadClientTLSCert = "LoadClientTLSCertError"
|
||||
)
|
||||
|
||||
// ErrSharedConfigSourceCollision will be returned if a section contains both
|
||||
|
@ -229,17 +236,46 @@ type Options struct {
|
|||
// the SDK will use instead of the default system's root CA bundle. Use this
|
||||
// only if you want to replace the CA bundle the SDK uses for TLS requests.
|
||||
//
|
||||
// Enabling this option will attempt to merge the Transport into the SDK's HTTP
|
||||
// client. If the client's Transport is not a http.Transport an error will be
|
||||
// returned. If the Transport's TLS config is set this option will cause the SDK
|
||||
// HTTP Client's Transport concrete implementation must be a http.Transport
|
||||
// or creating the session will fail.
|
||||
//
|
||||
// If the Transport's TLS config is set this option will cause the SDK
|
||||
// to overwrite the Transport's TLS config's RootCAs value. If the CA
|
||||
// bundle reader contains multiple certificates all of them will be loaded.
|
||||
//
|
||||
// The Session option CustomCABundle is also available when creating sessions
|
||||
// to also enable this feature. CustomCABundle session option field has priority
|
||||
// over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
|
||||
// Can also be specified via the environment variable:
|
||||
//
|
||||
// AWS_CA_BUNDLE=$HOME/ca_bundle
|
||||
//
|
||||
// Can also be specified via the shared config field:
|
||||
//
|
||||
// ca_bundle = $HOME/ca_bundle
|
||||
CustomCABundle io.Reader
|
||||
|
||||
// Reader for the TLC client certificate that should be used by the SDK's
|
||||
// HTTP transport when making requests. The certificate must be paired with
|
||||
// a TLS client key file. Will be ignored if both are not provided.
|
||||
//
|
||||
// HTTP Client's Transport concrete implementation must be a http.Transport
|
||||
// or creating the session will fail.
|
||||
//
|
||||
// Can also be specified via the environment variable:
|
||||
//
|
||||
// AWS_SDK_GO_CLIENT_TLS_CERT=$HOME/my_client_cert
|
||||
ClientTLSCert io.Reader
|
||||
|
||||
// Reader for the TLC client key that should be used by the SDK's HTTP
|
||||
// transport when making requests. The key must be paired with a TLS client
|
||||
// certificate file. Will be ignored if both are not provided.
|
||||
//
|
||||
// HTTP Client's Transport concrete implementation must be a http.Transport
|
||||
// or creating the session will fail.
|
||||
//
|
||||
// Can also be specified via the environment variable:
|
||||
//
|
||||
// AWS_SDK_GO_CLIENT_TLS_KEY=$HOME/my_client_key
|
||||
ClientTLSKey io.Reader
|
||||
|
||||
// The handlers that the session and all API clients will be created with.
|
||||
// This must be a complete set of handlers. Use the defaults.Handlers()
|
||||
// function to initialize this value before changing the handlers to be
|
||||
|
@ -319,17 +355,6 @@ func NewSessionWithOptions(opts Options) (*Session, error) {
|
|||
envCfg.EnableSharedConfig = true
|
||||
}
|
||||
|
||||
// Only use AWS_CA_BUNDLE if session option is not provided.
|
||||
if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil {
|
||||
f, err := os.Open(envCfg.CustomCABundle)
|
||||
if err != nil {
|
||||
return nil, awserr.New("LoadCustomCABundleError",
|
||||
"failed to open custom CA bundle PEM file", err)
|
||||
}
|
||||
defer f.Close()
|
||||
opts.CustomCABundle = f
|
||||
}
|
||||
|
||||
return newSession(opts, envCfg, &opts.Config)
|
||||
}
|
||||
|
||||
|
@ -460,6 +485,10 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := setTLSOptions(&opts, cfg, envCfg, sharedCfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &Session{
|
||||
Config: cfg,
|
||||
Handlers: handlers,
|
||||
|
@ -479,13 +508,6 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session,
|
|||
}
|
||||
}
|
||||
|
||||
// Setup HTTP client with custom cert bundle if enabled
|
||||
if opts.CustomCABundle != nil {
|
||||
if err := loadCustomCABundle(s, opts.CustomCABundle); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
@ -529,22 +551,83 @@ func loadCSMConfig(envCfg envConfig, cfgFiles []string) (csmConfig, error) {
|
|||
return csmConfig{}, nil
|
||||
}
|
||||
|
||||
func loadCustomCABundle(s *Session, bundle io.Reader) error {
|
||||
func setTLSOptions(opts *Options, cfg *aws.Config, envCfg envConfig, sharedCfg sharedConfig) error {
|
||||
// CA Bundle can be specified in both environment variable shared config file.
|
||||
var caBundleFilename = envCfg.CustomCABundle
|
||||
if len(caBundleFilename) == 0 {
|
||||
caBundleFilename = sharedCfg.CustomCABundle
|
||||
}
|
||||
|
||||
// Only use environment value if session option is not provided.
|
||||
customTLSOptions := map[string]struct {
|
||||
filename string
|
||||
field *io.Reader
|
||||
errCode string
|
||||
}{
|
||||
"custom CA bundle PEM": {filename: caBundleFilename, field: &opts.CustomCABundle, errCode: ErrCodeLoadCustomCABundle},
|
||||
"custom client TLS cert": {filename: envCfg.ClientTLSCert, field: &opts.ClientTLSCert, errCode: ErrCodeLoadClientTLSCert},
|
||||
"custom client TLS key": {filename: envCfg.ClientTLSKey, field: &opts.ClientTLSKey, errCode: ErrCodeLoadClientTLSCert},
|
||||
}
|
||||
for name, v := range customTLSOptions {
|
||||
if len(v.filename) != 0 && *v.field == nil {
|
||||
f, err := os.Open(v.filename)
|
||||
if err != nil {
|
||||
return awserr.New(v.errCode, fmt.Sprintf("failed to open %s file", name), err)
|
||||
}
|
||||
defer f.Close()
|
||||
*v.field = f
|
||||
}
|
||||
}
|
||||
|
||||
// Setup HTTP client with custom cert bundle if enabled
|
||||
if opts.CustomCABundle != nil {
|
||||
if err := loadCustomCABundle(cfg.HTTPClient, opts.CustomCABundle); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Setup HTTP client TLS certificate and key for client TLS authentication.
|
||||
if opts.ClientTLSCert != nil && opts.ClientTLSKey != nil {
|
||||
if err := loadClientTLSCert(cfg.HTTPClient, opts.ClientTLSCert, opts.ClientTLSKey); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if opts.ClientTLSCert == nil && opts.ClientTLSKey == nil {
|
||||
// Do nothing if neither values are available.
|
||||
|
||||
} else {
|
||||
return awserr.New(ErrCodeLoadClientTLSCert,
|
||||
fmt.Sprintf("client TLS cert(%t) and key(%t) must both be provided",
|
||||
opts.ClientTLSCert != nil, opts.ClientTLSKey != nil), nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHTTPTransport(client *http.Client) (*http.Transport, error) {
|
||||
var t *http.Transport
|
||||
switch v := s.Config.HTTPClient.Transport.(type) {
|
||||
switch v := client.Transport.(type) {
|
||||
case *http.Transport:
|
||||
t = v
|
||||
default:
|
||||
if s.Config.HTTPClient.Transport != nil {
|
||||
return awserr.New("LoadCustomCABundleError",
|
||||
"unable to load custom CA bundle, HTTPClient's transport unsupported type", nil)
|
||||
if client.Transport != nil {
|
||||
return nil, fmt.Errorf("unsupported transport, %T", client.Transport)
|
||||
}
|
||||
}
|
||||
if t == nil {
|
||||
// Nil transport implies `http.DefaultTransport` should be used. Since
|
||||
// the SDK cannot modify, nor copy the `DefaultTransport` specifying
|
||||
// the values the next closest behavior.
|
||||
t = getCABundleTransport()
|
||||
t = getCustomTransport()
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func loadCustomCABundle(client *http.Client, bundle io.Reader) error {
|
||||
t, err := getHTTPTransport(client)
|
||||
if err != nil {
|
||||
return awserr.New(ErrCodeLoadCustomCABundle,
|
||||
"unable to load custom CA bundle, HTTPClient's transport unsupported type", err)
|
||||
}
|
||||
|
||||
p, err := loadCertPool(bundle)
|
||||
|
@ -556,7 +639,7 @@ func loadCustomCABundle(s *Session, bundle io.Reader) error {
|
|||
}
|
||||
t.TLSClientConfig.RootCAs = p
|
||||
|
||||
s.Config.HTTPClient.Transport = t
|
||||
client.Transport = t
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -564,19 +647,57 @@ func loadCustomCABundle(s *Session, bundle io.Reader) error {
|
|||
func loadCertPool(r io.Reader) (*x509.CertPool, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, awserr.New("LoadCustomCABundleError",
|
||||
return nil, awserr.New(ErrCodeLoadCustomCABundle,
|
||||
"failed to read custom CA bundle PEM file", err)
|
||||
}
|
||||
|
||||
p := x509.NewCertPool()
|
||||
if !p.AppendCertsFromPEM(b) {
|
||||
return nil, awserr.New("LoadCustomCABundleError",
|
||||
return nil, awserr.New(ErrCodeLoadCustomCABundle,
|
||||
"failed to load custom CA bundle PEM file", err)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func loadClientTLSCert(client *http.Client, certFile, keyFile io.Reader) error {
|
||||
t, err := getHTTPTransport(client)
|
||||
if err != nil {
|
||||
return awserr.New(ErrCodeLoadClientTLSCert,
|
||||
"unable to get usable HTTP transport from client", err)
|
||||
}
|
||||
|
||||
cert, err := ioutil.ReadAll(certFile)
|
||||
if err != nil {
|
||||
return awserr.New(ErrCodeLoadClientTLSCert,
|
||||
"unable to get read client TLS cert file", err)
|
||||
}
|
||||
|
||||
key, err := ioutil.ReadAll(keyFile)
|
||||
if err != nil {
|
||||
return awserr.New(ErrCodeLoadClientTLSCert,
|
||||
"unable to get read client TLS key file", err)
|
||||
}
|
||||
|
||||
clientCert, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return awserr.New(ErrCodeLoadClientTLSCert,
|
||||
"unable to load x509 key pair from client cert", err)
|
||||
}
|
||||
|
||||
tlsCfg := t.TLSClientConfig
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &tls.Config{}
|
||||
}
|
||||
|
||||
tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
|
||||
|
||||
t.TLSClientConfig = tlsCfg
|
||||
client.Transport = t
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeConfigSrcs(cfg, userCfg *aws.Config,
|
||||
envCfg envConfig, sharedCfg sharedConfig,
|
||||
handlers request.Handlers,
|
||||
|
|
|
@ -34,6 +34,9 @@ const (
|
|||
// Additional Config fields
|
||||
regionKey = `region`
|
||||
|
||||
// custom CA Bundle filename
|
||||
customCABundleKey = `ca_bundle`
|
||||
|
||||
// endpoint discovery group
|
||||
enableEndpointDiscoveryKey = `endpoint_discovery_enabled` // optional
|
||||
|
||||
|
@ -90,6 +93,15 @@ type sharedConfig struct {
|
|||
// region
|
||||
Region string
|
||||
|
||||
// CustomCABundle is the file path to a PEM file the SDK will read and
|
||||
// use to configure the HTTP transport with additional CA certs that are
|
||||
// not present in the platforms default CA store.
|
||||
//
|
||||
// This value will be ignored if the file does not exist.
|
||||
//
|
||||
// ca_bundle
|
||||
CustomCABundle string
|
||||
|
||||
// EnableEndpointDiscovery can be enabled in the shared config by setting
|
||||
// endpoint_discovery_enabled to true
|
||||
//
|
||||
|
@ -276,6 +288,7 @@ func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile, e
|
|||
updateString(&cfg.SourceProfileName, section, sourceProfileKey)
|
||||
updateString(&cfg.CredentialSource, section, credentialSourceKey)
|
||||
updateString(&cfg.Region, section, regionKey)
|
||||
updateString(&cfg.CustomCABundle, section, customCABundleKey)
|
||||
|
||||
if section.Has(roleDurationSecondsKey) {
|
||||
d := time.Duration(section.Int(roleDurationSecondsKey)) * time.Second
|
||||
|
|
|
@ -5,4 +5,4 @@ package aws
|
|||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.36.0"
|
||||
const SDKVersion = "1.36.5"
|
||||
|
|
|
@ -48804,10 +48804,25 @@ type CreateImageInput struct {
|
|||
Name *string `locationName:"name" type:"string" required:"true"`
|
||||
|
||||
// By default, Amazon EC2 attempts to shut down and reboot the instance before
|
||||
// creating the image. If the 'No Reboot' option is set, Amazon EC2 doesn't
|
||||
// shut down the instance before creating the image. When this option is used,
|
||||
// file system integrity on the created image can't be guaranteed.
|
||||
// creating the image. If the No Reboot option is set, Amazon EC2 doesn't shut
|
||||
// down the instance before creating the image. When this option is used, file
|
||||
// system integrity on the created image can't be guaranteed.
|
||||
NoReboot *bool `locationName:"noReboot" type:"boolean"`
|
||||
|
||||
// The tags to apply to the AMI and snapshots on creation. You can tag the AMI,
|
||||
// the snapshots, or both.
|
||||
//
|
||||
// * To tag the AMI, the value for ResourceType must be image.
|
||||
//
|
||||
// * To tag the snapshots that are created of the root volume and of other
|
||||
// EBS volumes that are attached to the instance, the value for ResourceType
|
||||
// must be snapshot. The same tag is applied to all of the snapshots that
|
||||
// are created.
|
||||
//
|
||||
// If you specify other values for ResourceType, the request fails.
|
||||
//
|
||||
// To tag an AMI or snapshot after it has been created, see CreateTags (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateTags.html).
|
||||
TagSpecifications []*TagSpecification `locationName:"TagSpecification" locationNameList:"item" type:"list"`
|
||||
}
|
||||
|
||||
// String returns the string representation
|
||||
|
@ -48872,6 +48887,12 @@ func (s *CreateImageInput) SetNoReboot(v bool) *CreateImageInput {
|
|||
return s
|
||||
}
|
||||
|
||||
// SetTagSpecifications sets the TagSpecifications field's value.
|
||||
func (s *CreateImageInput) SetTagSpecifications(v []*TagSpecification) *CreateImageInput {
|
||||
s.TagSpecifications = v
|
||||
return s
|
||||
}
|
||||
|
||||
type CreateImageOutput struct {
|
||||
_ struct{} `type:"structure"`
|
||||
|
||||
|
@ -110333,15 +110354,15 @@ type TagSpecification struct {
|
|||
// The type of resource to tag. Currently, the resource types that support tagging
|
||||
// on creation are: capacity-reservation | carrier-gateway | client-vpn-endpoint
|
||||
// | customer-gateway | dedicated-host | dhcp-options | export-image-task |
|
||||
// export-instance-task | fleet | fpga-image | host-reservation | import-image-task
|
||||
// export-instance-task | fleet | fpga-image | host-reservation | image| import-image-task
|
||||
// | import-snapshot-task | instance | internet-gateway | ipv4pool-ec2 | ipv6pool-ec2
|
||||
// | key-pair | launch-template | placement-group | prefix-list | natgateway
|
||||
// | network-acl | route-table | security-group | spot-fleet-request | spot-instances-request
|
||||
// | snapshot | subnet | traffic-mirror-filter | traffic-mirror-session | traffic-mirror-target
|
||||
// | transit-gateway | transit-gateway-attachment | transit-gateway-route-table
|
||||
// | volume |vpc | vpc-peering-connection | vpc-endpoint (for interface and
|
||||
// gateway endpoints) | vpc-endpoint-service (for AWS PrivateLink) | vpc-flow-log
|
||||
// | vpn-connection | vpn-gateway.
|
||||
// | network-acl | route-table | security-group| snapshot | spot-fleet-request
|
||||
// | spot-instances-request | snapshot | subnet | traffic-mirror-filter | traffic-mirror-session
|
||||
// | traffic-mirror-target | transit-gateway | transit-gateway-attachment |
|
||||
// transit-gateway-route-table | volume |vpc | vpc-peering-connection | vpc-endpoint
|
||||
// (for interface and gateway endpoints) | vpc-endpoint-service (for AWS PrivateLink)
|
||||
// | vpc-flow-log | vpn-connection | vpn-gateway.
|
||||
//
|
||||
// To tag a resource after it has been created, see CreateTags (https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateTags.html).
|
||||
ResourceType *string `locationName:"resourceType" type:"string" enum:"ResourceType"`
|
||||
|
@ -119133,6 +119154,15 @@ const (
|
|||
// InstanceTypeG3sXlarge is a InstanceType enum value
|
||||
InstanceTypeG3sXlarge = "g3s.xlarge"
|
||||
|
||||
// InstanceTypeG4ad4xlarge is a InstanceType enum value
|
||||
InstanceTypeG4ad4xlarge = "g4ad.4xlarge"
|
||||
|
||||
// InstanceTypeG4ad8xlarge is a InstanceType enum value
|
||||
InstanceTypeG4ad8xlarge = "g4ad.8xlarge"
|
||||
|
||||
// InstanceTypeG4ad16xlarge is a InstanceType enum value
|
||||
InstanceTypeG4ad16xlarge = "g4ad.16xlarge"
|
||||
|
||||
// InstanceTypeG4dnXlarge is a InstanceType enum value
|
||||
InstanceTypeG4dnXlarge = "g4dn.xlarge"
|
||||
|
||||
|
@ -119816,6 +119846,9 @@ func InstanceType_Values() []string {
|
|||
InstanceTypeG38xlarge,
|
||||
InstanceTypeG316xlarge,
|
||||
InstanceTypeG3sXlarge,
|
||||
InstanceTypeG4ad4xlarge,
|
||||
InstanceTypeG4ad8xlarge,
|
||||
InstanceTypeG4ad16xlarge,
|
||||
InstanceTypeG4dnXlarge,
|
||||
InstanceTypeG4dn2xlarge,
|
||||
InstanceTypeG4dn4xlarge,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -135,6 +135,12 @@ const (
|
|||
// The manifest list is referencing an image that does not exist.
|
||||
ErrCodeReferencedImagesNotFoundException = "ReferencedImagesNotFoundException"
|
||||
|
||||
// ErrCodeRegistryPolicyNotFoundException for service response error code
|
||||
// "RegistryPolicyNotFoundException".
|
||||
//
|
||||
// The registry doesn't have an associated registry policy.
|
||||
ErrCodeRegistryPolicyNotFoundException = "RegistryPolicyNotFoundException"
|
||||
|
||||
// ErrCodeRepositoryAlreadyExistsException for service response error code
|
||||
// "RepositoryAlreadyExistsException".
|
||||
//
|
||||
|
@ -194,6 +200,12 @@ const (
|
|||
// The upload could not be found, or the specified upload ID is not valid for
|
||||
// this repository.
|
||||
ErrCodeUploadNotFoundException = "UploadNotFoundException"
|
||||
|
||||
// ErrCodeValidationException for service response error code
|
||||
// "ValidationException".
|
||||
//
|
||||
// There was an exception validating this request.
|
||||
ErrCodeValidationException = "ValidationException"
|
||||
)
|
||||
|
||||
var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{
|
||||
|
@ -216,6 +228,7 @@ var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{
|
|||
"LifecyclePolicyPreviewNotFoundException": newErrorLifecyclePolicyPreviewNotFoundException,
|
||||
"LimitExceededException": newErrorLimitExceededException,
|
||||
"ReferencedImagesNotFoundException": newErrorReferencedImagesNotFoundException,
|
||||
"RegistryPolicyNotFoundException": newErrorRegistryPolicyNotFoundException,
|
||||
"RepositoryAlreadyExistsException": newErrorRepositoryAlreadyExistsException,
|
||||
"RepositoryNotEmptyException": newErrorRepositoryNotEmptyException,
|
||||
"RepositoryNotFoundException": newErrorRepositoryNotFoundException,
|
||||
|
@ -225,4 +238,5 @@ var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{
|
|||
"TooManyTagsException": newErrorTooManyTagsException,
|
||||
"UnsupportedImageTypeException": newErrorUnsupportedImageTypeException,
|
||||
"UploadNotFoundException": newErrorUploadNotFoundException,
|
||||
"ValidationException": newErrorValidationException,
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -569,6 +569,46 @@ const (
|
|||
// The specified OpsItem ID doesn't exist. Verify the ID and try again.
|
||||
ErrCodeOpsItemNotFoundException = "OpsItemNotFoundException"
|
||||
|
||||
// ErrCodeOpsMetadataAlreadyExistsException for service response error code
|
||||
// "OpsMetadataAlreadyExistsException".
|
||||
//
|
||||
// An OpsMetadata object already exists for the selected resource.
|
||||
ErrCodeOpsMetadataAlreadyExistsException = "OpsMetadataAlreadyExistsException"
|
||||
|
||||
// ErrCodeOpsMetadataInvalidArgumentException for service response error code
|
||||
// "OpsMetadataInvalidArgumentException".
|
||||
//
|
||||
// One of the arguments passed is invalid.
|
||||
ErrCodeOpsMetadataInvalidArgumentException = "OpsMetadataInvalidArgumentException"
|
||||
|
||||
// ErrCodeOpsMetadataKeyLimitExceededException for service response error code
|
||||
// "OpsMetadataKeyLimitExceededException".
|
||||
//
|
||||
// The OpsMetadata object exceeds the maximum number of OpsMetadata keys that
|
||||
// you can assign to an application in AppManager.
|
||||
ErrCodeOpsMetadataKeyLimitExceededException = "OpsMetadataKeyLimitExceededException"
|
||||
|
||||
// ErrCodeOpsMetadataLimitExceededException for service response error code
|
||||
// "OpsMetadataLimitExceededException".
|
||||
//
|
||||
// Your account reached the maximum number of OpsMetadata objects allowed by
|
||||
// AppManager. The maximum is 200 OpsMetadata objects. Delete one or more OpsMetadata
|
||||
// object and try again.
|
||||
ErrCodeOpsMetadataLimitExceededException = "OpsMetadataLimitExceededException"
|
||||
|
||||
// ErrCodeOpsMetadataNotFoundException for service response error code
|
||||
// "OpsMetadataNotFoundException".
|
||||
//
|
||||
// The OpsMetadata object does not exist.
|
||||
ErrCodeOpsMetadataNotFoundException = "OpsMetadataNotFoundException"
|
||||
|
||||
// ErrCodeOpsMetadataTooManyUpdatesException for service response error code
|
||||
// "OpsMetadataTooManyUpdatesException".
|
||||
//
|
||||
// The system is processing too many concurrent updates. Wait a few moments
|
||||
// and try again.
|
||||
ErrCodeOpsMetadataTooManyUpdatesException = "OpsMetadataTooManyUpdatesException"
|
||||
|
||||
// ErrCodeParameterAlreadyExists for service response error code
|
||||
// "ParameterAlreadyExists".
|
||||
//
|
||||
|
@ -882,6 +922,12 @@ var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{
|
|||
"OpsItemInvalidParameterException": newErrorOpsItemInvalidParameterException,
|
||||
"OpsItemLimitExceededException": newErrorOpsItemLimitExceededException,
|
||||
"OpsItemNotFoundException": newErrorOpsItemNotFoundException,
|
||||
"OpsMetadataAlreadyExistsException": newErrorOpsMetadataAlreadyExistsException,
|
||||
"OpsMetadataInvalidArgumentException": newErrorOpsMetadataInvalidArgumentException,
|
||||
"OpsMetadataKeyLimitExceededException": newErrorOpsMetadataKeyLimitExceededException,
|
||||
"OpsMetadataLimitExceededException": newErrorOpsMetadataLimitExceededException,
|
||||
"OpsMetadataNotFoundException": newErrorOpsMetadataNotFoundException,
|
||||
"OpsMetadataTooManyUpdatesException": newErrorOpsMetadataTooManyUpdatesException,
|
||||
"ParameterAlreadyExists": newErrorParameterAlreadyExists,
|
||||
"ParameterLimitExceeded": newErrorParameterLimitExceeded,
|
||||
"ParameterMaxVersionLimitExceeded": newErrorParameterMaxVersionLimitExceeded,
|
||||
|
|
|
@ -96,6 +96,10 @@ type SSMAPI interface {
|
|||
CreateOpsItemWithContext(aws.Context, *ssm.CreateOpsItemInput, ...request.Option) (*ssm.CreateOpsItemOutput, error)
|
||||
CreateOpsItemRequest(*ssm.CreateOpsItemInput) (*request.Request, *ssm.CreateOpsItemOutput)
|
||||
|
||||
CreateOpsMetadata(*ssm.CreateOpsMetadataInput) (*ssm.CreateOpsMetadataOutput, error)
|
||||
CreateOpsMetadataWithContext(aws.Context, *ssm.CreateOpsMetadataInput, ...request.Option) (*ssm.CreateOpsMetadataOutput, error)
|
||||
CreateOpsMetadataRequest(*ssm.CreateOpsMetadataInput) (*request.Request, *ssm.CreateOpsMetadataOutput)
|
||||
|
||||
CreatePatchBaseline(*ssm.CreatePatchBaselineInput) (*ssm.CreatePatchBaselineOutput, error)
|
||||
CreatePatchBaselineWithContext(aws.Context, *ssm.CreatePatchBaselineInput, ...request.Option) (*ssm.CreatePatchBaselineOutput, error)
|
||||
CreatePatchBaselineRequest(*ssm.CreatePatchBaselineInput) (*request.Request, *ssm.CreatePatchBaselineOutput)
|
||||
|
@ -124,6 +128,10 @@ type SSMAPI interface {
|
|||
DeleteMaintenanceWindowWithContext(aws.Context, *ssm.DeleteMaintenanceWindowInput, ...request.Option) (*ssm.DeleteMaintenanceWindowOutput, error)
|
||||
DeleteMaintenanceWindowRequest(*ssm.DeleteMaintenanceWindowInput) (*request.Request, *ssm.DeleteMaintenanceWindowOutput)
|
||||
|
||||
DeleteOpsMetadata(*ssm.DeleteOpsMetadataInput) (*ssm.DeleteOpsMetadataOutput, error)
|
||||
DeleteOpsMetadataWithContext(aws.Context, *ssm.DeleteOpsMetadataInput, ...request.Option) (*ssm.DeleteOpsMetadataOutput, error)
|
||||
DeleteOpsMetadataRequest(*ssm.DeleteOpsMetadataInput) (*request.Request, *ssm.DeleteOpsMetadataOutput)
|
||||
|
||||
DeleteParameter(*ssm.DeleteParameterInput) (*ssm.DeleteParameterOutput, error)
|
||||
DeleteParameterWithContext(aws.Context, *ssm.DeleteParameterInput, ...request.Option) (*ssm.DeleteParameterOutput, error)
|
||||
DeleteParameterRequest(*ssm.DeleteParameterInput) (*request.Request, *ssm.DeleteParameterOutput)
|
||||
|
@ -434,6 +442,10 @@ type SSMAPI interface {
|
|||
GetOpsItemWithContext(aws.Context, *ssm.GetOpsItemInput, ...request.Option) (*ssm.GetOpsItemOutput, error)
|
||||
GetOpsItemRequest(*ssm.GetOpsItemInput) (*request.Request, *ssm.GetOpsItemOutput)
|
||||
|
||||
GetOpsMetadata(*ssm.GetOpsMetadataInput) (*ssm.GetOpsMetadataOutput, error)
|
||||
GetOpsMetadataWithContext(aws.Context, *ssm.GetOpsMetadataInput, ...request.Option) (*ssm.GetOpsMetadataOutput, error)
|
||||
GetOpsMetadataRequest(*ssm.GetOpsMetadataInput) (*request.Request, *ssm.GetOpsMetadataOutput)
|
||||
|
||||
GetOpsSummary(*ssm.GetOpsSummaryInput) (*ssm.GetOpsSummaryOutput, error)
|
||||
GetOpsSummaryWithContext(aws.Context, *ssm.GetOpsSummaryInput, ...request.Option) (*ssm.GetOpsSummaryOutput, error)
|
||||
GetOpsSummaryRequest(*ssm.GetOpsSummaryInput) (*request.Request, *ssm.GetOpsSummaryOutput)
|
||||
|
@ -539,6 +551,10 @@ type SSMAPI interface {
|
|||
ListInventoryEntriesWithContext(aws.Context, *ssm.ListInventoryEntriesInput, ...request.Option) (*ssm.ListInventoryEntriesOutput, error)
|
||||
ListInventoryEntriesRequest(*ssm.ListInventoryEntriesInput) (*request.Request, *ssm.ListInventoryEntriesOutput)
|
||||
|
||||
ListOpsMetadata(*ssm.ListOpsMetadataInput) (*ssm.ListOpsMetadataOutput, error)
|
||||
ListOpsMetadataWithContext(aws.Context, *ssm.ListOpsMetadataInput, ...request.Option) (*ssm.ListOpsMetadataOutput, error)
|
||||
ListOpsMetadataRequest(*ssm.ListOpsMetadataInput) (*request.Request, *ssm.ListOpsMetadataOutput)
|
||||
|
||||
ListResourceComplianceSummaries(*ssm.ListResourceComplianceSummariesInput) (*ssm.ListResourceComplianceSummariesOutput, error)
|
||||
ListResourceComplianceSummariesWithContext(aws.Context, *ssm.ListResourceComplianceSummariesInput, ...request.Option) (*ssm.ListResourceComplianceSummariesOutput, error)
|
||||
ListResourceComplianceSummariesRequest(*ssm.ListResourceComplianceSummariesInput) (*request.Request, *ssm.ListResourceComplianceSummariesOutput)
|
||||
|
@ -665,6 +681,10 @@ type SSMAPI interface {
|
|||
UpdateOpsItemWithContext(aws.Context, *ssm.UpdateOpsItemInput, ...request.Option) (*ssm.UpdateOpsItemOutput, error)
|
||||
UpdateOpsItemRequest(*ssm.UpdateOpsItemInput) (*request.Request, *ssm.UpdateOpsItemOutput)
|
||||
|
||||
UpdateOpsMetadata(*ssm.UpdateOpsMetadataInput) (*ssm.UpdateOpsMetadataOutput, error)
|
||||
UpdateOpsMetadataWithContext(aws.Context, *ssm.UpdateOpsMetadataInput, ...request.Option) (*ssm.UpdateOpsMetadataOutput, error)
|
||||
UpdateOpsMetadataRequest(*ssm.UpdateOpsMetadataInput) (*request.Request, *ssm.UpdateOpsMetadataOutput)
|
||||
|
||||
UpdatePatchBaseline(*ssm.UpdatePatchBaselineInput) (*ssm.UpdatePatchBaselineOutput, error)
|
||||
UpdatePatchBaselineWithContext(aws.Context, *ssm.UpdatePatchBaselineInput, ...request.Option) (*ssm.UpdatePatchBaselineOutput, error)
|
||||
UpdatePatchBaselineRequest(*ssm.UpdatePatchBaselineInput) (*request.Request, *ssm.UpdatePatchBaselineOutput)
|
||||
|
|
|
@ -16,4 +16,4 @@ change is the ability to represent an invalid UUID (vs a NIL UUID).
|
|||
|
||||
Full `go doc` style documentation for the package can be viewed online without
|
||||
installing this package by using the GoDoc site here:
|
||||
http://godoc.org/github.com/google/uuid
|
||||
http://pkg.go.dev/github.com/google/uuid
|
||||
|
|
|
@ -16,10 +16,11 @@ func (uuid UUID) MarshalText() ([]byte, error) {
|
|||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (uuid *UUID) UnmarshalText(data []byte) error {
|
||||
id, err := ParseBytes(data)
|
||||
if err == nil {
|
||||
*uuid = id
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
*uuid = id
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||
|
|
|
@ -17,12 +17,6 @@ import (
|
|||
//
|
||||
// In most cases, New should be used.
|
||||
func NewUUID() (UUID, error) {
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
nodeMu.Unlock()
|
||||
|
||||
var uuid UUID
|
||||
now, seq, err := GetTime()
|
||||
if err != nil {
|
||||
|
@ -38,7 +32,13 @@ func NewUUID() (UUID, error) {
|
|||
binary.BigEndian.PutUint16(uuid[4:], timeMid)
|
||||
binary.BigEndian.PutUint16(uuid[6:], timeHi)
|
||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
copy(uuid[10:], nodeID[:])
|
||||
nodeMu.Unlock()
|
||||
|
||||
return uuid, nil
|
||||
}
|
||||
|
|
|
@ -27,8 +27,13 @@ func New() UUID {
|
|||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||
// year and having one duplicate.
|
||||
func NewRandom() (UUID, error) {
|
||||
return NewRandomFromReader(rander)
|
||||
}
|
||||
|
||||
// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
|
||||
func NewRandomFromReader(r io.Reader) (UUID, error) {
|
||||
var uuid UUID
|
||||
_, err := io.ReadFull(rander, uuid[:])
|
||||
_, err := io.ReadFull(r, uuid[:])
|
||||
if err != nil {
|
||||
return Nil, err
|
||||
}
|
||||
|
|
|
@ -1,5 +1,44 @@
|
|||
# HCL Changelog
|
||||
|
||||
## v2.8.0 (December 7, 2020)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* hclsyntax: Expression grouping parentheses will now be reflected by an explicit node in the AST, whereas before they were only considered during parsing. ([#426](https://github.com/hashicorp/hcl/pull/426))
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* hclwrite: The parser will now correctly include the `(` and `)` tokens when an expression is surrounded by parentheses. Previously it would incorrectly recognize those tokens as being extraneous tokens outside of the expression. ([#426](https://github.com/hashicorp/hcl/pull/426))
|
||||
* hclwrite: The formatter will now remove (rather than insert) spaces between the `!` (unary boolean "not") operator and its subsequent operand. ([#403](https://github.com/hashicorp/hcl/pull/403))
|
||||
* hclsyntax: Unmark conditional values in expressions before checking their truthfulness ([#427](https://github.com/hashicorp/hcl/pull/427))
|
||||
|
||||
## v2.7.2 (November 30, 2020)
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* gohcl: Fix panic when decoding into type containing value slices. ([#335](https://github.com/hashicorp/hcl/pull/335))
|
||||
* hclsyntax: The unusual expression `null[*]` was previously always returning an unknown value, even though the rules for `[*]` normally call for it to return an empty tuple when applied to a null. As well as being a surprising result, it was particularly problematic because it violated the rule that a calling application may assume that an expression result will always be known unless the application itself introduces unknown values via the evaluation context. `null[*]` will now produce an empty tuple. ([#416](https://github.com/hashicorp/hcl/pull/416))
|
||||
* hclsyntax: Fix panic when traversing a list, tuple, or map with cty "marks" ([#424](https://github.com/hashicorp/hcl/pull/424))
|
||||
|
||||
## v2.7.1 (November 18, 2020)
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* hclwrite: Correctly handle blank quoted string block labels, instead of dropping them ([#422](https://github.com/hashicorp/hcl/pull/422))
|
||||
|
||||
## v2.7.0 (October 14, 2020)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* json: There is a new function `ParseWithStartPos`, which allows overriding the starting position for parsing in case the given JSON bytes are a fragment of a larger document, such as might happen when decoding with `encoding/json` into a `json.RawMessage`. ([#389](https://github.com/hashicorp/hcl/pull/389))
|
||||
* json: There is a new function `ParseExpression`, which allows parsing a JSON string directly in expression mode, whereas previously it was only possible to parse a JSON string in body mode. ([#381](https://github.com/hashicorp/hcl/pull/381))
|
||||
* hclwrite: `Block` type now supports `SetType` and `SetLabels`, allowing surgical changes to the type and labels of an existing block without having to reconstruct the entire block. ([#340](https://github.com/hashicorp/hcl/pull/340))
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* hclsyntax: Fix confusing error message for bitwise OR operator ([#380](https://github.com/hashicorp/hcl/pull/380))
|
||||
* hclsyntax: Several bug fixes for using HCL with values containing cty "marks" ([#404](https://github.com/hashicorp/hcl/pull/404), [#406](https://github.com/hashicorp/hcl/pull/404), [#407](https://github.com/hashicorp/hcl/pull/404))
|
||||
|
||||
## v2.6.0 (June 4, 2020)
|
||||
|
||||
### Enhancements
|
||||
|
|
|
@ -33,11 +33,25 @@ package main
|
|||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
LogLevel string `hcl:"log_level"`
|
||||
IOMode string `hcl:"io_mode"`
|
||||
Service ServiceConfig `hcl:"service,block"`
|
||||
}
|
||||
|
||||
type ServiceConfig struct {
|
||||
Protocol string `hcl:"protocol,label"`
|
||||
Type string `hcl:"type,label"`
|
||||
ListenAddr string `hcl:"listen_addr"`
|
||||
Processes []ProcessConfig `hcl:"process,block"`
|
||||
}
|
||||
|
||||
type ProcessConfig struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Command []string `hcl:"command"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -65,6 +65,19 @@ func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value)
|
|||
|
||||
tags := getFieldTags(val.Type())
|
||||
|
||||
if tags.Body != nil {
|
||||
fieldIdx := *tags.Body
|
||||
field := val.Type().Field(fieldIdx)
|
||||
fieldV := val.Field(fieldIdx)
|
||||
switch {
|
||||
case bodyType.AssignableTo(field.Type):
|
||||
fieldV.Set(reflect.ValueOf(body))
|
||||
|
||||
default:
|
||||
diags = append(diags, decodeBodyToValue(body, ctx, fieldV)...)
|
||||
}
|
||||
}
|
||||
|
||||
if tags.Remain != nil {
|
||||
fieldIdx := *tags.Remain
|
||||
field := val.Type().Field(fieldIdx)
|
||||
|
@ -185,6 +198,9 @@ func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value)
|
|||
diags = append(diags, decodeBlockToValue(block, ctx, v.Elem())...)
|
||||
sli.Index(i).Set(v)
|
||||
} else {
|
||||
if i >= sli.Len() {
|
||||
sli = reflect.Append(sli, reflect.Indirect(reflect.New(ty)))
|
||||
}
|
||||
diags = append(diags, decodeBlockToValue(block, ctx, sli.Index(i))...)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,13 @@
|
|||
// in which case multiple blocks of the corresponding type are decoded into
|
||||
// the slice.
|
||||
//
|
||||
// "body" can be placed on a single field of type hcl.Body to capture
|
||||
// the full hcl.Body that was decoded for a block. This does not allow leftover
|
||||
// values like "remain", so a decoding error will still be returned if leftover
|
||||
// fields are given. If you want to capture the decoding body PLUS leftover
|
||||
// fields, you must specify a "remain" field as well to prevent errors. The
|
||||
// body field and the remain field will both contain the leftover fields.
|
||||
//
|
||||
// "label" fields are considered only in a struct used as the type of a field
|
||||
// marked as "block", and are used sequentially to capture the labels of
|
||||
// the blocks being decoded. In this case, the name token is used only as
|
||||
|
|
|
@ -113,6 +113,7 @@ type fieldTags struct {
|
|||
Blocks map[string]int
|
||||
Labels []labelField
|
||||
Remain *int
|
||||
Body *int
|
||||
Optional map[string]bool
|
||||
}
|
||||
|
||||
|
@ -162,6 +163,12 @@ func getFieldTags(ty reflect.Type) *fieldTags {
|
|||
}
|
||||
idx := i // copy, because this loop will continue assigning to i
|
||||
ret.Remain = &idx
|
||||
case "body":
|
||||
if ret.Body != nil {
|
||||
panic("only one 'body' tag is permitted")
|
||||
}
|
||||
idx := i // copy, because this loop will continue assigning to i
|
||||
ret.Body = &idx
|
||||
case "optional":
|
||||
ret.Attributes[name] = i
|
||||
ret.Optional[name] = true
|
||||
|
|
|
@ -27,6 +27,32 @@ type Expression interface {
|
|||
// Assert that Expression implements hcl.Expression
|
||||
var assertExprImplExpr hcl.Expression = Expression(nil)
|
||||
|
||||
// ParenthesesExpr represents an expression written in grouping
|
||||
// parentheses.
|
||||
//
|
||||
// The parser takes care of the precedence effect of the parentheses, so the
|
||||
// only purpose of this separate expression node is to capture the source range
|
||||
// of the parentheses themselves, rather than the source range of the
|
||||
// expression within. All of the other expression operations just pass through
|
||||
// to the underlying expression.
|
||||
type ParenthesesExpr struct {
|
||||
Expression
|
||||
SrcRange hcl.Range
|
||||
}
|
||||
|
||||
var _ hcl.Expression = (*ParenthesesExpr)(nil)
|
||||
|
||||
func (e *ParenthesesExpr) Range() hcl.Range {
|
||||
return e.SrcRange
|
||||
}
|
||||
|
||||
func (e *ParenthesesExpr) walkChildNodes(w internalWalkFunc) {
|
||||
// We override the walkChildNodes from the embedded Expression to
|
||||
// ensure that both the parentheses _and_ the content are visible
|
||||
// in a walk.
|
||||
w(e.Expression)
|
||||
}
|
||||
|
||||
// LiteralValueExpr is an expression that just always returns a given value.
|
||||
type LiteralValueExpr struct {
|
||||
Val cty.Value
|
||||
|
@ -598,6 +624,8 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
|
|||
return cty.UnknownVal(resultType), diags
|
||||
}
|
||||
|
||||
// Unmark result before testing for truthiness
|
||||
condResult, _ = condResult.UnmarkDeep()
|
||||
if condResult.True() {
|
||||
diags = append(diags, trueDiags...)
|
||||
if convs[0] != nil {
|
||||
|
@ -971,6 +999,9 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
if collVal.Type() == cty.DynamicPseudoType {
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
// Unmark collection before checking for iterability, because marked
|
||||
// values cannot be iterated
|
||||
collVal, marks := collVal.Unmark()
|
||||
if !collVal.CanIterateElements() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
|
@ -1178,7 +1209,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
}
|
||||
}
|
||||
|
||||
return cty.ObjectVal(vals), diags
|
||||
return cty.ObjectVal(vals).WithMarks(marks), diags
|
||||
|
||||
} else {
|
||||
// Producing a tuple
|
||||
|
@ -1254,7 +1285,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
return cty.TupleVal(vals), diags
|
||||
return cty.TupleVal(vals).WithMarks(marks), diags
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1317,12 +1348,6 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
}
|
||||
|
||||
sourceTy := sourceVal.Type()
|
||||
if sourceTy == cty.DynamicPseudoType {
|
||||
// If we don't even know the _type_ of our source value yet then
|
||||
// we'll need to defer all processing, since we can't decide our
|
||||
// result type either.
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
// A "special power" of splat expressions is that they can be applied
|
||||
// both to tuples/lists and to other values, and in the latter case
|
||||
|
@ -1346,6 +1371,13 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
if sourceTy == cty.DynamicPseudoType {
|
||||
// If we don't even know the _type_ of our source value yet then
|
||||
// we'll need to defer all processing, since we can't decide our
|
||||
// result type either.
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
|
||||
if autoUpgrade {
|
||||
sourceVal = cty.TupleVal([]cty.Value{sourceVal})
|
||||
sourceTy = sourceVal.Type()
|
||||
|
|
|
@ -26,6 +26,9 @@ func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||
var diags hcl.Diagnostics
|
||||
isKnown := true
|
||||
|
||||
// Maintain a set of marks for values used in the template
|
||||
marks := make(cty.ValueMarks)
|
||||
|
||||
for _, part := range e.Parts {
|
||||
partVal, partDiags := part.Value(ctx)
|
||||
diags = append(diags, partDiags...)
|
||||
|
@ -71,14 +74,24 @@ func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||
continue
|
||||
}
|
||||
|
||||
buf.WriteString(strVal.AsString())
|
||||
// Unmark the part and merge its marks into the set
|
||||
unmarked, partMarks := strVal.Unmark()
|
||||
for k, v := range partMarks {
|
||||
marks[k] = v
|
||||
}
|
||||
|
||||
buf.WriteString(unmarked.AsString())
|
||||
}
|
||||
|
||||
var ret cty.Value
|
||||
if !isKnown {
|
||||
return cty.UnknownVal(cty.String), diags
|
||||
ret = cty.UnknownVal(cty.String)
|
||||
} else {
|
||||
ret = cty.StringVal(buf.String())
|
||||
}
|
||||
|
||||
return cty.StringVal(buf.String()), diags
|
||||
// Apply the full set of marks to the returned value
|
||||
return ret.WithMarks(marks), diags
|
||||
}
|
||||
|
||||
func (e *TemplateExpr) Range() hcl.Range {
|
||||
|
|
|
@ -911,7 +911,7 @@ func (p *parser) parseExpressionTerm() (Expression, hcl.Diagnostics) {
|
|||
|
||||
switch start.Type {
|
||||
case TokenOParen:
|
||||
p.Read() // eat open paren
|
||||
oParen := p.Read() // eat open paren
|
||||
|
||||
p.PushIncludeNewlines(false)
|
||||
|
||||
|
@ -937,9 +937,19 @@ func (p *parser) parseExpressionTerm() (Expression, hcl.Diagnostics) {
|
|||
p.setRecovery()
|
||||
}
|
||||
|
||||
p.Read() // eat closing paren
|
||||
cParen := p.Read() // eat closing paren
|
||||
p.PopIncludeNewlines()
|
||||
|
||||
// Our parser's already taken care of the precedence effect of the
|
||||
// parentheses by considering them to be a kind of "term", but we
|
||||
// still need to include the parentheses in our AST so we can give
|
||||
// an accurate representation of the source range that includes the
|
||||
// open and closing parentheses.
|
||||
expr = &ParenthesesExpr{
|
||||
Expression: expr,
|
||||
SrcRange: hcl.RangeBetween(oParen.Range, cParen.Range),
|
||||
}
|
||||
|
||||
return expr, diags
|
||||
|
||||
case TokenNumberLit:
|
||||
|
|
|
@ -202,7 +202,7 @@ func checkInvalidTokens(tokens Tokens) hcl.Diagnostics {
|
|||
case TokenBitwiseAnd:
|
||||
suggestion = " Did you mean boolean AND (\"&&\")?"
|
||||
case TokenBitwiseOr:
|
||||
suggestion = " Did you mean boolean OR (\"&&\")?"
|
||||
suggestion = " Did you mean boolean OR (\"||\")?"
|
||||
case TokenBitwiseNot:
|
||||
suggestion = " Did you mean boolean NOT (\"!\")?"
|
||||
}
|
||||
|
@ -294,12 +294,23 @@ func checkInvalidTokens(tokens Tokens) hcl.Diagnostics {
|
|||
Subject: &tok.Range,
|
||||
})
|
||||
case TokenInvalid:
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid character",
|
||||
Detail: "This character is not used within the language.",
|
||||
Subject: &tok.Range,
|
||||
})
|
||||
chars := string(tok.Bytes)
|
||||
switch chars {
|
||||
case "“", "”":
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid character",
|
||||
Detail: "\"Curly quotes\" are not valid here. These can sometimes be inadvertently introduced when sharing code via documents or discussion forums. It might help to replace the character with a \"straight quote\".",
|
||||
Subject: &tok.Range,
|
||||
})
|
||||
default:
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid character",
|
||||
Detail: "This character is not used within the language.",
|
||||
Subject: &tok.Range,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return diags
|
||||
|
|
|
@ -10,7 +10,7 @@ type Block struct {
|
|||
|
||||
leadComments *node
|
||||
typeName *node
|
||||
labels nodeSet
|
||||
labels *node
|
||||
open *node
|
||||
body *node
|
||||
close *node
|
||||
|
@ -19,7 +19,6 @@ type Block struct {
|
|||
func newBlock() *Block {
|
||||
return &Block{
|
||||
inTree: newInTree(),
|
||||
labels: newNodeSet(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,12 +34,8 @@ func (b *Block) init(typeName string, labels []string) {
|
|||
nameObj := newIdentifier(nameTok)
|
||||
b.leadComments = b.children.Append(newComments(nil))
|
||||
b.typeName = b.children.Append(nameObj)
|
||||
for _, label := range labels {
|
||||
labelToks := TokensForValue(cty.StringVal(label))
|
||||
labelObj := newQuoted(labelToks)
|
||||
labelNode := b.children.Append(labelObj)
|
||||
b.labels.Add(labelNode)
|
||||
}
|
||||
labelsObj := newBlockLabels(labels)
|
||||
b.labels = b.children.Append(labelsObj)
|
||||
b.open = b.children.AppendUnstructuredTokens(Tokens{
|
||||
{
|
||||
Type: hclsyntax.TokenOBrace,
|
||||
|
@ -79,10 +74,68 @@ func (b *Block) Type() string {
|
|||
return string(typeNameObj.token.Bytes)
|
||||
}
|
||||
|
||||
// SetType updates the type name of the block to a given name.
|
||||
func (b *Block) SetType(typeName string) {
|
||||
nameTok := newIdentToken(typeName)
|
||||
nameObj := newIdentifier(nameTok)
|
||||
b.typeName.ReplaceWith(nameObj)
|
||||
}
|
||||
|
||||
// Labels returns the labels of the block.
|
||||
func (b *Block) Labels() []string {
|
||||
labelNames := make([]string, 0, len(b.labels))
|
||||
list := b.labels.List()
|
||||
return b.labelsObj().Current()
|
||||
}
|
||||
|
||||
// SetLabels updates the labels of the block to given labels.
|
||||
// Since we cannot assume that old and new labels are equal in length,
|
||||
// remove old labels and insert new ones before TokenOBrace.
|
||||
func (b *Block) SetLabels(labels []string) {
|
||||
b.labelsObj().Replace(labels)
|
||||
}
|
||||
|
||||
// labelsObj returns the internal node content representation of the block
|
||||
// labels. This is not part of the public API because we're intentionally
|
||||
// exposing only a limited API to get/set labels on the block itself in a
|
||||
// manner similar to the main hcl.Block type, but our block accessors all
|
||||
// use this to get the underlying node content to work with.
|
||||
func (b *Block) labelsObj() *blockLabels {
|
||||
return b.labels.content.(*blockLabels)
|
||||
}
|
||||
|
||||
type blockLabels struct {
|
||||
inTree
|
||||
|
||||
items nodeSet
|
||||
}
|
||||
|
||||
func newBlockLabels(labels []string) *blockLabels {
|
||||
ret := &blockLabels{
|
||||
inTree: newInTree(),
|
||||
items: newNodeSet(),
|
||||
}
|
||||
|
||||
ret.Replace(labels)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (bl *blockLabels) Replace(newLabels []string) {
|
||||
bl.inTree.children.Clear()
|
||||
bl.items.Clear()
|
||||
|
||||
for _, label := range newLabels {
|
||||
labelToks := TokensForValue(cty.StringVal(label))
|
||||
// Force a new label to use the quoted form, which is the idiomatic
|
||||
// form. The unquoted form is supported in HCL 2 only for compatibility
|
||||
// with historical use in HCL 1.
|
||||
labelObj := newQuoted(labelToks)
|
||||
labelNode := bl.children.Append(labelObj)
|
||||
bl.items.Add(labelNode)
|
||||
}
|
||||
}
|
||||
|
||||
func (bl *blockLabels) Current() []string {
|
||||
labelNames := make([]string, 0, len(bl.items))
|
||||
list := bl.items.List()
|
||||
|
||||
for _, label := range list {
|
||||
switch labelObj := label.content.(type) {
|
||||
|
@ -106,6 +159,12 @@ func (b *Block) Labels() []string {
|
|||
if !diags.HasErrors() {
|
||||
labelNames = append(labelNames, labelString)
|
||||
}
|
||||
} else if len(tokens) == 2 &&
|
||||
tokens[0].Type == hclsyntax.TokenOQuote &&
|
||||
tokens[1].Type == hclsyntax.TokenCQuote {
|
||||
// An open quote followed immediately by a closing quote is a
|
||||
// valid but unusual blank string label.
|
||||
labelNames = append(labelNames, "")
|
||||
}
|
||||
|
||||
default:
|
||||
|
|
|
@ -263,6 +263,10 @@ func spaceAfterToken(subject, before, after *Token) bool {
|
|||
case after.Type == hclsyntax.TokenOBrack && (subject.Type == hclsyntax.TokenIdent || subject.Type == hclsyntax.TokenNumberLit || tokenBracketChange(subject) < 0):
|
||||
return false
|
||||
|
||||
case subject.Type == hclsyntax.TokenBang:
|
||||
// No space after a bang
|
||||
return false
|
||||
|
||||
case subject.Type == hclsyntax.TokenMinus:
|
||||
// Since a minus can either be subtraction or negation, and the latter
|
||||
// should _not_ have a space after it, we need to use some heuristics
|
||||
|
|
|
@ -130,6 +130,36 @@ func (ns *nodes) AppendNode(n *node) {
|
|||
}
|
||||
}
|
||||
|
||||
// Insert inserts a nodeContent at a given position.
|
||||
// This is just a wrapper for InsertNode. See InsertNode for details.
|
||||
func (ns *nodes) Insert(pos *node, c nodeContent) *node {
|
||||
n := &node{
|
||||
content: c,
|
||||
}
|
||||
ns.InsertNode(pos, n)
|
||||
n.list = ns
|
||||
return n
|
||||
}
|
||||
|
||||
// InsertNode inserts a node at a given position.
|
||||
// The first argument is a node reference before which to insert.
|
||||
// To insert it to an empty list, set position to nil.
|
||||
func (ns *nodes) InsertNode(pos *node, n *node) {
|
||||
if pos == nil {
|
||||
// inserts n to empty list.
|
||||
ns.first = n
|
||||
ns.last = n
|
||||
} else {
|
||||
// inserts n before pos.
|
||||
pos.before.after = n
|
||||
n.before = pos.before
|
||||
pos.before = n
|
||||
n.after = pos
|
||||
}
|
||||
|
||||
n.list = ns
|
||||
}
|
||||
|
||||
func (ns *nodes) AppendUnstructuredTokens(tokens Tokens) *node {
|
||||
if len(tokens) == 0 {
|
||||
return nil
|
||||
|
@ -177,6 +207,12 @@ func (ns nodeSet) Remove(n *node) {
|
|||
delete(ns, n)
|
||||
}
|
||||
|
||||
func (ns nodeSet) Clear() {
|
||||
for n := range ns {
|
||||
delete(ns, n)
|
||||
}
|
||||
}
|
||||
|
||||
func (ns nodeSet) List() []*node {
|
||||
if len(ns) == 0 {
|
||||
return nil
|
||||
|
|
|
@ -289,7 +289,6 @@ func parseAttribute(nativeAttr *hclsyntax.Attribute, from, leadComments, lineCom
|
|||
func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments, newline inputTokens) *node {
|
||||
block := &Block{
|
||||
inTree: newInTree(),
|
||||
labels: newNodeSet(),
|
||||
}
|
||||
children := block.inTree.children
|
||||
|
||||
|
@ -312,24 +311,13 @@ func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments,
|
|||
children.AppendNode(in)
|
||||
}
|
||||
|
||||
for _, rng := range nativeBlock.LabelRanges {
|
||||
var labelTokens inputTokens
|
||||
before, labelTokens, from = from.Partition(rng)
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
tokens := labelTokens.Tokens()
|
||||
var ln *node
|
||||
if len(tokens) == 1 && tokens[0].Type == hclsyntax.TokenIdent {
|
||||
ln = newNode(newIdentifier(tokens[0]))
|
||||
} else {
|
||||
ln = newNode(newQuoted(tokens))
|
||||
}
|
||||
block.labels.Add(ln)
|
||||
children.AppendNode(ln)
|
||||
}
|
||||
before, labelsNode, from := parseBlockLabels(nativeBlock, from)
|
||||
block.labels = labelsNode
|
||||
children.AppendNode(labelsNode)
|
||||
|
||||
before, oBrace, from := from.Partition(nativeBlock.OpenBraceRange)
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
children.AppendUnstructuredTokens(oBrace.Tokens())
|
||||
block.open = children.AppendUnstructuredTokens(oBrace.Tokens())
|
||||
|
||||
// We go a bit out of order here: we go hunting for the closing brace
|
||||
// so that we have a delimited body, but then we'll deal with the body
|
||||
|
@ -342,7 +330,7 @@ func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments,
|
|||
children.AppendNode(body)
|
||||
children.AppendUnstructuredTokens(after.Tokens())
|
||||
|
||||
children.AppendUnstructuredTokens(cBrace.Tokens())
|
||||
block.close = children.AppendUnstructuredTokens(cBrace.Tokens())
|
||||
|
||||
// stragglers
|
||||
children.AppendUnstructuredTokens(from.Tokens())
|
||||
|
@ -356,6 +344,34 @@ func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments,
|
|||
return newNode(block)
|
||||
}
|
||||
|
||||
func parseBlockLabels(nativeBlock *hclsyntax.Block, from inputTokens) (inputTokens, *node, inputTokens) {
|
||||
labelsObj := newBlockLabels(nil)
|
||||
children := labelsObj.children
|
||||
|
||||
var beforeAll inputTokens
|
||||
for i, rng := range nativeBlock.LabelRanges {
|
||||
var before, labelTokens inputTokens
|
||||
before, labelTokens, from = from.Partition(rng)
|
||||
if i == 0 {
|
||||
beforeAll = before
|
||||
} else {
|
||||
children.AppendUnstructuredTokens(before.Tokens())
|
||||
}
|
||||
tokens := labelTokens.Tokens()
|
||||
var ln *node
|
||||
if len(tokens) == 1 && tokens[0].Type == hclsyntax.TokenIdent {
|
||||
ln = newNode(newIdentifier(tokens[0]))
|
||||
} else {
|
||||
ln = newNode(newQuoted(tokens))
|
||||
}
|
||||
labelsObj.items.Add(ln)
|
||||
children.AppendNode(ln)
|
||||
}
|
||||
|
||||
after := from
|
||||
return beforeAll, newNode(labelsObj), after
|
||||
}
|
||||
|
||||
func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *node {
|
||||
expr := newExpression()
|
||||
children := expr.inTree.children
|
||||
|
|
|
@ -8,15 +8,23 @@ import (
|
|||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func parseFileContent(buf []byte, filename string) (node, hcl.Diagnostics) {
|
||||
tokens := scan(buf, pos{
|
||||
Filename: filename,
|
||||
Pos: hcl.Pos{
|
||||
Byte: 0,
|
||||
Line: 1,
|
||||
Column: 1,
|
||||
},
|
||||
})
|
||||
func parseFileContent(buf []byte, filename string, start hcl.Pos) (node, hcl.Diagnostics) {
|
||||
tokens := scan(buf, pos{Filename: filename, Pos: start})
|
||||
p := newPeeker(tokens)
|
||||
node, diags := parseValue(p)
|
||||
if len(diags) == 0 && p.Peek().Type != tokenEOF {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Extraneous data after value",
|
||||
Detail: "Extra characters appear after the JSON value.",
|
||||
Subject: p.Peek().Range.Ptr(),
|
||||
})
|
||||
}
|
||||
return node, diags
|
||||
}
|
||||
|
||||
func parseExpression(buf []byte, filename string, start hcl.Pos) (node, hcl.Diagnostics) {
|
||||
tokens := scan(buf, pos{Filename: filename, Pos: start})
|
||||
p := newPeeker(tokens)
|
||||
node, diags := parseValue(p)
|
||||
if len(diags) == 0 && p.Peek().Type != tokenEOF {
|
||||
|
|
|
@ -18,7 +18,16 @@ import (
|
|||
// from its HasErrors method. If HasErrors returns true, the file represents
|
||||
// the subset of data that was able to be parsed, which may be none.
|
||||
func Parse(src []byte, filename string) (*hcl.File, hcl.Diagnostics) {
|
||||
rootNode, diags := parseFileContent(src, filename)
|
||||
return ParseWithStartPos(src, filename, hcl.Pos{Byte: 0, Line: 1, Column: 1})
|
||||
}
|
||||
|
||||
// ParseWithStartPos attempts to parse like json.Parse, but unlike json.Parse
|
||||
// you can pass a start position of the given JSON as a hcl.Pos.
|
||||
//
|
||||
// In most cases json.Parse should be sufficient, but it can be useful for parsing
|
||||
// a part of JSON with correct positions.
|
||||
func ParseWithStartPos(src []byte, filename string, start hcl.Pos) (*hcl.File, hcl.Diagnostics) {
|
||||
rootNode, diags := parseFileContent(src, filename, start)
|
||||
|
||||
switch rootNode.(type) {
|
||||
case *objectVal, *arrayVal:
|
||||
|
@ -62,6 +71,20 @@ func Parse(src []byte, filename string) (*hcl.File, hcl.Diagnostics) {
|
|||
return file, diags
|
||||
}
|
||||
|
||||
// ParseExpression parses the given buffer as a standalone JSON expression,
|
||||
// returning it as an instance of Expression.
|
||||
func ParseExpression(src []byte, filename string) (hcl.Expression, hcl.Diagnostics) {
|
||||
return ParseExpressionWithStartPos(src, filename, hcl.Pos{Byte: 0, Line: 1, Column: 1})
|
||||
}
|
||||
|
||||
// ParseExpressionWithStartPos parses like json.ParseExpression, but unlike
|
||||
// json.ParseExpression you can pass a start position of the given JSON
|
||||
// expression as a hcl.Pos.
|
||||
func ParseExpressionWithStartPos(src []byte, filename string, start hcl.Pos) (hcl.Expression, hcl.Diagnostics) {
|
||||
node, diags := parseExpression(src, filename, start)
|
||||
return &expression{src: node}, diags
|
||||
}
|
||||
|
||||
// ParseFile is a convenience wrapper around Parse that first attempts to load
|
||||
// data from the given filename, passing the result to Parse if successful.
|
||||
//
|
||||
|
|
|
@ -76,7 +76,10 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics)
|
|||
}
|
||||
}
|
||||
|
||||
has := collection.HasIndex(key)
|
||||
// Here we drop marks from HasIndex result, in order to allow basic
|
||||
// traversal of a marked list, tuple, or map in the same way we can
|
||||
// traverse a marked object
|
||||
has, _ := collection.HasIndex(key).Unmark()
|
||||
if !has.IsKnown() {
|
||||
if ty.IsTupleType() {
|
||||
return cty.DynamicVal, nil
|
||||
|
@ -217,7 +220,12 @@ func GetAttr(obj cty.Value, attrName string, srcRange *Range) (cty.Value, Diagno
|
|||
}
|
||||
|
||||
idx := cty.StringVal(attrName)
|
||||
if obj.HasIndex(idx).False() {
|
||||
|
||||
// Here we drop marks from HasIndex result, in order to allow basic
|
||||
// traversal of a marked map in the same way we can traverse a marked
|
||||
// object
|
||||
hasIndex, _ := obj.HasIndex(idx).Unmark()
|
||||
if hasIndex.False() {
|
||||
return cty.DynamicVal, Diagnostics{
|
||||
{
|
||||
Severity: DiagError,
|
||||
|
|
10
vendor/github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps/http_config.go
generated
vendored
10
vendor/github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps/http_config.go
generated
vendored
|
@ -52,6 +52,11 @@ func (c *HTTPConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
c.HTTPPortMax = 9000
|
||||
}
|
||||
|
||||
if c.HTTPInterface != "" && c.HTTPAddress != "" {
|
||||
errs = append(errs,
|
||||
errors.New("either http_interface or http_bind_address can be specified"))
|
||||
}
|
||||
|
||||
if c.HTTPAddress == "" {
|
||||
c.HTTPAddress = "0.0.0.0"
|
||||
}
|
||||
|
@ -61,10 +66,5 @@ func (c *HTTPConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
errors.New("http_port_min must be less than http_port_max"))
|
||||
}
|
||||
|
||||
if c.HTTPInterface != "" && c.HTTPAddress == "0.0.0.0" {
|
||||
errs = append(errs,
|
||||
errors.New("either http_interface of http_bind_address can be specified"))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
2
vendor/github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps/iso_config.go
generated
vendored
2
vendor/github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps/iso_config.go
generated
vendored
|
@ -90,7 +90,7 @@ type ISOConfig struct {
|
|||
// * ebfb681885ddf1234c18094a45bbeafd91467911
|
||||
// * sha256:ed363350696a726b7932db864dda019bd2017365c9e299627830f06954643f93
|
||||
// * ed363350696a726b7932db864dda019bd2017365c9e299627830f06954643f93
|
||||
// * file:http://releases.ubuntu.com/20.04/MD5SUMS
|
||||
// * file:http://releases.ubuntu.com/20.04/SHA256SUMS
|
||||
// * file:file://./local/path/file.sum
|
||||
// * file:./local/path/file.sum
|
||||
// * none
|
||||
|
|
|
@ -13,6 +13,16 @@ import (
|
|||
"github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
// Use this name to make the name of the plugin in the packer template match
|
||||
// the multiplugin suffix, instead of requiring a second part.
|
||||
// For example, calling :
|
||||
// pps.RegisterProvisioner(plugin.DEFAULT_NAME, new(CommentProvisioner))
|
||||
// On a plugin named `packer-plugin-foo`, will make the `foo` provisioner available
|
||||
// with your CommentProvisioner doing that. There can only be one unnamed
|
||||
// plugin per plugin type.
|
||||
const DEFAULT_NAME = "-packer-default-plugin-name-"
|
||||
|
||||
|
||||
// Set is a plugin set. It's API is meant to be very close to what is returned
|
||||
// by plugin.Server
|
||||
// It can describe itself or run a single plugin using the CLI arguments.
|
||||
|
@ -113,7 +123,7 @@ func (i *Set) start(kind, name string) error {
|
|||
err = server.RegisterBuilder(i.Builders[name])
|
||||
case "post-processor":
|
||||
err = server.RegisterPostProcessor(i.PostProcessors[name])
|
||||
case "provisioners":
|
||||
case "provisioner":
|
||||
err = server.RegisterProvisioner(i.Provisioners[name])
|
||||
default:
|
||||
err = fmt.Errorf("Unknown plugin type: %s", kind)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
arch:
|
||||
- amd64
|
||||
- ppc64le
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
|
|
|
@ -12,11 +12,13 @@ import (
|
|||
"github.com/masterzen/winrm/soap"
|
||||
)
|
||||
|
||||
//ClientAuthRequest ClientAuthRequest
|
||||
type ClientAuthRequest struct {
|
||||
transport http.RoundTripper
|
||||
dial func(network, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
//Transport Transport
|
||||
func (c *ClientAuthRequest) Transport(endpoint *Endpoint) error {
|
||||
cert, err := tls.X509KeyPair(endpoint.Cert, endpoint.Key)
|
||||
if err != nil {
|
||||
|
@ -80,6 +82,7 @@ func parse(response *http.Response) (string, error) {
|
|||
return "", fmt.Errorf("invalid content type")
|
||||
}
|
||||
|
||||
//Post Post
|
||||
func (c ClientAuthRequest) Post(client *Client, request *soap.SoapMessage) (string, error) {
|
||||
httpClient := &http.Client{Transport: c.transport}
|
||||
|
||||
|
@ -112,6 +115,7 @@ func (c ClientAuthRequest) Post(client *Client, request *soap.SoapMessage) (stri
|
|||
return body, err
|
||||
}
|
||||
|
||||
//NewClientAuthRequestWithDial NewClientAuthRequestWithDial
|
||||
func NewClientAuthRequestWithDial(dial func(network, addr string) (net.Conn, error)) *ClientAuthRequest {
|
||||
return &ClientAuthRequest{
|
||||
dial: dial,
|
||||
|
|
|
@ -3,6 +3,7 @@ package winrm
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
@ -177,6 +178,20 @@ func (c *Client) RunWithString(command string, stdin string) (string, string, in
|
|||
return outWriter.String(), errWriter.String(), cmd.ExitCode(), cmd.err
|
||||
}
|
||||
|
||||
//RunPSWithString will basically wrap your code to execute commands in powershell.exe. Default RunWithString
|
||||
// runs commands in cmd.exe
|
||||
func (c *Client) RunPSWithString(command string, stdin string) (string, string, int, error) {
|
||||
command = Powershell(command)
|
||||
|
||||
// Let's check if we actually created a command
|
||||
if command == "" {
|
||||
return "", "", 1, errors.New("cannot encode the given command")
|
||||
}
|
||||
|
||||
// Specify powershell.exe to run encoded command
|
||||
return c.RunWithString(command, stdin)
|
||||
}
|
||||
|
||||
// RunWithInput will run command on the the remote host, writing the process stdout and stderr to
|
||||
// the given writers, and injecting the process stdin with the stdin reader.
|
||||
// Warning stdin (not stdout/stderr) are bufferized, which means reading only one byte in stdin will
|
||||
|
|
|
@ -115,12 +115,14 @@ func (c clientRequest) Post(client *Client, request *soap.SoapMessage) (string,
|
|||
return body, err
|
||||
}
|
||||
|
||||
//NewClientWithDial NewClientWithDial
|
||||
func NewClientWithDial(dial func(network, addr string) (net.Conn, error)) *clientRequest {
|
||||
return &clientRequest{
|
||||
dial: dial,
|
||||
}
|
||||
}
|
||||
|
||||
//NewClientWithProxyFunc NewClientWithProxyFunc
|
||||
func NewClientWithProxyFunc(proxyfunc func(req *http.Request) (*url.URL, error)) *clientRequest {
|
||||
return &clientRequest{
|
||||
proxyfunc: proxyfunc,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package winrm
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Azure/go-ntlmssp"
|
||||
"github.com/masterzen/winrm/soap"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ClientNTLM provides a transport via NTLMv2
|
||||
|
@ -23,11 +24,11 @@ func (c ClientNTLM) Post(client *Client, request *soap.SoapMessage) (string, err
|
|||
return c.clientRequest.Post(client, request)
|
||||
}
|
||||
|
||||
|
||||
//NewClientNTLMWithDial NewClientNTLMWithDial
|
||||
func NewClientNTLMWithDial(dial func(network, addr string) (net.Conn, error)) *ClientNTLM {
|
||||
return &ClientNTLM{
|
||||
clientRequest{
|
||||
dial:dial,
|
||||
dial: dial,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,22 +2,26 @@ package winrm
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
)
|
||||
|
||||
// Powershell wraps a PowerShell script
|
||||
// and prepares it for execution by the winrm client
|
||||
func Powershell(psCmd string) string {
|
||||
// 2 byte chars to make PowerShell happy
|
||||
wideCmd := ""
|
||||
for _, b := range []byte(psCmd) {
|
||||
wideCmd += string(b) + "\x00"
|
||||
// Disable unnecessary progress bars which considered as stderr.
|
||||
psCmd = "$ProgressPreference = 'SilentlyContinue';" + psCmd
|
||||
|
||||
// Encode string to UTF16-LE
|
||||
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
|
||||
encoded, err := encoder.String(psCmd)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Base64 encode the command
|
||||
input := []uint8(wideCmd)
|
||||
encodedCmd := base64.StdEncoding.EncodeToString(input)
|
||||
// Finally make it base64 encoded which is required for powershell.
|
||||
psCmd = base64.StdEncoding.EncodeToString([]byte(encoded))
|
||||
|
||||
// Create the powershell.exe command line to execute the script
|
||||
return fmt.Sprintf("powershell.exe -EncodedCommand %s", encodedCmd)
|
||||
// Specify powershell.exe to run encoded command
|
||||
return "powershell.exe -EncodedCommand " + psCmd
|
||||
}
|
||||
|
|
|
@ -47,14 +47,14 @@ func NewOpenShellRequest(uri string, params *Parameters) *soap.SoapMessage {
|
|||
}
|
||||
|
||||
// NewDeleteShellRequest ...
|
||||
func NewDeleteShellRequest(uri, shellId string, params *Parameters) *soap.SoapMessage {
|
||||
func NewDeleteShellRequest(uri, shellID string, params *Parameters) *soap.SoapMessage {
|
||||
if params == nil {
|
||||
params = DefaultParameters
|
||||
}
|
||||
message := soap.NewMessage()
|
||||
defaultHeaders(message, uri, params).
|
||||
Action("http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete").
|
||||
ShellId(shellId).
|
||||
ShellId(shellID).
|
||||
ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd").
|
||||
Build()
|
||||
|
||||
|
@ -64,7 +64,7 @@ func NewDeleteShellRequest(uri, shellId string, params *Parameters) *soap.SoapMe
|
|||
}
|
||||
|
||||
// NewExecuteCommandRequest exec command on specific shellID
|
||||
func NewExecuteCommandRequest(uri, shellId, command string, arguments []string, params *Parameters) *soap.SoapMessage {
|
||||
func NewExecuteCommandRequest(uri, shellID, command string, arguments []string, params *Parameters) *soap.SoapMessage {
|
||||
if params == nil {
|
||||
params = DefaultParameters
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func NewExecuteCommandRequest(uri, shellId, command string, arguments []string,
|
|||
defaultHeaders(message, uri, params).
|
||||
Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command").
|
||||
ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd").
|
||||
ShellId(shellId).
|
||||
ShellId(shellID).
|
||||
AddOption(soap.NewHeaderOption("WINRS_CONSOLEMODE_STDIN", "TRUE")).
|
||||
AddOption(soap.NewHeaderOption("WINRS_SKIP_CMD_SHELL", "FALSE")).
|
||||
Build()
|
||||
|
@ -93,7 +93,8 @@ func NewExecuteCommandRequest(uri, shellId, command string, arguments []string,
|
|||
return message
|
||||
}
|
||||
|
||||
func NewGetOutputRequest(uri, shellId, commandId, streams string, params *Parameters) *soap.SoapMessage {
|
||||
//NewGetOutputRequest NewGetOutputRequest
|
||||
func NewGetOutputRequest(uri, shellID, commandID, streams string, params *Parameters) *soap.SoapMessage {
|
||||
if params == nil {
|
||||
params = DefaultParameters
|
||||
}
|
||||
|
@ -101,18 +102,19 @@ func NewGetOutputRequest(uri, shellId, commandId, streams string, params *Parame
|
|||
defaultHeaders(message, uri, params).
|
||||
Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive").
|
||||
ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd").
|
||||
ShellId(shellId).
|
||||
ShellId(shellID).
|
||||
Build()
|
||||
|
||||
receive := message.CreateBodyElement("Receive", soap.DOM_NS_WIN_SHELL)
|
||||
desiredStreams := message.CreateElement(receive, "DesiredStream", soap.DOM_NS_WIN_SHELL)
|
||||
desiredStreams.SetAttr("CommandId", commandId)
|
||||
desiredStreams.SetAttr("CommandId", commandID)
|
||||
desiredStreams.SetContent(streams)
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func NewSendInputRequest(uri, shellId, commandId string, input []byte, eof bool, params *Parameters) *soap.SoapMessage {
|
||||
//NewSendInputRequest NewSendInputRequest
|
||||
func NewSendInputRequest(uri, shellID, commandID string, input []byte, eof bool, params *Parameters) *soap.SoapMessage {
|
||||
if params == nil {
|
||||
params = DefaultParameters
|
||||
}
|
||||
|
@ -121,7 +123,7 @@ func NewSendInputRequest(uri, shellId, commandId string, input []byte, eof bool,
|
|||
defaultHeaders(message, uri, params).
|
||||
Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send").
|
||||
ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd").
|
||||
ShellId(shellId).
|
||||
ShellId(shellID).
|
||||
Build()
|
||||
|
||||
content := base64.StdEncoding.EncodeToString(input)
|
||||
|
@ -129,7 +131,7 @@ func NewSendInputRequest(uri, shellId, commandId string, input []byte, eof bool,
|
|||
send := message.CreateBodyElement("Send", soap.DOM_NS_WIN_SHELL)
|
||||
streams := message.CreateElement(send, "Stream", soap.DOM_NS_WIN_SHELL)
|
||||
streams.SetAttr("Name", "stdin")
|
||||
streams.SetAttr("CommandId", commandId)
|
||||
streams.SetAttr("CommandId", commandID)
|
||||
streams.SetContent(content)
|
||||
if eof {
|
||||
streams.SetAttr("End", "true")
|
||||
|
@ -137,7 +139,8 @@ func NewSendInputRequest(uri, shellId, commandId string, input []byte, eof bool,
|
|||
return message
|
||||
}
|
||||
|
||||
func NewSignalRequest(uri string, shellId string, commandId string, params *Parameters) *soap.SoapMessage {
|
||||
//NewSignalRequest NewSignalRequest
|
||||
func NewSignalRequest(uri string, shellID string, commandID string, params *Parameters) *soap.SoapMessage {
|
||||
if params == nil {
|
||||
params = DefaultParameters
|
||||
}
|
||||
|
@ -146,11 +149,11 @@ func NewSignalRequest(uri string, shellId string, commandId string, params *Para
|
|||
defaultHeaders(message, uri, params).
|
||||
Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal").
|
||||
ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd").
|
||||
ShellId(shellId).
|
||||
ShellId(shellID).
|
||||
Build()
|
||||
|
||||
signal := message.CreateBodyElement("Signal", soap.DOM_NS_WIN_SHELL)
|
||||
signal.SetAttr("CommandId", commandId)
|
||||
signal.SetAttr("CommandId", commandID)
|
||||
code := message.CreateElement(signal, "Code", soap.DOM_NS_WIN_SHELL)
|
||||
code.SetContent("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate")
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ func xPath(node tree.Node, xpath string) (tree.NodeSet, error) {
|
|||
return nodes, nil
|
||||
}
|
||||
|
||||
//ParseOpenShellResponse ParseOpenShellResponse
|
||||
func ParseOpenShellResponse(response string) (string, error) {
|
||||
doc, err := xmltree.ParseXML(strings.NewReader(response))
|
||||
if err != nil {
|
||||
|
@ -52,6 +53,7 @@ func ParseOpenShellResponse(response string) (string, error) {
|
|||
return first(doc, "//w:Selector[@Name='ShellId']")
|
||||
}
|
||||
|
||||
//ParseExecuteCommandResponse ParseExecuteCommandResponse
|
||||
func ParseExecuteCommandResponse(response string) (string, error) {
|
||||
doc, err := xmltree.ParseXML(strings.NewReader(response))
|
||||
if err != nil {
|
||||
|
@ -60,6 +62,7 @@ func ParseExecuteCommandResponse(response string) (string, error) {
|
|||
return first(doc, "//rsp:CommandId")
|
||||
}
|
||||
|
||||
//ParseSlurpOutputErrResponse ParseSlurpOutputErrResponse
|
||||
func ParseSlurpOutputErrResponse(response string, stdout, stderr io.Writer) (bool, int, error) {
|
||||
var (
|
||||
finished bool
|
||||
|
@ -94,6 +97,7 @@ func ParseSlurpOutputErrResponse(response string, stdout, stderr io.Writer) (boo
|
|||
return finished, exitCode, err
|
||||
}
|
||||
|
||||
//ParseSlurpOutputResponse ParseSlurpOutputResponse
|
||||
func ParseSlurpOutputResponse(response string, stream io.Writer, streamType string) (bool, int, error) {
|
||||
var (
|
||||
finished bool
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- "1.14.x"
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test
|
||||
- go test -bench . -benchmem
|
|
@ -1,3 +1,27 @@
|
|||
## 1.4.0
|
||||
|
||||
* A new decode hook type `DecodeHookFuncValue` has been added that has
|
||||
access to the full values. [GH-183]
|
||||
* Squash is now supported with embedded fields that are struct pointers [GH-205]
|
||||
* Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206]
|
||||
|
||||
## 1.3.3
|
||||
|
||||
* Decoding maps from maps creates a settable value for decode hooks [GH-203]
|
||||
|
||||
## 1.3.2
|
||||
|
||||
* Decode into interface type with a struct value is supported [GH-187]
|
||||
|
||||
## 1.3.1
|
||||
|
||||
* Squash should only squash embedded structs. [GH-194]
|
||||
|
||||
## 1.3.0
|
||||
|
||||
* Added `",omitempty"` support. This will ignore zero values in the source
|
||||
structure when encoding. [GH-145]
|
||||
|
||||
## 1.2.3
|
||||
|
||||
* Fix duplicate entries in Keys list with pointer values. [GH-185]
|
||||
|
|
|
@ -16,10 +16,11 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
|
|||
// Create variables here so we can reference them with the reflect pkg
|
||||
var f1 DecodeHookFuncType
|
||||
var f2 DecodeHookFuncKind
|
||||
var f3 DecodeHookFuncValue
|
||||
|
||||
// Fill in the variables into this interface and the rest is done
|
||||
// automatically using the reflect package.
|
||||
potential := []interface{}{f1, f2}
|
||||
potential := []interface{}{f1, f2, f3}
|
||||
|
||||
v := reflect.ValueOf(h)
|
||||
vt := v.Type()
|
||||
|
@ -38,13 +39,15 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
|
|||
// that took reflect.Kind instead of reflect.Type.
|
||||
func DecodeHookExec(
|
||||
raw DecodeHookFunc,
|
||||
from reflect.Type, to reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
from reflect.Value, to reflect.Value) (interface{}, error) {
|
||||
|
||||
switch f := typedDecodeHook(raw).(type) {
|
||||
case DecodeHookFuncType:
|
||||
return f(from, to, data)
|
||||
return f(from.Type(), to.Type(), from.Interface())
|
||||
case DecodeHookFuncKind:
|
||||
return f(from.Kind(), to.Kind(), data)
|
||||
return f(from.Kind(), to.Kind(), from.Interface())
|
||||
case DecodeHookFuncValue:
|
||||
return f(from, to)
|
||||
default:
|
||||
return nil, errors.New("invalid decode hook signature")
|
||||
}
|
||||
|
@ -56,22 +59,16 @@ func DecodeHookExec(
|
|||
// The composed funcs are called in order, with the result of the
|
||||
// previous transformation.
|
||||
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
|
||||
var err error
|
||||
var data interface{}
|
||||
newFrom := f
|
||||
for _, f1 := range fs {
|
||||
data, err = DecodeHookExec(f1, f, t, data)
|
||||
data, err = DecodeHookExec(f1, newFrom, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Modify the from kind to be correct with the new data
|
||||
f = nil
|
||||
if val := reflect.ValueOf(data); val.IsValid() {
|
||||
f = val.Type()
|
||||
}
|
||||
newFrom = reflect.ValueOf(data)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
|
@ -215,3 +212,21 @@ func WeaklyTypedHook(
|
|||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func RecursiveStructToMapHookFunc() DecodeHookFunc {
|
||||
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
|
||||
if f.Kind() != reflect.Struct {
|
||||
return f.Interface(), nil
|
||||
}
|
||||
|
||||
var i interface{} = struct{}{}
|
||||
if t.Type() != reflect.TypeOf(&i).Elem() {
|
||||
return f.Interface(), nil
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
t.Set(reflect.ValueOf(m))
|
||||
|
||||
return f.Interface(), nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,6 +100,47 @@
|
|||
// "address": "123 Maple St.",
|
||||
// }
|
||||
//
|
||||
// Omit Empty Values
|
||||
//
|
||||
// When decoding from a struct to any other value, you may use the
|
||||
// ",omitempty" suffix on your tag to omit that value if it equates to
|
||||
// the zero value. The zero value of all types is specified in the Go
|
||||
// specification.
|
||||
//
|
||||
// For example, the zero type of a numeric type is zero ("0"). If the struct
|
||||
// field value is zero and a numeric type, the field is empty, and it won't
|
||||
// be encoded into the destination type.
|
||||
//
|
||||
// type Source {
|
||||
// Age int `mapstructure:",omitempty"`
|
||||
// }
|
||||
//
|
||||
// Unexported fields
|
||||
//
|
||||
// Since unexported (private) struct fields cannot be set outside the package
|
||||
// where they are defined, the decoder will simply skip them.
|
||||
//
|
||||
// For this output type definition:
|
||||
//
|
||||
// type Exported struct {
|
||||
// private string // this unexported field will be skipped
|
||||
// Public string
|
||||
// }
|
||||
//
|
||||
// Using this map as input:
|
||||
//
|
||||
// map[string]interface{}{
|
||||
// "private": "I will be ignored",
|
||||
// "Public": "I made it through!",
|
||||
// }
|
||||
//
|
||||
// The following struct will be decoded:
|
||||
//
|
||||
// type Exported struct {
|
||||
// private: "" // field is left with an empty string (zero value)
|
||||
// Public: "I made it through!"
|
||||
// }
|
||||
//
|
||||
// Other Configuration
|
||||
//
|
||||
// mapstructure is highly configurable. See the DecoderConfig struct
|
||||
|
@ -139,6 +180,10 @@ type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface
|
|||
// source and target types.
|
||||
type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error)
|
||||
|
||||
// DecodeHookFuncRaw is a DecodeHookFunc which has complete access to both the source and target
|
||||
// values.
|
||||
type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error)
|
||||
|
||||
// DecoderConfig is the configuration that is used to create a new decoder
|
||||
// and allows customization of various aspects of decoding.
|
||||
type DecoderConfig struct {
|
||||
|
@ -368,9 +413,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
|
|||
if d.config.DecodeHook != nil {
|
||||
// We have a DecodeHook, so let's pre-process the input.
|
||||
var err error
|
||||
input, err = DecodeHookExec(
|
||||
d.config.DecodeHook,
|
||||
inputVal.Type(), outVal.Type(), input)
|
||||
input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding '%s': %s", name, err)
|
||||
}
|
||||
|
@ -422,7 +465,34 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
|
|||
// value to "data" of that type.
|
||||
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
|
||||
if val.IsValid() && val.Elem().IsValid() {
|
||||
return d.decode(name, data, val.Elem())
|
||||
elem := val.Elem()
|
||||
|
||||
// If we can't address this element, then its not writable. Instead,
|
||||
// we make a copy of the value (which is a pointer and therefore
|
||||
// writable), decode into that, and replace the whole value.
|
||||
copied := false
|
||||
if !elem.CanAddr() {
|
||||
copied = true
|
||||
|
||||
// Make *T
|
||||
copy := reflect.New(elem.Type())
|
||||
|
||||
// *T = elem
|
||||
copy.Elem().Set(elem)
|
||||
|
||||
// Set elem so we decode into it
|
||||
elem = copy
|
||||
}
|
||||
|
||||
// Decode. If we have an error then return. We also return right
|
||||
// away if we're not a copy because that means we decoded directly.
|
||||
if err := d.decode(name, data, elem); err != nil || !copied {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we're a copy, we need to set te final result
|
||||
val.Set(elem.Elem())
|
||||
return nil
|
||||
}
|
||||
|
||||
dataVal := reflect.ValueOf(data)
|
||||
|
@ -494,8 +564,8 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value)
|
|||
|
||||
if !converted {
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
|
||||
name, val.Type(), dataVal.Type(), data)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -520,7 +590,12 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
|
|||
val.SetInt(0)
|
||||
}
|
||||
case dataKind == reflect.String && d.config.WeaklyTypedInput:
|
||||
i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits())
|
||||
str := dataVal.String()
|
||||
if str == "" {
|
||||
str = "0"
|
||||
}
|
||||
|
||||
i, err := strconv.ParseInt(str, 0, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetInt(i)
|
||||
} else {
|
||||
|
@ -536,8 +611,8 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
|
|||
val.SetInt(i)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
|
||||
name, val.Type(), dataVal.Type(), data)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -572,7 +647,12 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
|
|||
val.SetUint(0)
|
||||
}
|
||||
case dataKind == reflect.String && d.config.WeaklyTypedInput:
|
||||
i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits())
|
||||
str := dataVal.String()
|
||||
if str == "" {
|
||||
str = "0"
|
||||
}
|
||||
|
||||
i, err := strconv.ParseUint(str, 0, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetUint(i)
|
||||
} else {
|
||||
|
@ -592,8 +672,8 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
|
|||
val.SetUint(uint64(i))
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
|
||||
name, val.Type(), dataVal.Type(), data)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -623,8 +703,8 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e
|
|||
}
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
|
||||
name, val.Type(), dataVal.Type(), data)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -649,7 +729,12 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value)
|
|||
val.SetFloat(0)
|
||||
}
|
||||
case dataKind == reflect.String && d.config.WeaklyTypedInput:
|
||||
f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits())
|
||||
str := dataVal.String()
|
||||
if str == "" {
|
||||
str = "0"
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(str, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetFloat(f)
|
||||
} else {
|
||||
|
@ -665,8 +750,8 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value)
|
|||
val.SetFloat(i)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
|
||||
name, val.Type(), dataVal.Type(), data)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -717,7 +802,7 @@ func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val ref
|
|||
|
||||
for i := 0; i < dataVal.Len(); i++ {
|
||||
err := d.decode(
|
||||
fmt.Sprintf("%s[%d]", name, i),
|
||||
name+"["+strconv.Itoa(i)+"]",
|
||||
dataVal.Index(i).Interface(), val)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -750,7 +835,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle
|
|||
}
|
||||
|
||||
for _, k := range dataVal.MapKeys() {
|
||||
fieldName := fmt.Sprintf("%s[%s]", name, k)
|
||||
fieldName := name + "[" + k.String() + "]"
|
||||
|
||||
// First decode the key into the proper type
|
||||
currentKey := reflect.Indirect(reflect.New(valKeyType))
|
||||
|
@ -794,35 +879,40 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
|||
// Next get the actual value of this field and verify it is assignable
|
||||
// to the map value.
|
||||
v := dataVal.Field(i)
|
||||
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
|
||||
// Handle embedded struct pointers as embedded structs.
|
||||
v = v.Elem()
|
||||
}
|
||||
if !v.Type().AssignableTo(valMap.Type().Elem()) {
|
||||
return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
|
||||
}
|
||||
|
||||
tagValue := f.Tag.Get(d.config.TagName)
|
||||
tagParts := strings.Split(tagValue, ",")
|
||||
|
||||
// Determine the name of the key in the map
|
||||
keyName := f.Name
|
||||
if tagParts[0] != "" {
|
||||
if tagParts[0] == "-" {
|
||||
continue
|
||||
}
|
||||
keyName = tagParts[0]
|
||||
}
|
||||
|
||||
// If Squash is set in the config, we squash the field down.
|
||||
squash := d.config.Squash && v.Kind() == reflect.Struct
|
||||
// If "squash" is specified in the tag, we squash the field down.
|
||||
if !squash {
|
||||
for _, tag := range tagParts[1:] {
|
||||
if tag == "squash" {
|
||||
squash = true
|
||||
break
|
||||
}
|
||||
squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
|
||||
// Determine the name of the key in the map
|
||||
if index := strings.Index(tagValue, ","); index != -1 {
|
||||
if tagValue[:index] == "-" {
|
||||
continue
|
||||
}
|
||||
// If "omitempty" is specified in the tag, it ignores empty values.
|
||||
if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
|
||||
continue
|
||||
}
|
||||
|
||||
// If "squash" is specified in the tag, we squash the field down.
|
||||
squash = !squash && strings.Index(tagValue[index+1:], "squash") != -1
|
||||
if squash && v.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
|
||||
}
|
||||
keyName = tagValue[:index]
|
||||
} else if len(tagValue) > 0 {
|
||||
if tagValue == "-" {
|
||||
continue
|
||||
}
|
||||
keyName = tagValue
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
|
@ -837,11 +927,22 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
|||
mType := reflect.MapOf(vKeyType, vElemType)
|
||||
vMap := reflect.MakeMap(mType)
|
||||
|
||||
err := d.decode(keyName, x.Interface(), vMap)
|
||||
// Creating a pointer to a map so that other methods can completely
|
||||
// overwrite the map if need be (looking at you decodeMapFromMap). The
|
||||
// indirection allows the underlying map to be settable (CanSet() == true)
|
||||
// where as reflect.MakeMap returns an unsettable map.
|
||||
addrVal := reflect.New(vMap.Type())
|
||||
reflect.Indirect(addrVal).Set(vMap)
|
||||
|
||||
err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// the underlying map may have been completely overwritten so pull
|
||||
// it indirectly out of the enclosing value.
|
||||
vMap = reflect.Indirect(addrVal)
|
||||
|
||||
if squash {
|
||||
for _, k := range vMap.MapKeys() {
|
||||
valMap.SetMapIndex(k, vMap.MapIndex(k))
|
||||
|
@ -915,8 +1016,8 @@ func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) e
|
|||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
if val.Type() != dataVal.Type() {
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
|
||||
name, val.Type(), dataVal.Type(), data)
|
||||
}
|
||||
val.Set(dataVal)
|
||||
return nil
|
||||
|
@ -982,7 +1083,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
|||
}
|
||||
currentField := valSlice.Index(i)
|
||||
|
||||
fieldName := fmt.Sprintf("%s[%d]", name, i)
|
||||
fieldName := name + "[" + strconv.Itoa(i) + "]"
|
||||
if err := d.decode(fieldName, currentData, currentField); err != nil {
|
||||
errors = appendErrors(errors, err)
|
||||
}
|
||||
|
@ -1049,7 +1150,7 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value)
|
|||
currentData := dataVal.Index(i).Interface()
|
||||
currentField := valArray.Index(i)
|
||||
|
||||
fieldName := fmt.Sprintf("%s[%d]", name, i)
|
||||
fieldName := name + "[" + strconv.Itoa(i) + "]"
|
||||
if err := d.decode(fieldName, currentData, currentField); err != nil {
|
||||
errors = appendErrors(errors, err)
|
||||
}
|
||||
|
@ -1085,13 +1186,23 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
|
|||
// Not the most efficient way to do this but we can optimize later if
|
||||
// we want to. To convert from struct to struct we go to map first
|
||||
// as an intermediary.
|
||||
m := make(map[string]interface{})
|
||||
mval := reflect.Indirect(reflect.ValueOf(&m))
|
||||
if err := d.decodeMapFromStruct(name, dataVal, mval, mval); err != nil {
|
||||
|
||||
// Make a new map to hold our result
|
||||
mapType := reflect.TypeOf((map[string]interface{})(nil))
|
||||
mval := reflect.MakeMap(mapType)
|
||||
|
||||
// Creating a pointer to a map so that other methods can completely
|
||||
// overwrite the map if need be (looking at you decodeMapFromMap). The
|
||||
// indirection allows the underlying map to be settable (CanSet() == true)
|
||||
// where as reflect.MakeMap returns an unsettable map.
|
||||
addrVal := reflect.New(mval.Type())
|
||||
|
||||
reflect.Indirect(addrVal).Set(mval)
|
||||
if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := d.decodeStructFromMap(name, mval, val)
|
||||
result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val)
|
||||
return result
|
||||
|
||||
default:
|
||||
|
@ -1142,10 +1253,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
fieldType := structType.Field(i)
|
||||
fieldKind := fieldType.Type.Kind()
|
||||
fieldVal := structVal.Field(i)
|
||||
if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct {
|
||||
// Handle embedded struct pointers as embedded structs.
|
||||
fieldVal = fieldVal.Elem()
|
||||
}
|
||||
|
||||
// If "squash" is specified in the tag, we squash the field down.
|
||||
squash := d.config.Squash && fieldKind == reflect.Struct
|
||||
squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous
|
||||
remain := false
|
||||
|
||||
// We always parse the tags cause we're looking for other tags too
|
||||
|
@ -1163,22 +1278,21 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
}
|
||||
|
||||
if squash {
|
||||
if fieldKind != reflect.Struct {
|
||||
if fieldVal.Kind() != reflect.Struct {
|
||||
errors = appendErrors(errors,
|
||||
fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldKind))
|
||||
fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
|
||||
} else {
|
||||
structs = append(structs, structVal.FieldByName(fieldType.Name))
|
||||
structs = append(structs, fieldVal)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Build our field
|
||||
fieldCurrent := field{fieldType, structVal.Field(i)}
|
||||
if remain {
|
||||
remainField = &fieldCurrent
|
||||
remainField = &field{fieldType, fieldVal}
|
||||
} else {
|
||||
// Normal struct field, store it away
|
||||
fields = append(fields, field{fieldType, structVal.Field(i)})
|
||||
fields = append(fields, field{fieldType, fieldVal})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1237,7 +1351,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
// If the name is empty string, then we're at the root, and we
|
||||
// don't dot-join the fields.
|
||||
if name != "" {
|
||||
fieldName = fmt.Sprintf("%s.%s", name, fieldName)
|
||||
fieldName = name + "." + fieldName
|
||||
}
|
||||
|
||||
if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {
|
||||
|
@ -1284,7 +1398,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
for rawKey := range dataValKeysUnused {
|
||||
key := rawKey.(string)
|
||||
if name != "" {
|
||||
key = fmt.Sprintf("%s.%s", name, key)
|
||||
key = name + "." + key
|
||||
}
|
||||
|
||||
d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)
|
||||
|
@ -1294,6 +1408,24 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
|||
return nil
|
||||
}
|
||||
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch getKind(v) {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getKind(val reflect.Value) reflect.Kind {
|
||||
kind := val.Kind()
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell
|
||||
Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -93,7 +93,7 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
|
|||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
|
||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
|
||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -127,7 +127,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
|
|||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -173,7 +173,7 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool
|
|||
// Greaterf asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1))
|
||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
|
||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
@ -225,7 +225,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u
|
|||
//
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -237,7 +237,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string,
|
|||
//
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -245,6 +245,18 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
|
|||
return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// HTTPSuccessf asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
|
@ -259,7 +271,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin
|
|||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
|
||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -341,7 +353,7 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf
|
|||
// Lessf asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2))
|
||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
|
||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
@ -454,6 +466,16 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
|
|||
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
|
||||
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
|
@ -476,7 +498,7 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo
|
|||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
@ -552,7 +574,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
|
|||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
|
|
@ -169,7 +169,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
|
|||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
|
||||
// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted")
|
||||
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -251,7 +251,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg
|
|||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
|
||||
// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted")
|
||||
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -370,7 +370,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string,
|
|||
// Greaterf asserts that the first element is greater than the second
|
||||
//
|
||||
// a.Greaterf(2, 1, "error message %s", "formatted")
|
||||
// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1))
|
||||
// a.Greaterf(float64(2), float64(1), "error message %s", "formatted")
|
||||
// a.Greaterf("b", "a", "error message %s", "formatted")
|
||||
func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
@ -447,7 +447,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri
|
|||
//
|
||||
// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -471,7 +471,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s
|
|||
//
|
||||
// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -479,6 +479,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
|
|||
return HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPStatusCode asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...)
|
||||
}
|
||||
|
||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
|
||||
|
@ -515,7 +539,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{},
|
|||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
|
||||
// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -706,7 +730,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar
|
|||
// Lessf asserts that the first element is less than the second
|
||||
//
|
||||
// a.Lessf(1, 2, "error message %s", "formatted")
|
||||
// a.Lessf(float64(1, "error message %s", "formatted"), float64(2))
|
||||
// a.Lessf(float64(1), float64(2), "error message %s", "formatted")
|
||||
// a.Lessf("a", "b", "error message %s", "formatted")
|
||||
func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
@ -884,6 +908,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr
|
|||
return NotEqual(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// a.NotEqualValues(obj1, obj2)
|
||||
func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEqualValues(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted")
|
||||
func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotEqualValuesf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
|
||||
|
@ -950,7 +994,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in
|
|||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
|
||||
// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
|
||||
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
@ -1102,7 +1146,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
|
|||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
|
||||
// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
|
||||
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
|
|
@ -1,309 +0,0 @@
|
|||
package assert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
|
||||
switch kind {
|
||||
case reflect.Int:
|
||||
{
|
||||
intobj1 := obj1.(int)
|
||||
intobj2 := obj2.(int)
|
||||
if intobj1 > intobj2 {
|
||||
return -1, true
|
||||
}
|
||||
if intobj1 == intobj2 {
|
||||
return 0, true
|
||||
}
|
||||
if intobj1 < intobj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Int8:
|
||||
{
|
||||
int8obj1 := obj1.(int8)
|
||||
int8obj2 := obj2.(int8)
|
||||
if int8obj1 > int8obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if int8obj1 == int8obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if int8obj1 < int8obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Int16:
|
||||
{
|
||||
int16obj1 := obj1.(int16)
|
||||
int16obj2 := obj2.(int16)
|
||||
if int16obj1 > int16obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if int16obj1 == int16obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if int16obj1 < int16obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Int32:
|
||||
{
|
||||
int32obj1 := obj1.(int32)
|
||||
int32obj2 := obj2.(int32)
|
||||
if int32obj1 > int32obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if int32obj1 == int32obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if int32obj1 < int32obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Int64:
|
||||
{
|
||||
int64obj1 := obj1.(int64)
|
||||
int64obj2 := obj2.(int64)
|
||||
if int64obj1 > int64obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if int64obj1 == int64obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if int64obj1 < int64obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint:
|
||||
{
|
||||
uintobj1 := obj1.(uint)
|
||||
uintobj2 := obj2.(uint)
|
||||
if uintobj1 > uintobj2 {
|
||||
return -1, true
|
||||
}
|
||||
if uintobj1 == uintobj2 {
|
||||
return 0, true
|
||||
}
|
||||
if uintobj1 < uintobj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint8:
|
||||
{
|
||||
uint8obj1 := obj1.(uint8)
|
||||
uint8obj2 := obj2.(uint8)
|
||||
if uint8obj1 > uint8obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if uint8obj1 == uint8obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if uint8obj1 < uint8obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint16:
|
||||
{
|
||||
uint16obj1 := obj1.(uint16)
|
||||
uint16obj2 := obj2.(uint16)
|
||||
if uint16obj1 > uint16obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if uint16obj1 == uint16obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if uint16obj1 < uint16obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint32:
|
||||
{
|
||||
uint32obj1 := obj1.(uint32)
|
||||
uint32obj2 := obj2.(uint32)
|
||||
if uint32obj1 > uint32obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if uint32obj1 == uint32obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if uint32obj1 < uint32obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Uint64:
|
||||
{
|
||||
uint64obj1 := obj1.(uint64)
|
||||
uint64obj2 := obj2.(uint64)
|
||||
if uint64obj1 > uint64obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if uint64obj1 == uint64obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if uint64obj1 < uint64obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Float32:
|
||||
{
|
||||
float32obj1 := obj1.(float32)
|
||||
float32obj2 := obj2.(float32)
|
||||
if float32obj1 > float32obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if float32obj1 == float32obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if float32obj1 < float32obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.Float64:
|
||||
{
|
||||
float64obj1 := obj1.(float64)
|
||||
float64obj2 := obj2.(float64)
|
||||
if float64obj1 > float64obj2 {
|
||||
return -1, true
|
||||
}
|
||||
if float64obj1 == float64obj2 {
|
||||
return 0, true
|
||||
}
|
||||
if float64obj1 < float64obj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
{
|
||||
stringobj1 := obj1.(string)
|
||||
stringobj2 := obj2.(string)
|
||||
if stringobj1 > stringobj2 {
|
||||
return -1, true
|
||||
}
|
||||
if stringobj1 == stringobj2 {
|
||||
return 0, true
|
||||
}
|
||||
if stringobj1 < stringobj2 {
|
||||
return 1, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Greater asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greater(t, 2, 1)
|
||||
// assert.Greater(t, float64(2), float64(1))
|
||||
// assert.Greater(t, "b", "a")
|
||||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
e1Kind := reflect.ValueOf(e1).Kind()
|
||||
e2Kind := reflect.ValueOf(e2).Kind()
|
||||
if e1Kind != e2Kind {
|
||||
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||
}
|
||||
|
||||
res, isComparable := compare(e1, e2, e1Kind)
|
||||
if !isComparable {
|
||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||
}
|
||||
|
||||
if res != -1 {
|
||||
return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// GreaterOrEqual asserts that the first element is greater than or equal to the second
|
||||
//
|
||||
// assert.GreaterOrEqual(t, 2, 1)
|
||||
// assert.GreaterOrEqual(t, 2, 2)
|
||||
// assert.GreaterOrEqual(t, "b", "a")
|
||||
// assert.GreaterOrEqual(t, "b", "b")
|
||||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
e1Kind := reflect.ValueOf(e1).Kind()
|
||||
e2Kind := reflect.ValueOf(e2).Kind()
|
||||
if e1Kind != e2Kind {
|
||||
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||
}
|
||||
|
||||
res, isComparable := compare(e1, e2, e1Kind)
|
||||
if !isComparable {
|
||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||
}
|
||||
|
||||
if res != -1 && res != 0 {
|
||||
return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Less asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Less(t, 1, 2)
|
||||
// assert.Less(t, float64(1), float64(2))
|
||||
// assert.Less(t, "a", "b")
|
||||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
e1Kind := reflect.ValueOf(e1).Kind()
|
||||
e2Kind := reflect.ValueOf(e2).Kind()
|
||||
if e1Kind != e2Kind {
|
||||
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||
}
|
||||
|
||||
res, isComparable := compare(e1, e2, e1Kind)
|
||||
if !isComparable {
|
||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||
}
|
||||
|
||||
if res != 1 {
|
||||
return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// LessOrEqual asserts that the first element is less than or equal to the second
|
||||
//
|
||||
// assert.LessOrEqual(t, 1, 2)
|
||||
// assert.LessOrEqual(t, 2, 2)
|
||||
// assert.LessOrEqual(t, "a", "b")
|
||||
// assert.LessOrEqual(t, "b", "b")
|
||||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
e1Kind := reflect.ValueOf(e1).Kind()
|
||||
e2Kind := reflect.ValueOf(e2).Kind()
|
||||
if e1Kind != e2Kind {
|
||||
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||
}
|
||||
|
||||
res, isComparable := compare(e1, e2, e1Kind)
|
||||
if !isComparable {
|
||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||
}
|
||||
|
||||
if res != 1 && res != 0 {
|
||||
return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -19,7 +19,7 @@ import (
|
|||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
|
||||
|
@ -45,7 +45,7 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
|
|||
// for table driven tests.
|
||||
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
|
||||
|
||||
// Comparison a custom function that returns true on success and false on failure
|
||||
// Comparison is a custom function that returns true on success and false on failure
|
||||
type Comparison func() (success bool)
|
||||
|
||||
/*
|
||||
|
@ -104,11 +104,11 @@ the problem actually occurred in calling code.*/
|
|||
// failed.
|
||||
func CallerInfo() []string {
|
||||
|
||||
pc := uintptr(0)
|
||||
file := ""
|
||||
line := 0
|
||||
ok := false
|
||||
name := ""
|
||||
var pc uintptr
|
||||
var ok bool
|
||||
var file string
|
||||
var line int
|
||||
var name string
|
||||
|
||||
callers := []string{}
|
||||
for i := 0; ; i++ {
|
||||
|
@ -429,14 +429,27 @@ func samePointers(first, second interface{}) bool {
|
|||
// to a type conversion in the Go grammar.
|
||||
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
||||
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
|
||||
return fmt.Sprintf("%T(%#v)", expected, expected),
|
||||
fmt.Sprintf("%T(%#v)", actual, actual)
|
||||
return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)),
|
||||
fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual))
|
||||
}
|
||||
switch expected.(type) {
|
||||
case time.Duration:
|
||||
return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual)
|
||||
}
|
||||
return fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual)
|
||||
return truncatingFormat(expected), truncatingFormat(actual)
|
||||
}
|
||||
|
||||
// truncatingFormat formats the data and truncates it if it's too long.
|
||||
//
|
||||
// This helps keep formatted error messages lines from exceeding the
|
||||
// bufio.MaxScanTokenSize max line length that the go testing framework imposes.
|
||||
func truncatingFormat(data interface{}) string {
|
||||
value := fmt.Sprintf("%#v", data)
|
||||
max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed.
|
||||
if len(value) > max {
|
||||
value = value[0:max] + "<... truncated>"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
|
@ -483,12 +496,12 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
|
|||
//
|
||||
// assert.NotNil(t, err)
|
||||
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if !isNil(object) {
|
||||
return true
|
||||
}
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Fail(t, "Expected value not to be nil.", msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -529,12 +542,12 @@ func isNil(object interface{}) bool {
|
|||
//
|
||||
// assert.Nil(t, err)
|
||||
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if isNil(object) {
|
||||
return true
|
||||
}
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -571,12 +584,11 @@ func isEmpty(object interface{}) bool {
|
|||
//
|
||||
// assert.Empty(t, obj)
|
||||
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
pass := isEmpty(object)
|
||||
if !pass {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -591,12 +603,11 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
|||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
pass := !isEmpty(object)
|
||||
if !pass {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -639,16 +650,10 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
|
|||
//
|
||||
// assert.True(t, myBool)
|
||||
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if h, ok := t.(interface {
|
||||
Helper()
|
||||
}); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if value != true {
|
||||
if !value {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Fail(t, "Should be true", msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -660,11 +665,10 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
|||
//
|
||||
// assert.False(t, myBool)
|
||||
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if value != false {
|
||||
if value {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Fail(t, "Should be false", msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -695,6 +699,21 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
|
|||
|
||||
}
|
||||
|
||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValues(t, obj1, obj2)
|
||||
func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if ObjectsAreEqualValues(expected, actual) {
|
||||
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// containsElement try loop over the list check if the list includes the element.
|
||||
// return (false, false) if impossible.
|
||||
// return (true, false) if element was not found.
|
||||
|
@ -747,10 +766,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
|
|||
|
||||
ok, found := includeElement(s, contains)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
|
||||
}
|
||||
if !found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -881,27 +900,39 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface
|
|||
return true
|
||||
}
|
||||
|
||||
aKind := reflect.TypeOf(listA).Kind()
|
||||
bKind := reflect.TypeOf(listB).Kind()
|
||||
|
||||
if aKind != reflect.Array && aKind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
|
||||
if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) {
|
||||
return false
|
||||
}
|
||||
|
||||
if bKind != reflect.Array && bKind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
|
||||
extraA, extraB := diffLists(listA, listB)
|
||||
|
||||
if len(extraA) == 0 && len(extraB) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...)
|
||||
}
|
||||
|
||||
// isList checks that the provided value is array or slice.
|
||||
func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
kind := reflect.TypeOf(list).Kind()
|
||||
if kind != reflect.Array && kind != reflect.Slice {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind),
|
||||
msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B.
|
||||
// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and
|
||||
// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored.
|
||||
func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) {
|
||||
aValue := reflect.ValueOf(listA)
|
||||
bValue := reflect.ValueOf(listB)
|
||||
|
||||
aLen := aValue.Len()
|
||||
bLen := bValue.Len()
|
||||
|
||||
if aLen != bLen {
|
||||
return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
|
||||
}
|
||||
|
||||
// Mark indexes in bValue that we already used
|
||||
visited := make([]bool, bLen)
|
||||
for i := 0; i < aLen; i++ {
|
||||
|
@ -918,11 +949,38 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface
|
|||
}
|
||||
}
|
||||
if !found {
|
||||
return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
|
||||
extraA = append(extraA, element)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
for j := 0; j < bLen; j++ {
|
||||
if visited[j] {
|
||||
continue
|
||||
}
|
||||
extraB = append(extraB, bValue.Index(j).Interface())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string {
|
||||
var msg bytes.Buffer
|
||||
|
||||
msg.WriteString("elements differ")
|
||||
if len(extraA) > 0 {
|
||||
msg.WriteString("\n\nextra elements in list A:\n")
|
||||
msg.WriteString(spewConfig.Sdump(extraA))
|
||||
}
|
||||
if len(extraB) > 0 {
|
||||
msg.WriteString("\n\nextra elements in list B:\n")
|
||||
msg.WriteString(spewConfig.Sdump(extraB))
|
||||
}
|
||||
msg.WriteString("\n\nlistA:\n")
|
||||
msg.WriteString(spewConfig.Sdump(listA))
|
||||
msg.WriteString("\n\nlistB:\n")
|
||||
msg.WriteString(spewConfig.Sdump(listB))
|
||||
|
||||
return msg.String()
|
||||
}
|
||||
|
||||
// Condition uses a Comparison to assert a complex condition.
|
||||
|
@ -1058,6 +1116,8 @@ func toFloat(x interface{}) (float64, bool) {
|
|||
xok := true
|
||||
|
||||
switch xn := x.(type) {
|
||||
case uint:
|
||||
xf = float64(xn)
|
||||
case uint8:
|
||||
xf = float64(xn)
|
||||
case uint16:
|
||||
|
@ -1079,7 +1139,7 @@ func toFloat(x interface{}) (float64, bool) {
|
|||
case float32:
|
||||
xf = float64(xn)
|
||||
case float64:
|
||||
xf = float64(xn)
|
||||
xf = xn
|
||||
case time.Duration:
|
||||
xf = float64(xn)
|
||||
default:
|
||||
|
@ -1193,6 +1253,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
|
|||
if !aok {
|
||||
return 0, fmt.Errorf("expected value %q cannot be converted to float", expected)
|
||||
}
|
||||
if math.IsNaN(af) {
|
||||
return 0, errors.New("expected value must not be NaN")
|
||||
}
|
||||
if af == 0 {
|
||||
return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
|
||||
}
|
||||
|
@ -1200,6 +1263,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
|
|||
if !bok {
|
||||
return 0, fmt.Errorf("actual value %q cannot be converted to float", actual)
|
||||
}
|
||||
if math.IsNaN(bf) {
|
||||
return 0, errors.New("actual value must not be NaN")
|
||||
}
|
||||
|
||||
return math.Abs(af-bf) / math.Abs(af), nil
|
||||
}
|
||||
|
@ -1209,6 +1275,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd
|
|||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if math.IsNaN(epsilon) {
|
||||
return Fail(t, "epsilon must not be NaN")
|
||||
}
|
||||
actualEpsilon, err := calcRelativeError(expected, actual)
|
||||
if err != nil {
|
||||
return Fail(t, err.Error(), msgAndArgs...)
|
||||
|
@ -1256,10 +1325,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
|
|||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if err != nil {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -1273,11 +1342,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
|||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return Fail(t, "An error is expected but got nil.", msgAndArgs...)
|
||||
}
|
||||
|
||||
|
@ -1553,6 +1621,7 @@ var spewConfig = spew.ConfigState{
|
|||
DisablePointerAddresses: true,
|
||||
DisableCapacities: true,
|
||||
SortKeys: true,
|
||||
DisableMethods: true,
|
||||
}
|
||||
|
||||
type tHelper interface {
|
||||
|
|
|
@ -33,7 +33,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
|
|||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
return false
|
||||
}
|
||||
|
||||
isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent
|
||||
|
@ -56,7 +55,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
|
|||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
return false
|
||||
}
|
||||
|
||||
isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
|
||||
|
@ -79,7 +77,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
|
|||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
return false
|
||||
}
|
||||
|
||||
isErrorCode := code >= http.StatusBadRequest
|
||||
|
@ -90,6 +87,28 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
|
|||
return isErrorCode
|
||||
}
|
||||
|
||||
// HTTPStatusCode asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
}
|
||||
|
||||
successful := code == statuscode
|
||||
if !successful {
|
||||
Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code))
|
||||
}
|
||||
|
||||
return successful
|
||||
}
|
||||
|
||||
// HTTPBody is a helper that returns HTTP body of the response. It returns
|
||||
// empty string if building a new request fails.
|
||||
func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
|
||||
|
|
|
@ -212,7 +212,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg
|
|||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
|
||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
|
||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -315,7 +315,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ..
|
|||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -470,7 +470,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg
|
|||
// Greaterf asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1))
|
||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
|
||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
@ -565,7 +565,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string,
|
|||
//
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -595,7 +595,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin
|
|||
//
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -606,6 +606,36 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
|
|||
t.FailNow()
|
||||
}
|
||||
|
||||
// HTTPStatusCode asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
||||
|
@ -651,7 +681,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg
|
|||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
|
||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -902,7 +932,7 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
|
|||
// Lessf asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2))
|
||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
|
||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
@ -1128,6 +1158,32 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs .
|
|||
t.FailNow()
|
||||
}
|
||||
|
||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValues(t, obj1, obj2)
|
||||
func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.NotEqualValues(t, expected, actual, msgAndArgs...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
|
||||
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if assert.NotEqualValuesf(t, expected, actual, msg, args...) {
|
||||
return
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
|
@ -1212,7 +1268,7 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
|
|||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
@ -1406,7 +1462,7 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface
|
|||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
|
|
|
@ -170,7 +170,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
|
|||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// and equal.
|
||||
//
|
||||
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
|
||||
// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted")
|
||||
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -252,7 +252,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg
|
|||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
|
||||
// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted")
|
||||
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -371,7 +371,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string,
|
|||
// Greaterf asserts that the first element is greater than the second
|
||||
//
|
||||
// a.Greaterf(2, 1, "error message %s", "formatted")
|
||||
// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1))
|
||||
// a.Greaterf(float64(2), float64(1), "error message %s", "formatted")
|
||||
// a.Greaterf("b", "a", "error message %s", "formatted")
|
||||
func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
@ -448,7 +448,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri
|
|||
//
|
||||
// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -472,7 +472,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s
|
|||
//
|
||||
// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -480,6 +480,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
|
|||
HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPStatusCode asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...)
|
||||
}
|
||||
|
||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...)
|
||||
}
|
||||
|
||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
|
||||
|
@ -516,7 +540,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{},
|
|||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
|
||||
// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
@ -707,7 +731,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar
|
|||
// Lessf asserts that the first element is less than the second
|
||||
//
|
||||
// a.Lessf(1, 2, "error message %s", "formatted")
|
||||
// a.Lessf(float64(1, "error message %s", "formatted"), float64(2))
|
||||
// a.Lessf(float64(1), float64(2), "error message %s", "formatted")
|
||||
// a.Lessf("a", "b", "error message %s", "formatted")
|
||||
func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
@ -885,6 +909,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr
|
|||
NotEqual(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// a.NotEqualValues(obj1, obj2)
|
||||
func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
NotEqualValues(a.t, expected, actual, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted")
|
||||
func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
NotEqualValuesf(a.t, expected, actual, msg, args...)
|
||||
}
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
|
||||
|
@ -951,7 +995,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in
|
|||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
|
||||
// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
|
||||
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
@ -1103,7 +1147,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
|
|||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
|
||||
// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
|
||||
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) {
|
||||
if h, ok := a.t.(tHelper); ok {
|
||||
|
|
|
@ -13,6 +13,14 @@ import (
|
|||
// if we're converting from a set into a list of the same element type.)
|
||||
func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
if !val.Length().IsKnown() {
|
||||
// If the input collection has an unknown length (which is true
|
||||
// for a set containing unknown values) then our result must be
|
||||
// an unknown list, because we can't predict how many elements
|
||||
// the resulting list should have.
|
||||
return cty.UnknownVal(cty.List(val.Type().ElementType())), nil
|
||||
}
|
||||
|
||||
elems := make([]cty.Value, 0, val.LengthInt())
|
||||
i := int64(0)
|
||||
elemPath := append(path.Copy(), nil)
|
||||
|
@ -156,34 +164,45 @@ func conversionCollectionToMap(ety cty.Type, conv conversion) conversion {
|
|||
// given tuple type and return a set of the given element type.
|
||||
//
|
||||
// Will panic if the given tupleType isn't actually a tuple type.
|
||||
func conversionTupleToSet(tupleType cty.Type, listEty cty.Type, unsafe bool) conversion {
|
||||
func conversionTupleToSet(tupleType cty.Type, setEty cty.Type, unsafe bool) conversion {
|
||||
tupleEtys := tupleType.TupleElementTypes()
|
||||
|
||||
if len(tupleEtys) == 0 {
|
||||
// Empty tuple short-circuit
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
return cty.SetValEmpty(listEty), nil
|
||||
return cty.SetValEmpty(setEty), nil
|
||||
}
|
||||
}
|
||||
|
||||
if listEty == cty.DynamicPseudoType {
|
||||
if setEty == cty.DynamicPseudoType {
|
||||
// This is a special case where the caller wants us to find
|
||||
// a suitable single type that all elements can convert to, if
|
||||
// possible.
|
||||
listEty, _ = unify(tupleEtys, unsafe)
|
||||
if listEty == cty.NilType {
|
||||
setEty, _ = unify(tupleEtys, unsafe)
|
||||
if setEty == cty.NilType {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the set element type after unification is still the dynamic
|
||||
// type, the only way this can result in a valid set is if all values
|
||||
// are of dynamic type
|
||||
if setEty == cty.DynamicPseudoType {
|
||||
for _, tupleEty := range tupleEtys {
|
||||
if !tupleEty.Equals(cty.DynamicPseudoType) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elemConvs := make([]conversion, len(tupleEtys))
|
||||
for i, tupleEty := range tupleEtys {
|
||||
if tupleEty.Equals(listEty) {
|
||||
if tupleEty.Equals(setEty) {
|
||||
// no conversion required
|
||||
continue
|
||||
}
|
||||
|
||||
elemConvs[i] = getConversion(tupleEty, listEty, unsafe)
|
||||
elemConvs[i] = getConversion(tupleEty, setEty, unsafe)
|
||||
if elemConvs[i] == nil {
|
||||
// If any of our element conversions are impossible, then the our
|
||||
// whole conversion is impossible.
|
||||
|
@ -244,6 +263,17 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co
|
|||
if listEty == cty.NilType {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the list element type after unification is still the dynamic
|
||||
// type, the only way this can result in a valid list is if all values
|
||||
// are of dynamic type
|
||||
if listEty == cty.DynamicPseudoType {
|
||||
for _, tupleEty := range tupleEtys {
|
||||
if !tupleEty.Equals(cty.DynamicPseudoType) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elemConvs := make([]conversion, len(tupleEtys))
|
||||
|
@ -265,6 +295,7 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co
|
|||
// element conversions in elemConvs
|
||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||
elems := make([]cty.Value, 0, len(elemConvs))
|
||||
elemTys := make([]cty.Type, 0, len(elems))
|
||||
elemPath := append(path.Copy(), nil)
|
||||
i := int64(0)
|
||||
it := val.ElementIterator()
|
||||
|
@ -284,10 +315,15 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co
|
|||
}
|
||||
}
|
||||
elems = append(elems, val)
|
||||
elemTys = append(elemTys, val.Type())
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
elems, err := conversionUnifyListElements(elems, elemPath, unsafe)
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
}
|
||||
return cty.ListVal(elems), nil
|
||||
}
|
||||
}
|
||||
|
@ -430,6 +466,16 @@ func conversionMapToObject(mapType cty.Type, objType cty.Type, unsafe bool) conv
|
|||
elems[name.AsString()] = val
|
||||
}
|
||||
|
||||
for name, aty := range objectAtys {
|
||||
if _, exists := elems[name]; !exists {
|
||||
if optional := objType.AttributeOptional(name); optional {
|
||||
elems[name] = cty.NullVal(aty)
|
||||
} else {
|
||||
return cty.NilVal, path.NewErrorf("map has no element for required attribute %q", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cty.ObjectVal(elems), nil
|
||||
}
|
||||
}
|
||||
|
@ -441,6 +487,7 @@ func conversionUnifyCollectionElements(elems map[string]cty.Value, path cty.Path
|
|||
}
|
||||
unifiedType, _ := unify(elemTypes, unsafe)
|
||||
if unifiedType == cty.NilType {
|
||||
return nil, path.NewErrorf("collection elements cannot be unified")
|
||||
}
|
||||
|
||||
unifiedElems := make(map[string]cty.Value)
|
||||
|
@ -486,3 +533,37 @@ func conversionCheckMapElementTypes(elems map[string]cty.Value, path cty.Path) e
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func conversionUnifyListElements(elems []cty.Value, path cty.Path, unsafe bool) ([]cty.Value, error) {
|
||||
elemTypes := make([]cty.Type, len(elems))
|
||||
for i, elem := range elems {
|
||||
elemTypes[i] = elem.Type()
|
||||
}
|
||||
unifiedType, _ := unify(elemTypes, unsafe)
|
||||
if unifiedType == cty.NilType {
|
||||
return nil, path.NewErrorf("collection elements cannot be unified")
|
||||
}
|
||||
|
||||
ret := make([]cty.Value, len(elems))
|
||||
elemPath := append(path.Copy(), nil)
|
||||
|
||||
for i, elem := range elems {
|
||||
if elem.Type().Equals(unifiedType) {
|
||||
ret[i] = elem
|
||||
continue
|
||||
}
|
||||
conv := getConversion(elem.Type(), unifiedType, unsafe)
|
||||
if conv == nil {
|
||||
}
|
||||
elemPath[len(elemPath)-1] = cty.IndexStep{
|
||||
Key: cty.NumberIntVal(int64(i)),
|
||||
}
|
||||
val, err := conv(elem, elemPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = val
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
@ -11,17 +11,29 @@ import (
|
|||
// type, meaning that each attribute of the output type has a corresponding
|
||||
// attribute in the input type where a recursive conversion is available.
|
||||
//
|
||||
// If the "out" type has any optional attributes, those attributes may be
|
||||
// absent in the "in" type, in which case null values will be used in their
|
||||
// place in the result.
|
||||
//
|
||||
// Shallow object conversions work the same for both safe and unsafe modes,
|
||||
// but the safety flag is passed on to recursive conversions and may thus
|
||||
// limit the above definition of "subset".
|
||||
func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
|
||||
inAtys := in.AttributeTypes()
|
||||
outAtys := out.AttributeTypes()
|
||||
outOptionals := out.OptionalAttributes()
|
||||
attrConvs := make(map[string]conversion)
|
||||
|
||||
for name, outAty := range outAtys {
|
||||
inAty, exists := inAtys[name]
|
||||
if !exists {
|
||||
if _, optional := outOptionals[name]; optional {
|
||||
// If it's optional then we'll skip inserting an
|
||||
// attribute conversion and then deal with inserting
|
||||
// the default value in our overall conversion logic
|
||||
// later.
|
||||
continue
|
||||
}
|
||||
// No conversion is available, then.
|
||||
return nil
|
||||
}
|
||||
|
@ -71,6 +83,13 @@ func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
|
|||
attrVals[name] = val
|
||||
}
|
||||
|
||||
for name := range outOptionals {
|
||||
if _, exists := attrVals[name]; !exists {
|
||||
wantTy := outAtys[name]
|
||||
attrVals[name] = cty.NullVal(wantTy)
|
||||
}
|
||||
}
|
||||
|
||||
return cty.ObjectVal(attrVals), nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,9 @@ func mismatchMessageObjects(got, want cty.Type) string {
|
|||
for name, wantAty := range wantAtys {
|
||||
gotAty, exists := gotAtys[name]
|
||||
if !exists {
|
||||
missingAttrs = append(missingAttrs, name)
|
||||
if !want.AttributeOptional(name) {
|
||||
missingAttrs = append(missingAttrs, name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -244,19 +244,21 @@ func (f Function) Call(args []cty.Value) (val cty.Value, err error) {
|
|||
return cty.UnknownVal(expectedType), nil
|
||||
}
|
||||
|
||||
if val.IsMarked() && !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.Unmark()
|
||||
// In order to avoid additional overhead on applications that
|
||||
// are not using marked values, we copy the given args only
|
||||
// if we encounter a marked value we need to unmark. However,
|
||||
// as a consequence we end up doing redundant copying if multiple
|
||||
// marked values need to be unwrapped. That seems okay because
|
||||
// argument lists are generally small.
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
if !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.UnmarkDeep()
|
||||
if len(marks) > 0 {
|
||||
// In order to avoid additional overhead on applications that
|
||||
// are not using marked values, we copy the given args only
|
||||
// if we encounter a marked value we need to unmark. However,
|
||||
// as a consequence we end up doing redundant copying if multiple
|
||||
// marked values need to be unwrapped. That seems okay because
|
||||
// argument lists are generally small.
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,13 +268,15 @@ func (f Function) Call(args []cty.Value) (val cty.Value, err error) {
|
|||
if !val.IsKnown() && !spec.AllowUnknown {
|
||||
return cty.UnknownVal(expectedType), nil
|
||||
}
|
||||
if val.IsMarked() && !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.Unmark()
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[len(posArgs)+i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
if !spec.AllowMarked {
|
||||
unwrappedVal, marks := val.UnmarkDeep()
|
||||
if len(marks) > 0 {
|
||||
newArgs := make([]cty.Value, len(args))
|
||||
copy(newArgs, args)
|
||||
newArgs[len(posArgs)+i] = unwrappedVal
|
||||
resultMarks = append(resultMarks, marks)
|
||||
args = newArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,6 +138,13 @@ var ElementFunc = function.New(&function.Spec{
|
|||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
list := args[0]
|
||||
index := args[1]
|
||||
if index.IsKnown() {
|
||||
if index.LessThan(cty.NumberIntVal(0)).True() {
|
||||
return cty.DynamicPseudoType, fmt.Errorf("cannot use element function with a negative index")
|
||||
}
|
||||
}
|
||||
|
||||
listTy := list.Type()
|
||||
switch {
|
||||
case listTy.IsListType():
|
||||
|
@ -173,6 +180,10 @@ var ElementFunc = function.New(&function.Spec{
|
|||
return cty.DynamicVal, fmt.Errorf("invalid index: %s", err)
|
||||
}
|
||||
|
||||
if args[1].LessThan(cty.NumberIntVal(0)).True() {
|
||||
return cty.DynamicVal, fmt.Errorf("cannot use element function with a negative index")
|
||||
}
|
||||
|
||||
if !args[0].IsKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
@ -492,6 +503,11 @@ var FlattenFunc = function.New(&function.Spec{
|
|||
// We can flatten lists with unknown values, as long as they are not
|
||||
// lists themselves.
|
||||
func flattener(flattenList cty.Value) ([]cty.Value, bool) {
|
||||
if !flattenList.Length().IsKnown() {
|
||||
// If we don't know the length of what we're flattening then we can't
|
||||
// predict the length of our result yet either.
|
||||
return nil, false
|
||||
}
|
||||
out := make([]cty.Value, 0)
|
||||
for it := flattenList.ElementIterator(); it.Next(); {
|
||||
_, val := it.Element()
|
||||
|
@ -762,6 +778,9 @@ var MergeFunc = function.New(&function.Spec{
|
|||
case allNull:
|
||||
return cty.NullVal(retType), nil
|
||||
case retType.IsMapType():
|
||||
if len(outputMap) == 0 {
|
||||
return cty.MapValEmpty(retType.ElementType()), nil
|
||||
}
|
||||
return cty.MapVal(outputMap), nil
|
||||
case retType.IsObjectType(), retType.Equals(cty.DynamicPseudoType):
|
||||
return cty.ObjectVal(outputMap), nil
|
||||
|
@ -869,6 +888,10 @@ var SetProductFunc = function.New(&function.Spec{
|
|||
|
||||
total := 1
|
||||
for _, arg := range args {
|
||||
if !arg.Length().IsKnown() {
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
// Because of our type checking function, we are guaranteed that
|
||||
// all of the arguments are known, non-null values of types that
|
||||
// support LengthInt.
|
||||
|
@ -1016,7 +1039,8 @@ func sliceIndexes(args []cty.Value) (int, int, bool, error) {
|
|||
var startIndex, endIndex, length int
|
||||
var startKnown, endKnown, lengthKnown bool
|
||||
|
||||
if args[0].Type().IsTupleType() || args[0].IsKnown() { // if it's a tuple then we always know the length by the type, but lists must be known
|
||||
// If it's a tuple then we always know the length by the type, but collections might be unknown or have unknown length
|
||||
if args[0].Type().IsTupleType() || args[0].Length().IsKnown() {
|
||||
length = args[0].LengthInt()
|
||||
lengthKnown = true
|
||||
}
|
||||
|
|
|
@ -362,9 +362,14 @@ func splitDateFormat(data []byte, atEOF bool) (advance int, token []byte, err er
|
|||
for i := 1; i < len(data); i++ {
|
||||
if data[i] == esc {
|
||||
if (i + 1) == len(data) {
|
||||
// We need at least one more byte to decide if this is an
|
||||
// escape or a terminator.
|
||||
return 0, nil, nil
|
||||
if atEOF {
|
||||
// We have a closing quote and are at the end of our input
|
||||
return len(data), data, nil
|
||||
} else {
|
||||
// We need at least one more byte to decide if this is an
|
||||
// escape or a terminator.
|
||||
return 0, nil, nil
|
||||
}
|
||||
}
|
||||
if data[i+1] == esc {
|
||||
i++ // doubled-up quotes are an escape sequence
|
||||
|
|
|
@ -85,7 +85,7 @@ var FormatListFunc = function.New(&function.Spec{
|
|||
argTy := arg.Type()
|
||||
switch {
|
||||
case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull():
|
||||
if !argTy.IsTupleType() && !arg.IsKnown() {
|
||||
if !argTy.IsTupleType() && !(arg.IsKnown() && arg.Length().IsKnown()) {
|
||||
// We can't iterate this one at all yet then, so we can't
|
||||
// yet produce a result.
|
||||
unknowns[i] = true
|
||||
|
|
|
@ -12,6 +12,7 @@ var JSONEncodeFunc = function.New(&function.Spec{
|
|||
Name: "val",
|
||||
Type: cty.DynamicPseudoType,
|
||||
AllowDynamicType: true,
|
||||
AllowNull: true,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
|
@ -24,6 +25,10 @@ var JSONEncodeFunc = function.New(&function.Spec{
|
|||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
if val.IsNull() {
|
||||
return cty.StringVal("null"), nil
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(val, val.Type())
|
||||
if err != nil {
|
||||
return cty.NilVal, err
|
||||
|
|
|
@ -44,7 +44,7 @@ var SetUnionFunc = function.New(&function.Spec{
|
|||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Union(s2)
|
||||
}),
|
||||
}, true),
|
||||
})
|
||||
|
||||
var SetIntersectionFunc = function.New(&function.Spec{
|
||||
|
@ -63,7 +63,7 @@ var SetIntersectionFunc = function.New(&function.Spec{
|
|||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Intersection(s2)
|
||||
}),
|
||||
}, false),
|
||||
})
|
||||
|
||||
var SetSubtractFunc = function.New(&function.Spec{
|
||||
|
@ -82,7 +82,7 @@ var SetSubtractFunc = function.New(&function.Spec{
|
|||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Subtract(s2)
|
||||
}),
|
||||
}, false),
|
||||
})
|
||||
|
||||
var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
||||
|
@ -100,8 +100,8 @@ var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
|||
},
|
||||
Type: setOperationReturnType,
|
||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||
return s1.Subtract(s2)
|
||||
}),
|
||||
return s1.SymmetricDifference(s2)
|
||||
}, false),
|
||||
})
|
||||
|
||||
// SetHasElement determines whether the given set contains the given value as an
|
||||
|
@ -163,8 +163,23 @@ func SetSymmetricDifference(sets ...cty.Value) (cty.Value, error) {
|
|||
func setOperationReturnType(args []cty.Value) (ret cty.Type, err error) {
|
||||
var etys []cty.Type
|
||||
for _, arg := range args {
|
||||
etys = append(etys, arg.Type().ElementType())
|
||||
ty := arg.Type().ElementType()
|
||||
|
||||
// Do not unify types for empty dynamic pseudo typed collections. These
|
||||
// will always convert to any other concrete type.
|
||||
if arg.IsKnown() && arg.LengthInt() == 0 && ty.Equals(cty.DynamicPseudoType) {
|
||||
continue
|
||||
}
|
||||
|
||||
etys = append(etys, ty)
|
||||
}
|
||||
|
||||
// If all element types were skipped (due to being empty dynamic collections),
|
||||
// the return type should also be a set of dynamic pseudo type.
|
||||
if len(etys) == 0 {
|
||||
return cty.Set(cty.DynamicPseudoType), nil
|
||||
}
|
||||
|
||||
newEty, _ := convert.UnifyUnsafe(etys)
|
||||
if newEty == cty.NilType {
|
||||
return cty.NilType, fmt.Errorf("given sets must all have compatible element types")
|
||||
|
@ -172,13 +187,21 @@ func setOperationReturnType(args []cty.Value) (ret cty.Type, err error) {
|
|||
return cty.Set(newEty), nil
|
||||
}
|
||||
|
||||
func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet) function.ImplFunc {
|
||||
func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet, allowUnknowns bool) function.ImplFunc {
|
||||
return func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
first := args[0]
|
||||
first, err = convert.Convert(first, retType)
|
||||
if err != nil {
|
||||
return cty.NilVal, function.NewArgError(0, err)
|
||||
}
|
||||
if !allowUnknowns && !first.IsWhollyKnown() {
|
||||
// This set function can produce a correct result only when all
|
||||
// elements are known, because eventually knowing the unknown
|
||||
// values may cause the result to have fewer known elements, or
|
||||
// might cause a result with no unknown elements at all to become
|
||||
// one with a different length.
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
set := first.AsValueSet()
|
||||
for i, arg := range args[1:] {
|
||||
|
@ -186,6 +209,10 @@ func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet) function.ImplFun
|
|||
if err != nil {
|
||||
return cty.NilVal, function.NewArgError(i+1, err)
|
||||
}
|
||||
if !allowUnknowns && !arg.IsWhollyKnown() {
|
||||
// (For the same reason as we did this check for "first" above.)
|
||||
return cty.UnknownVal(retType), nil
|
||||
}
|
||||
|
||||
argSet := arg.AsValueSet()
|
||||
set = f(set, argSet)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// MarshalJSON is an implementation of json.Marshaler that allows Type
|
||||
|
@ -52,6 +53,19 @@ func (t Type) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
buf.WriteString(`["object",`)
|
||||
buf.Write(atysJSON)
|
||||
if optionals := t.OptionalAttributes(); len(optionals) > 0 {
|
||||
buf.WriteByte(',')
|
||||
optionalNames := make([]string, 0, len(optionals))
|
||||
for k := range optionals {
|
||||
optionalNames = append(optionalNames, k)
|
||||
}
|
||||
sort.Strings(optionalNames)
|
||||
optionalsJSON, err := json.Marshal(optionalNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(optionalsJSON)
|
||||
}
|
||||
buf.WriteRune(']')
|
||||
return buf.Bytes(), nil
|
||||
case typeTuple:
|
||||
|
@ -148,7 +162,16 @@ func (t *Type) UnmarshalJSON(buf []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = Object(atys)
|
||||
if dec.More() {
|
||||
var optionals []string
|
||||
err = dec.Decode(&optionals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = ObjectWithOptionalAttrs(atys, optionals)
|
||||
} else {
|
||||
*t = Object(atys)
|
||||
}
|
||||
case "tuple":
|
||||
var etys []Type
|
||||
err = dec.Decode(&etys)
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
func marshal(val cty.Value, t cty.Type, path cty.Path, b *bytes.Buffer) error {
|
||||
if val.IsMarked() {
|
||||
return path.NewErrorf("value has marks, so it cannot be seralized")
|
||||
return path.NewErrorf("value has marks, so it cannot be serialized as JSON")
|
||||
}
|
||||
|
||||
// If we're going to decode as DynamicPseudoType then we need to save
|
||||
|
|
|
@ -67,6 +67,23 @@ func (m ValueMarks) GoString() string {
|
|||
return s.String()
|
||||
}
|
||||
|
||||
// PathValueMarks is a structure that enables tracking marks
|
||||
// and the paths where they are located in one type
|
||||
type PathValueMarks struct {
|
||||
Path Path
|
||||
Marks ValueMarks
|
||||
}
|
||||
|
||||
func (p PathValueMarks) Equal(o PathValueMarks) bool {
|
||||
if !p.Path.Equals(o.Path) {
|
||||
return false
|
||||
}
|
||||
if !p.Marks.Equal(o.Marks) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsMarked returns true if and only if the receiving value carries at least
|
||||
// one mark. A marked value cannot be used directly with integration methods
|
||||
// without explicitly unmarking it (and retrieving the markings) first.
|
||||
|
@ -174,6 +191,31 @@ func (val Value) Mark(mark interface{}) Value {
|
|||
}
|
||||
}
|
||||
|
||||
type applyPathValueMarksTransformer struct {
|
||||
pvm []PathValueMarks
|
||||
}
|
||||
|
||||
func (t *applyPathValueMarksTransformer) Enter(p Path, v Value) (Value, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (t *applyPathValueMarksTransformer) Exit(p Path, v Value) (Value, error) {
|
||||
for _, path := range t.pvm {
|
||||
if p.Equals(path.Path) {
|
||||
return v.WithMarks(path.Marks), nil
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// MarkWithPaths accepts a slice of PathValueMarks to apply
|
||||
// markers to particular paths and returns the marked
|
||||
// Value.
|
||||
func (val Value) MarkWithPaths(pvm []PathValueMarks) Value {
|
||||
ret, _ := TransformWithTransformer(val, &applyPathValueMarksTransformer{pvm})
|
||||
return ret
|
||||
}
|
||||
|
||||
// Unmark separates the marks of the receiving value from the value itself,
|
||||
// removing a new unmarked value and a map (representing a set) of the marks.
|
||||
//
|
||||
|
@ -191,6 +233,24 @@ func (val Value) Unmark() (Value, ValueMarks) {
|
|||
}, marks
|
||||
}
|
||||
|
||||
type unmarkTransformer struct {
|
||||
pvm []PathValueMarks
|
||||
}
|
||||
|
||||
func (t *unmarkTransformer) Enter(p Path, v Value) (Value, error) {
|
||||
unmarkedVal, marks := v.Unmark()
|
||||
if len(marks) > 0 {
|
||||
path := make(Path, len(p), len(p)+1)
|
||||
copy(path, p)
|
||||
t.pvm = append(t.pvm, PathValueMarks{path, marks})
|
||||
}
|
||||
return unmarkedVal, nil
|
||||
}
|
||||
|
||||
func (t *unmarkTransformer) Exit(p Path, v Value) (Value, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// UnmarkDeep is similar to Unmark, but it works with an entire nested structure
|
||||
// rather than just the given value directly.
|
||||
//
|
||||
|
@ -198,17 +258,29 @@ func (val Value) Unmark() (Value, ValueMarks) {
|
|||
// the returned marks set includes the superset of all of the marks encountered
|
||||
// during the operation.
|
||||
func (val Value) UnmarkDeep() (Value, ValueMarks) {
|
||||
t := unmarkTransformer{}
|
||||
ret, _ := TransformWithTransformer(val, &t)
|
||||
|
||||
marks := make(ValueMarks)
|
||||
ret, _ := Transform(val, func(_ Path, v Value) (Value, error) {
|
||||
unmarkedV, valueMarks := v.Unmark()
|
||||
for m, s := range valueMarks {
|
||||
for _, pvm := range t.pvm {
|
||||
for m, s := range pvm.Marks {
|
||||
marks[m] = s
|
||||
}
|
||||
return unmarkedV, nil
|
||||
})
|
||||
}
|
||||
|
||||
return ret, marks
|
||||
}
|
||||
|
||||
// UnmarkDeepWithPaths is like UnmarkDeep, except it returns a slice
|
||||
// of PathValueMarks rather than a superset of all marks. This allows
|
||||
// a caller to know which marks are associated with which paths
|
||||
// in the Value.
|
||||
func (val Value) UnmarkDeepWithPaths() (Value, []PathValueMarks) {
|
||||
t := unmarkTransformer{}
|
||||
ret, _ := TransformWithTransformer(val, &t)
|
||||
return ret, t.pvm
|
||||
}
|
||||
|
||||
func (val Value) unmarkForce() Value {
|
||||
unw, _ := val.Unmark()
|
||||
return unw
|
||||
|
|
|
@ -2,11 +2,13 @@ package cty
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type typeObject struct {
|
||||
typeImplSigil
|
||||
AttrTypes map[string]Type
|
||||
AttrTypes map[string]Type
|
||||
AttrOptional map[string]struct{}
|
||||
}
|
||||
|
||||
// Object creates an object type with the given attribute types.
|
||||
|
@ -14,14 +16,52 @@ type typeObject struct {
|
|||
// After a map is passed to this function the caller must no longer access it,
|
||||
// since ownership is transferred to this library.
|
||||
func Object(attrTypes map[string]Type) Type {
|
||||
return ObjectWithOptionalAttrs(attrTypes, nil)
|
||||
}
|
||||
|
||||
// ObjectWithOptionalAttrs creates an object type where some of its attributes
|
||||
// are optional.
|
||||
//
|
||||
// This function is EXPERIMENTAL. The behavior of the function or of any other
|
||||
// functions working either directly or indirectly with a type created by
|
||||
// this function is not currently considered as a compatibility constraint, and
|
||||
// is subject to change even in minor-version releases of this module. Other
|
||||
// modules that work with cty types and values may or may not support object
|
||||
// types with optional attributes; if they do not, their behavior when
|
||||
// receiving one may be non-ideal.
|
||||
//
|
||||
// Optional attributes are significant only when an object type is being used
|
||||
// as a target type for conversion in the "convert" package. A value of an
|
||||
// object type always has a value for each of the attributes in the attribute
|
||||
// types table, with optional values replaced with null during conversion.
|
||||
//
|
||||
// All keys in the optional slice must also exist in the attrTypes map. If not,
|
||||
// this function will panic.
|
||||
//
|
||||
// After a map or array is passed to this function the caller must no longer
|
||||
// access it, since ownership is transferred to this library.
|
||||
func ObjectWithOptionalAttrs(attrTypes map[string]Type, optional []string) Type {
|
||||
attrTypesNorm := make(map[string]Type, len(attrTypes))
|
||||
for k, v := range attrTypes {
|
||||
attrTypesNorm[NormalizeString(k)] = v
|
||||
}
|
||||
|
||||
var optionalSet map[string]struct{}
|
||||
if len(optional) > 0 {
|
||||
optionalSet = make(map[string]struct{}, len(optional))
|
||||
for _, k := range optional {
|
||||
k = NormalizeString(k)
|
||||
if _, exists := attrTypesNorm[k]; !exists {
|
||||
panic(fmt.Sprintf("optional contains undeclared attribute %q", k))
|
||||
}
|
||||
optionalSet[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return Type{
|
||||
typeObject{
|
||||
AttrTypes: attrTypesNorm,
|
||||
AttrTypes: attrTypesNorm,
|
||||
AttrOptional: optionalSet,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +84,11 @@ func (t typeObject) Equals(other Type) bool {
|
|||
if !oty.Equals(ty) {
|
||||
return false
|
||||
}
|
||||
_, opt := t.AttrOptional[attr]
|
||||
_, oopt := ot.AttrOptional[attr]
|
||||
if opt != oopt {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -66,6 +111,14 @@ func (t typeObject) GoString() string {
|
|||
if len(t.AttrTypes) == 0 {
|
||||
return "cty.EmptyObject"
|
||||
}
|
||||
if len(t.AttrOptional) > 0 {
|
||||
opt := make([]string, len(t.AttrOptional))
|
||||
for k := range t.AttrOptional {
|
||||
opt = append(opt, k)
|
||||
}
|
||||
sort.Strings(opt)
|
||||
return fmt.Sprintf("cty.ObjectWithOptionalAttrs(%#v, %#v)", t.AttrTypes, opt)
|
||||
}
|
||||
return fmt.Sprintf("cty.Object(%#v)", t.AttrTypes)
|
||||
}
|
||||
|
||||
|
@ -133,3 +186,35 @@ func (t Type) AttributeTypes() map[string]Type {
|
|||
}
|
||||
panic("AttributeTypes on non-object Type")
|
||||
}
|
||||
|
||||
// OptionalAttributes returns a map representing the set of attributes
|
||||
// that are optional. Will panic if the receiver is not an object type
|
||||
// (use IsObjectType to confirm).
|
||||
//
|
||||
// The returned map is part of the internal state of the type, and is provided
|
||||
// for read access only. It is forbidden for any caller to modify the returned
|
||||
// map.
|
||||
func (t Type) OptionalAttributes() map[string]struct{} {
|
||||
if ot, ok := t.typeImpl.(typeObject); ok {
|
||||
return ot.AttrOptional
|
||||
}
|
||||
panic("OptionalAttributes on non-object Type")
|
||||
}
|
||||
|
||||
// AttributeOptional returns true if the attribute of the given name is
|
||||
// optional.
|
||||
//
|
||||
// Will panic if the receiver is not an object type (use IsObjectType to
|
||||
// confirm) or if the object type has no such attribute (use HasAttribute to
|
||||
// confirm).
|
||||
func (t Type) AttributeOptional(name string) bool {
|
||||
name = NormalizeString(name)
|
||||
if ot, ok := t.typeImpl.(typeObject); ok {
|
||||
if _, hasAttr := ot.AttrTypes[name]; !hasAttr {
|
||||
panic("no such attribute")
|
||||
}
|
||||
_, exists := ot.AttrOptional[name]
|
||||
return exists
|
||||
}
|
||||
panic("AttributeDefaultValue on non-object Type")
|
||||
}
|
||||
|
|
|
@ -196,3 +196,9 @@ func (r pathSetRules) Equivalent(a, b interface{}) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
// SameRules is true if both Rules instances are pathSetRules structs.
|
||||
func (r pathSetRules) SameRules(other set.Rules) bool {
|
||||
_, ok := other.(pathSetRules)
|
||||
return ok
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@ type Rules interface {
|
|||
// though it is *not* required that two values with the same hash value
|
||||
// be equivalent.
|
||||
Equivalent(interface{}, interface{}) bool
|
||||
|
||||
// SameRules returns true if the instance is equivalent to another Rules
|
||||
// instance.
|
||||
SameRules(Rules) bool
|
||||
}
|
||||
|
||||
// OrderedRules is an extension of Rules that can apply a partial order to
|
||||
|
|
|
@ -41,7 +41,7 @@ func NewSetFromSlice(rules Rules, vals []interface{}) Set {
|
|||
}
|
||||
|
||||
func sameRules(s1 Set, s2 Set) bool {
|
||||
return s1.rules == s2.rules
|
||||
return s1.rules.SameRules(s2.rules)
|
||||
}
|
||||
|
||||
func mustHaveSameRules(s1 Set, s2 Set) {
|
||||
|
@ -53,7 +53,7 @@ func mustHaveSameRules(s1 Set, s2 Set) {
|
|||
// HasRules returns true if and only if the receiving set has the given rules
|
||||
// instance as its rules.
|
||||
func (s Set) HasRules(rules Rules) bool {
|
||||
return s.rules == rules
|
||||
return s.rules.SameRules(rules)
|
||||
}
|
||||
|
||||
// Rules returns the receiving set's rules instance.
|
||||
|
|
|
@ -65,6 +65,17 @@ func (r setRules) Equivalent(v1 interface{}, v2 interface{}) bool {
|
|||
return eqv.v == true
|
||||
}
|
||||
|
||||
// SameRules is only true if the other Rules instance is also a setRules struct,
|
||||
// and the types are considered equal.
|
||||
func (r setRules) SameRules(other set.Rules) bool {
|
||||
rules, ok := other.(setRules)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return r.Type.Equals(rules.Type)
|
||||
}
|
||||
|
||||
// Less is an implementation of set.OrderedRules so that we can iterate over
|
||||
// set elements in a consistent order, where such an order is possible.
|
||||
func (r setRules) Less(v1, v2 interface{}) bool {
|
||||
|
|
|
@ -87,7 +87,7 @@ func (t Type) HasDynamicTypes() bool {
|
|||
case t.IsPrimitiveType():
|
||||
return false
|
||||
case t.IsCollectionType():
|
||||
return false
|
||||
return t.ElementType().HasDynamicTypes()
|
||||
case t.IsObjectType():
|
||||
attrTypes := t.AttributeTypes()
|
||||
for _, at := range attrTypes {
|
||||
|
|
|
@ -5,7 +5,8 @@ package cty
|
|||
type unknownType struct {
|
||||
}
|
||||
|
||||
// Unknown is a special value that can be
|
||||
// unknown is a special value that can be used as the internal value of a
|
||||
// Value to create a placeholder for a value that isn't yet known.
|
||||
var unknown interface{} = &unknownType{}
|
||||
|
||||
// UnknownVal returns an Value that represents an unknown value of the given
|
||||
|
|
|
@ -106,3 +106,37 @@ func (val Value) IsWhollyKnown() bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// HasWhollyKnownType checks if the value is dynamic, or contains any nested
|
||||
// DynamicVal. This implies that both the value is not known, and the final
|
||||
// type may change.
|
||||
func (val Value) HasWhollyKnownType() bool {
|
||||
// a null dynamic type is known
|
||||
if val.IsNull() {
|
||||
return true
|
||||
}
|
||||
|
||||
// an unknown DynamicPseudoType is a DynamicVal, but we don't want to
|
||||
// check that value for equality here, since this method is used within the
|
||||
// equality check.
|
||||
if !val.IsKnown() && val.ty == DynamicPseudoType {
|
||||
return false
|
||||
}
|
||||
|
||||
if val.CanIterateElements() {
|
||||
// if the value is not known, then we can look directly at the internal
|
||||
// types
|
||||
if !val.IsKnown() {
|
||||
return !val.ty.HasDynamicTypes()
|
||||
}
|
||||
|
||||
for it := val.ElementIterator(); it.Next(); {
|
||||
_, ev := it.Element()
|
||||
if !ev.HasWhollyKnownType() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -247,11 +247,6 @@ func SetVal(vals []Value) Value {
|
|||
val = unmarkedVal
|
||||
markSets = append(markSets, marks)
|
||||
}
|
||||
if val.ContainsMarked() {
|
||||
// FIXME: Allow this, but unmark the values and apply the
|
||||
// marking to the set itself instead.
|
||||
panic("set cannot contain marked values")
|
||||
}
|
||||
if elementType == DynamicPseudoType {
|
||||
elementType = val.ty
|
||||
} else if val.ty != DynamicPseudoType && !elementType.Equals(val.ty) {
|
||||
|
|
|
@ -3,7 +3,6 @@ package cty
|
|||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/go-cty/cty/set"
|
||||
)
|
||||
|
@ -133,9 +132,9 @@ func (val Value) Equals(other Value) Value {
|
|||
case val.IsKnown() && !other.IsKnown():
|
||||
switch {
|
||||
case val.IsNull(), other.ty.HasDynamicTypes():
|
||||
// If known is Null, we need to wait for the unkown value since
|
||||
// If known is Null, we need to wait for the unknown value since
|
||||
// nulls of any type are equal.
|
||||
// An unkown with a dynamic type compares as unknown, which we need
|
||||
// An unknown with a dynamic type compares as unknown, which we need
|
||||
// to check before the type comparison below.
|
||||
return UnknownVal(Bool)
|
||||
case !val.ty.Equals(other.ty):
|
||||
|
@ -148,9 +147,9 @@ func (val Value) Equals(other Value) Value {
|
|||
case other.IsKnown() && !val.IsKnown():
|
||||
switch {
|
||||
case other.IsNull(), val.ty.HasDynamicTypes():
|
||||
// If known is Null, we need to wait for the unkown value since
|
||||
// If known is Null, we need to wait for the unknown value since
|
||||
// nulls of any type are equal.
|
||||
// An unkown with a dynamic type compares as unknown, which we need
|
||||
// An unknown with a dynamic type compares as unknown, which we need
|
||||
// to check before the type comparison below.
|
||||
return UnknownVal(Bool)
|
||||
case !other.ty.Equals(val.ty):
|
||||
|
@ -171,7 +170,15 @@ func (val Value) Equals(other Value) Value {
|
|||
return BoolVal(false)
|
||||
}
|
||||
|
||||
if val.ty.HasDynamicTypes() || other.ty.HasDynamicTypes() {
|
||||
// Check if there are any nested dynamic values making this comparison
|
||||
// unknown.
|
||||
if !val.HasWhollyKnownType() || !other.HasWhollyKnownType() {
|
||||
// Even if we have dynamic values, we can still determine inequality if
|
||||
// there is no way the types could later conform.
|
||||
if val.ty.TestConformance(other.ty) != nil && other.ty.TestConformance(val.ty) != nil {
|
||||
return BoolVal(false)
|
||||
}
|
||||
|
||||
return UnknownVal(Bool)
|
||||
}
|
||||
|
||||
|
@ -262,24 +269,26 @@ func (val Value) Equals(other Value) Value {
|
|||
s2 := other.v.(set.Set)
|
||||
equal := true
|
||||
|
||||
// Note that by our definition of sets it's never possible for two
|
||||
// sets that contain unknown values (directly or indicrectly) to
|
||||
// ever be equal, even if they are otherwise identical.
|
||||
|
||||
// FIXME: iterating both lists and checking each item is not the
|
||||
// ideal implementation here, but it works with the primitives we
|
||||
// have in the set implementation. Perhaps the set implementation
|
||||
// can provide its own equality test later.
|
||||
s1.EachValue(func(v interface{}) {
|
||||
if !s2.Has(v) {
|
||||
// Two sets are equal if all of their values are known and all values
|
||||
// in one are also in the other.
|
||||
for it := s1.Iterator(); it.Next(); {
|
||||
rv := it.Value()
|
||||
if rv == unknown { // "unknown" is the internal representation of unknown-ness
|
||||
return UnknownVal(Bool)
|
||||
}
|
||||
if !s2.Has(rv) {
|
||||
equal = false
|
||||
}
|
||||
})
|
||||
s2.EachValue(func(v interface{}) {
|
||||
if !s1.Has(v) {
|
||||
}
|
||||
for it := s2.Iterator(); it.Next(); {
|
||||
rv := it.Value()
|
||||
if rv == unknown { // "unknown" is the internal representation of unknown-ness
|
||||
return UnknownVal(Bool)
|
||||
}
|
||||
if !s1.Has(rv) {
|
||||
equal = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
result = equal
|
||||
case ty.IsMapType():
|
||||
|
@ -454,17 +463,32 @@ func (val Value) RawEquals(other Value) bool {
|
|||
return true
|
||||
}
|
||||
return false
|
||||
case ty.IsSetType():
|
||||
s1 := val.v.(set.Set)
|
||||
s2 := other.v.(set.Set)
|
||||
|
||||
// Since we're intentionally ignoring our rule that two unknowns
|
||||
// are never equal, we can cheat here.
|
||||
// (This isn't 100% right since e.g. it will fail if the set contains
|
||||
// numbers that are infinite, which DeepEqual can't compare properly.
|
||||
// We're accepting that limitation for simplicity here, since this
|
||||
// function is here primarily for testing.)
|
||||
return reflect.DeepEqual(s1, s2)
|
||||
case ty.IsSetType():
|
||||
// Convert the set values into a slice so that we can compare each
|
||||
// value. This is safe because the underlying sets are ordered (see
|
||||
// setRules in set_internals.go), and so the results are guaranteed to
|
||||
// be in a consistent order for two equal sets
|
||||
setList1 := val.AsValueSlice()
|
||||
setList2 := other.AsValueSlice()
|
||||
|
||||
// If both physical sets have the same length and they have all of their
|
||||
// _known_ values in common, we know that both sets also have the same
|
||||
// number of unknown values.
|
||||
if len(setList1) != len(setList2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range setList1 {
|
||||
eq := setList1[i].RawEquals(setList2[i])
|
||||
if !eq {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here without returning false already then our sets are
|
||||
// equal enough for RawEquals purposes.
|
||||
return true
|
||||
|
||||
case ty.IsMapType():
|
||||
ety := ty.typeImpl.(typeMap).ElementTypeT
|
||||
|
@ -947,8 +971,7 @@ func (val Value) HasElement(elem Value) Value {
|
|||
// If the receiver is null then this function will panic.
|
||||
//
|
||||
// Note that Length is not supported for strings. To determine the length
|
||||
// of a string, call AsString and take the length of the native Go string
|
||||
// that is returned.
|
||||
// of a string, use the Length function in funcs/stdlib.
|
||||
func (val Value) Length() Value {
|
||||
if val.IsMarked() {
|
||||
val, valMarks := val.Unmark()
|
||||
|
@ -963,6 +986,25 @@ func (val Value) Length() Value {
|
|||
if !val.IsKnown() {
|
||||
return UnknownVal(Number)
|
||||
}
|
||||
if val.Type().IsSetType() {
|
||||
// The Length rules are a little different for sets because if any
|
||||
// unknown values are present then the values they are standing in for
|
||||
// may or may not be equal to other elements in the set, and thus they
|
||||
// may or may not coalesce with other elements and produce fewer
|
||||
// items in the resulting set.
|
||||
storeLength := int64(val.v.(set.Set).Length())
|
||||
if storeLength == 1 || val.IsWhollyKnown() {
|
||||
// If our set is wholly known then we know its length.
|
||||
//
|
||||
// We also know the length if the physical store has only one
|
||||
// element, even if that element is unknown, because there's
|
||||
// nothing else in the set for it to coalesce with and a single
|
||||
// unknown value cannot represent more than one known value.
|
||||
return NumberIntVal(storeLength)
|
||||
}
|
||||
// Otherwise, we cannot predict the length.
|
||||
return UnknownVal(Number)
|
||||
}
|
||||
|
||||
return NumberIntVal(int64(val.LengthInt()))
|
||||
}
|
||||
|
@ -972,6 +1014,13 @@ func (val Value) Length() Value {
|
|||
//
|
||||
// This is an integration method provided for the convenience of code bridging
|
||||
// into Go's type system.
|
||||
//
|
||||
// For backward compatibility with an earlier implementation error, LengthInt's
|
||||
// result can disagree with Length's result for any set containing unknown
|
||||
// values. Length can potentially indicate the set's length is unknown in that
|
||||
// case, whereas LengthInt will return the maximum possible length as if the
|
||||
// unknown values were each a placeholder for a value not equal to any other
|
||||
// value in the set.
|
||||
func (val Value) LengthInt() int {
|
||||
val.assertUnmarked()
|
||||
if val.Type().IsTupleType() {
|
||||
|
@ -995,6 +1044,15 @@ func (val Value) LengthInt() int {
|
|||
return len(val.v.([]interface{}))
|
||||
|
||||
case val.ty.IsSetType():
|
||||
// NOTE: This is technically not correct in cases where the set
|
||||
// contains unknown values, because in that case we can't know how
|
||||
// many known values those unknown values are standing in for -- they
|
||||
// might coalesce with other values once known.
|
||||
//
|
||||
// However, this incorrect behavior is preserved for backward
|
||||
// compatibility with callers that were relying on LengthInt rather
|
||||
// than calling Length. Instead of panicking when a set contains an
|
||||
// unknown value, LengthInt returns the largest possible length.
|
||||
return val.v.(set.Set).Length()
|
||||
|
||||
case val.ty.IsMapType():
|
||||
|
|
|
@ -61,6 +61,34 @@ func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Transformer is the interface used to optionally transform values in a
|
||||
// possibly-complex structure. The Enter method is called before traversing
|
||||
// through a given path, and the Exit method is called when traversal of a
|
||||
// path is complete.
|
||||
//
|
||||
// Use Enter when you want to transform a complex value before traversal
|
||||
// (preorder), and Exit when you want to transform a value after traversal
|
||||
// (postorder).
|
||||
//
|
||||
// The path passed to the given function may not be used after that function
|
||||
// returns, since its backing array is re-used for other calls.
|
||||
type Transformer interface {
|
||||
Enter(Path, Value) (Value, error)
|
||||
Exit(Path, Value) (Value, error)
|
||||
}
|
||||
|
||||
type postorderTransformer struct {
|
||||
callback func(Path, Value) (Value, error)
|
||||
}
|
||||
|
||||
func (t *postorderTransformer) Enter(p Path, v Value) (Value, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (t *postorderTransformer) Exit(p Path, v Value) (Value, error) {
|
||||
return t.callback(p, v)
|
||||
}
|
||||
|
||||
// Transform visits all of the values in a possibly-complex structure,
|
||||
// calling a given function for each value which has an opportunity to
|
||||
// replace that value.
|
||||
|
@ -77,7 +105,7 @@ func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
|
|||
// value constructor functions. An easy way to preserve invariants is to
|
||||
// ensure that the transform function never changes the value type.
|
||||
//
|
||||
// The callback function my halt the walk altogether by
|
||||
// The callback function may halt the walk altogether by
|
||||
// returning a non-nil error. If the returned error is about the element
|
||||
// currently being visited, it is recommended to use the provided path
|
||||
// value to produce a PathError describing that context.
|
||||
|
@ -86,10 +114,23 @@ func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
|
|||
// returns, since its backing array is re-used for other calls.
|
||||
func Transform(val Value, cb func(Path, Value) (Value, error)) (Value, error) {
|
||||
var path Path
|
||||
return transform(path, val, cb)
|
||||
return transform(path, val, &postorderTransformer{cb})
|
||||
}
|
||||
|
||||
func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value, error) {
|
||||
// TransformWithTransformer allows the caller to more closely control the
|
||||
// traversal used for transformation. See the documentation for Transformer for
|
||||
// more details.
|
||||
func TransformWithTransformer(val Value, t Transformer) (Value, error) {
|
||||
var path Path
|
||||
return transform(path, val, t)
|
||||
}
|
||||
|
||||
func transform(path Path, val Value, t Transformer) (Value, error) {
|
||||
val, err := t.Enter(path, val)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
|
||||
ty := val.Type()
|
||||
var newVal Value
|
||||
|
||||
|
@ -112,7 +153,7 @@ func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value
|
|||
path := append(path, IndexStep{
|
||||
Key: kv,
|
||||
})
|
||||
newEv, err := transform(path, ev, cb)
|
||||
newEv, err := transform(path, ev, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
|
@ -143,7 +184,7 @@ func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value
|
|||
path := append(path, IndexStep{
|
||||
Key: kv,
|
||||
})
|
||||
newEv, err := transform(path, ev, cb)
|
||||
newEv, err := transform(path, ev, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
|
@ -165,7 +206,7 @@ func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value
|
|||
path := append(path, GetAttrStep{
|
||||
Name: name,
|
||||
})
|
||||
newAV, err := transform(path, av, cb)
|
||||
newAV, err := transform(path, av, t)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
|
@ -178,5 +219,9 @@ func transform(path Path, val Value, cb func(Path, Value) (Value, error)) (Value
|
|||
newVal = val
|
||||
}
|
||||
|
||||
return cb(path, newVal)
|
||||
newVal, err = t.Exit(path, newVal)
|
||||
if err != nil {
|
||||
return DynamicVal, err
|
||||
}
|
||||
return newVal, err
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.11,!gccgo,!purego
|
||||
// +build go1.11,gc,!purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.11,!gccgo,!purego
|
||||
// +build go1.11,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm64,!s390x,!ppc64le arm64,!go1.11 gccgo purego
|
||||
// +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
// The differences in this and the original implementation are
|
||||
// due to the calling conventions and initialization of constants.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build amd64,!gccgo,!appengine,!purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
package curve25519
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// This code was translated into a form compatible with 6a from the public
|
||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||
|
||||
// +build amd64,!gccgo,!appengine,!purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
#define REDMASK51 0x0007FFFFFFFFFFFF
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64 gccgo appengine purego
|
||||
// +build !amd64 !gc purego
|
||||
|
||||
package curve25519
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build !purego
|
||||
|
||||
// Package subtle implements functions that are often useful in cryptographic
|
||||
// code but require careful thought to use correctly.
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build appengine
|
||||
|
||||
// Package subtle implements functions that are often useful in cryptographic
|
||||
// code but require careful thought to use correctly.
|
||||
package subtle // import "golang.org/x/crypto/internal/subtle"
|
||||
|
||||
// This is the Google App Engine standard variant based on reflect
|
||||
// because the unsafe package and cgo are disallowed.
|
||||
|
||||
import "reflect"
|
||||
|
||||
// AnyOverlap reports whether x and y share memory at any (not necessarily
|
||||
// corresponding) index. The memory beyond the slice length is ignored.
|
||||
func AnyOverlap(x, y []byte) bool {
|
||||
return len(x) > 0 && len(y) > 0 &&
|
||||
reflect.ValueOf(&x[0]).Pointer() <= reflect.ValueOf(&y[len(y)-1]).Pointer() &&
|
||||
reflect.ValueOf(&y[0]).Pointer() <= reflect.ValueOf(&x[len(x)-1]).Pointer()
|
||||
}
|
||||
|
||||
// InexactOverlap reports whether x and y share memory at any non-corresponding
|
||||
// index. The memory beyond the slice length is ignored. Note that x and y can
|
||||
// have different lengths and still not have any inexact overlap.
|
||||
//
|
||||
// InexactOverlap can be used to implement the requirements of the crypto/cipher
|
||||
// AEAD, Block, BlockMode and Stream interfaces.
|
||||
func InexactOverlap(x, y []byte) bool {
|
||||
if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
|
||||
return false
|
||||
}
|
||||
return AnyOverlap(x, y)
|
||||
}
|
|
@ -30,6 +30,8 @@ var (
|
|||
oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20})
|
||||
oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21})
|
||||
oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1})
|
||||
|
||||
errUnknownAttributeOID = errors.New("pkcs12: unknown attribute OID")
|
||||
)
|
||||
|
||||
type pfxPdu struct {
|
||||
|
@ -104,6 +106,11 @@ func unmarshal(in []byte, out interface{}) error {
|
|||
}
|
||||
|
||||
// ToPEM converts all "safe bags" contained in pfxData to PEM blocks.
|
||||
// Unknown attributes are discarded.
|
||||
//
|
||||
// Note that although the returned PEM blocks for private keys have type
|
||||
// "PRIVATE KEY", the bytes are not encoded according to PKCS #8, but according
|
||||
// to PKCS #1 for RSA keys and SEC 1 for ECDSA keys.
|
||||
func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) {
|
||||
encodedPassword, err := bmpString(password)
|
||||
if err != nil {
|
||||
|
@ -135,6 +142,9 @@ func convertBag(bag *safeBag, password []byte) (*pem.Block, error) {
|
|||
|
||||
for _, attribute := range bag.Attributes {
|
||||
k, v, err := convertAttribute(&attribute)
|
||||
if err == errUnknownAttributeOID {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -188,7 +198,7 @@ func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error)
|
|||
key = "Microsoft CSP Name"
|
||||
isString = true
|
||||
default:
|
||||
return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String())
|
||||
return "", "", errUnknownAttributeOID
|
||||
}
|
||||
|
||||
if isString {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!ppc64le,!s390x gccgo purego
|
||||
// +build !amd64,!ppc64le,!s390x !gc purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !gccgo,!purego
|
||||
// +build gc,!purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue