diff --git a/client/fingerprint/env_aws.go b/client/fingerprint/env_aws.go index e8b872940..efd00efbb 100644 --- a/client/fingerprint/env_aws.go +++ b/client/fingerprint/env_aws.go @@ -16,9 +16,15 @@ import ( "github.com/hashicorp/nomad/nomad/structs" ) -// This is where the AWS metadata server normally resides. We hardcode the -// "instance" path as well since it's the only one we access here. -const DEFAULT_AWS_URL = "http://169.254.169.254/latest/meta-data/" +const ( + // This is where the AWS metadata server normally resides. We hardcode the + // "instance" path as well since it's the only one we access here. + DEFAULT_AWS_URL = "http://169.254.169.254/latest/meta-data/" + + // AwsMetadataTimeout is the timeout used when contacting the AWS metadata + // service + AwsMetadataTimeout = 2 * time.Second +) // map of instance type to approximate speed, in Mbits/s // Estimates from http://stackoverflow.com/a/35806587 @@ -44,16 +50,25 @@ var ec2InstanceSpeedMap = map[*regexp.Regexp]int{ // EnvAWSFingerprint is used to fingerprint AWS metadata type EnvAWSFingerprint struct { StaticFingerprinter - logger *log.Logger + timeout time.Duration + logger *log.Logger } // NewEnvAWSFingerprint is used to create a fingerprint from AWS metadata func NewEnvAWSFingerprint(logger *log.Logger) Fingerprint { - f := &EnvAWSFingerprint{logger: logger} + f := &EnvAWSFingerprint{ + logger: logger, + timeout: AwsMetadataTimeout, + } return f } func (f *EnvAWSFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { + // Check if we should tighten the timeout + if cfg.ReadBoolDefault(TightenNetworkTimeoutsConfig, false) { + f.timeout = 1 * time.Millisecond + } + if !f.isAWS() { return false, nil } @@ -71,9 +86,8 @@ func (f *EnvAWSFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) metadataURL = DEFAULT_AWS_URL } - // assume 2 seconds is enough time for inside AWS network client := &http.Client{ - Timeout: 2 * time.Second, + Timeout: f.timeout, Transport: cleanhttp.DefaultTransport(), } @@ -174,9 +188,8 @@ func (f *EnvAWSFingerprint) isAWS() bool { metadataURL = DEFAULT_AWS_URL } - // assume 2 seconds is enough time for inside AWS network client := &http.Client{ - Timeout: 2 * time.Second, + Timeout: f.timeout, Transport: cleanhttp.DefaultTransport(), } @@ -217,9 +230,8 @@ func (f *EnvAWSFingerprint) linkSpeed() int { metadataURL = DEFAULT_AWS_URL } - // assume 2 seconds is enough time for inside AWS network client := &http.Client{ - Timeout: 2 * time.Second, + Timeout: f.timeout, Transport: cleanhttp.DefaultTransport(), } diff --git a/client/fingerprint/env_gce.go b/client/fingerprint/env_gce.go index 6f83ed224..506412e70 100644 --- a/client/fingerprint/env_gce.go +++ b/client/fingerprint/env_gce.go @@ -18,9 +18,15 @@ import ( "github.com/hashicorp/nomad/nomad/structs" ) -// This is where the GCE metadata server normally resides. We hardcode the -// "instance" path as well since it's the only one we access here. -const DEFAULT_GCE_URL = "http://169.254.169.254/computeMetadata/v1/instance/" +const ( + // This is where the GCE metadata server normally resides. We hardcode the + // "instance" path as well since it's the only one we access here. + DEFAULT_GCE_URL = "http://169.254.169.254/computeMetadata/v1/instance/" + + // GceMetadataTimeout is the timeout used when contacting the GCE metadata + // service + GceMetadataTimeout = 2 * time.Second +) type GCEMetadataNetworkInterface struct { AccessConfigs []struct { @@ -64,7 +70,7 @@ func NewEnvGCEFingerprint(logger *log.Logger) Fingerprint { // assume 2 seconds is enough time for inside GCE network client := &http.Client{ - Timeout: 2 * time.Second, + Timeout: GceMetadataTimeout, Transport: cleanhttp.DefaultTransport(), } @@ -126,6 +132,11 @@ func checkError(err error, logger *log.Logger, desc string) error { } func (f *EnvGCEFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { + // Check if we should tighten the timeout + if cfg.ReadBoolDefault(TightenNetworkTimeoutsConfig, false) { + f.client.Timeout = 1 * time.Millisecond + } + if !f.isGCE() { return false, nil } diff --git a/client/fingerprint/fingerprint.go b/client/fingerprint/fingerprint.go index 9706dd416..2d6d483b6 100644 --- a/client/fingerprint/fingerprint.go +++ b/client/fingerprint/fingerprint.go @@ -13,6 +13,10 @@ import ( // EmptyDuration is to be used by fingerprinters that are not periodic. const ( EmptyDuration = time.Duration(0) + + // TightenNetworkTimeoutsConfig is a config key that can be used during + // tests to tighten the timeouts for fingerprinters that make network calls. + TightenNetworkTimeoutsConfig = "test.tighten_network_timeouts" ) func init() { diff --git a/command/agent/testagent.go b/command/agent/testagent.go index 3e3d65063..0cb04bc70 100644 --- a/command/agent/testagent.go +++ b/command/agent/testagent.go @@ -14,6 +14,7 @@ import ( "time" "github.com/hashicorp/nomad/api" + "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/nomad" "github.com/hashicorp/nomad/nomad/structs" sconfig "github.com/hashicorp/nomad/nomad/structs/config" @@ -278,6 +279,12 @@ func (a *TestAgent) config() *Config { config.Bootstrap = true config.BootstrapExpect = 1 + // Tighten the fingerprinter timeouts + if conf.Client.Options == nil { + conf.Client.Options = make(map[string]string) + } + conf.Client.Options[fingerprint.TightenNetworkTimeoutsConfig] = "true" + if a.ConfigCallback != nil { a.ConfigCallback(conf) }