diff --git a/pkg/awsutils/awssession/session.go b/pkg/awsutils/awssession/session.go new file mode 100644 index 0000000000..4ccf4ad07e --- /dev/null +++ b/pkg/awsutils/awssession/session.go @@ -0,0 +1,81 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package awssession + +import ( + "fmt" + "net/http" + "os" + + "strconv" + "time" + + "github.com/aws/amazon-vpc-cni-k8s/pkg/utils/logger" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/session" +) + +// Http client timeout env for sessions +const ( + httpTimeoutEnv = "HTTP_TIMEOUT" + maxRetries = 15 +) + +var ( + version string + log = logger.Get() + // HTTP timeout default value in seconds (10 seconds) + httpTimeoutValue = 10 * time.Second +) + +func getHTTPTimeout() time.Duration { + httpTimeoutEnvInput := os.Getenv(httpTimeoutEnv) + // if httpTimeout is not empty, we convert value to int and overwrite default httpTimeoutValue + if httpTimeoutEnvInput != "" { + input, err := strconv.Atoi(httpTimeoutEnvInput) + if err == nil && input >= 10 { + log.Debugf("Using HTTP_TIMEOUT %v", input) + httpTimeoutValue = time.Duration(input) * time.Second + return httpTimeoutValue + } + } + log.Info("HTTP_TIMEOUT env is not set or set to less than 10 seconds, defaulting to httpTimeout to 10sec") + return httpTimeoutValue +} + +// New will return an session for service clients +func New() *session.Session { + awsCfg := aws.Config{ + MaxRetries: aws.Int(maxRetries), + HTTPClient: &http.Client{ + Timeout: getHTTPTimeout(), + }, + } + sess := session.Must(session.NewSession(&awsCfg)) + //injecting session handler info + injectUserAgent(&sess.Handlers) + + return sess +} + +// injectUserAgent will inject app specific user-agent into awsSDK +func injectUserAgent(handlers *request.Handlers) { + handlers.Build.PushFrontNamed(request.NamedHandler{ + Name: fmt.Sprintf("%s/user-agent", "amazon-vpc-cni-k8s"), + Fn: request.MakeAddToUserAgentHandler( + "amazon-vpc-cni-k8s", + fmt.Sprintf("version/%s",version)), + }) +} \ No newline at end of file diff --git a/pkg/awsutils/awssession/session_test.go b/pkg/awsutils/awssession/session_test.go new file mode 100644 index 0000000000..75f7989687 --- /dev/null +++ b/pkg/awsutils/awssession/session_test.go @@ -0,0 +1,23 @@ +package awssession + +import ( + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestHttpTimeoutReturnDefault(t *testing.T) { + os.Setenv(httpTimeoutEnv, "2") + defer os.Unsetenv(httpTimeoutEnv) + expectedHTTPTimeOut := time.Duration(10) * time.Second + assert.Equal(t, expectedHTTPTimeOut, getHTTPTimeout()) +} + +func TestHttpTimeoutWithValueAbove10(t *testing.T) { + os.Setenv(httpTimeoutEnv, "12") + defer os.Unsetenv(httpTimeoutEnv) + expectedHTTPTimeOut := time.Duration(12) * time.Second + assert.Equal(t, expectedHTTPTimeOut, getHTTPTimeout()) +} \ No newline at end of file diff --git a/pkg/awsutils/awsutils.go b/pkg/awsutils/awsutils.go index df65b83173..1cf83add95 100644 --- a/pkg/awsutils/awsutils.go +++ b/pkg/awsutils/awsutils.go @@ -30,13 +30,13 @@ import ( "github.com/aws/amazon-vpc-cni-k8s/pkg/utils/logger" "github.com/prometheus/client_golang/prometheus" + "github.com/aws/amazon-vpc-cni-k8s/pkg/awsutils/awssession" "github.com/aws/amazon-vpc-cni-k8s/pkg/ec2metadata" "github.com/aws/amazon-vpc-cni-k8s/pkg/ec2wrapper" "github.com/aws/amazon-vpc-cni-k8s/pkg/utils/retry" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" @@ -313,11 +313,10 @@ func New(useCustomNetworking bool) (*EC2InstanceMetadataCache, error) { cache.useCustomNetworking = useCustomNetworking log.Infof("Custom networking %v", cache.useCustomNetworking) - sess, err := session.NewSession(&aws.Config{Region: aws.String(cache.region), MaxRetries: aws.Int(15)}) - if err != nil { - log.Errorf("Failed to initialize AWS SDK session %v", err) - return nil, errors.Wrap(err, "instance metadata: failed to initialize AWS SDK session") - } + sess := awssession.New() + + awsCfg := aws.NewConfig().WithRegion(cache.region) + sess = sess.Copy(awsCfg) ec2SVC := ec2wrapper.New(sess) cache.ec2SVC = ec2SVC diff --git a/pkg/ec2metadatawrapper/ec2metadatawrapper.go b/pkg/ec2metadatawrapper/ec2metadatawrapper.go index b8243c45f2..66c56dc4fb 100644 --- a/pkg/ec2metadatawrapper/ec2metadatawrapper.go +++ b/pkg/ec2metadatawrapper/ec2metadatawrapper.go @@ -2,9 +2,8 @@ package ec2metadatawrapper import ( - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/amazon-vpc-cni-k8s/pkg/awsutils/awssession" "github.com/aws/aws-sdk-go/aws/ec2metadata" - "github.com/aws/aws-sdk-go/aws/session" ) const ( @@ -32,9 +31,7 @@ type ec2MetadataClientImpl struct { // New creates an ec2metadata client to retrieve metadata func New(client HTTPClient) EC2MetadataClient { if client == nil { - awsSession := session.Must(session.NewSession(aws.NewConfig(). - WithMaxRetries(metadataRetries), - )) + awsSession := awssession.New() return &ec2MetadataClientImpl{client: ec2metadata.New(awsSession)} } return &ec2MetadataClientImpl{client: client} diff --git a/pkg/ec2wrapper/ec2wrapper.go b/pkg/ec2wrapper/ec2wrapper.go index 34f0268ea2..67eac0c425 100644 --- a/pkg/ec2wrapper/ec2wrapper.go +++ b/pkg/ec2wrapper/ec2wrapper.go @@ -2,11 +2,11 @@ package ec2wrapper import ( + "github.com/aws/amazon-vpc-cni-k8s/pkg/awsutils/awssession" "github.com/aws/amazon-vpc-cni-k8s/pkg/ec2metadatawrapper" "github.com/aws/amazon-vpc-cni-k8s/pkg/utils/logger" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/ec2metadata" - "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2/ec2iface" "github.com/pkg/errors" @@ -30,7 +30,7 @@ type EC2Wrapper struct { //NewMetricsClient returns an instance of the EC2 wrapper func NewMetricsClient() (*EC2Wrapper, error) { - metricsSession := session.Must(session.NewSession()) + sess := awssession.New() ec2MetadataClient := ec2metadatawrapper.New(nil) instanceIdentityDocument, err := ec2MetadataClient.GetInstanceIdentityDocument() @@ -38,7 +38,9 @@ func NewMetricsClient() (*EC2Wrapper, error) { return &EC2Wrapper{}, err } - ec2ServiceClient := ec2.New(metricsSession, aws.NewConfig().WithMaxRetries(maxRetries).WithRegion(instanceIdentityDocument.Region)) + awsCfg := aws.NewConfig().WithRegion(instanceIdentityDocument.Region) + sess = sess.Copy(awsCfg) + ec2ServiceClient := ec2.New(sess) return &EC2Wrapper{ ec2ServiceClient: ec2ServiceClient, diff --git a/pkg/publisher/publisher.go b/pkg/publisher/publisher.go index 5ffaf40848..8a3d1675ff 100644 --- a/pkg/publisher/publisher.go +++ b/pkg/publisher/publisher.go @@ -19,10 +19,10 @@ import ( "sync" "time" + "github.com/aws/amazon-vpc-cni-k8s/pkg/awsutils/awssession" "github.com/aws/amazon-vpc-cni-k8s/pkg/ec2metadatawrapper" "github.com/aws/amazon-vpc-cni-k8s/pkg/ec2wrapper" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" @@ -91,7 +91,7 @@ type cloudWatchPublisher struct { // New returns a new instance of `Publisher` func New(ctx context.Context) (Publisher, error) { // Get AWS session - awsSession := session.Must(session.NewSession()) + sess := awssession.New() // Get cluster-ID ec2Client, err := ec2wrapper.NewMetricsClient() @@ -107,7 +107,7 @@ func New(ctx context.Context) (Publisher, error) { if err != nil { return nil, errors.Wrap(err, "publisher: Unable to obtain region") } - cloudwatchClient := cloudwatch.New(awsSession, aws.NewConfig().WithMaxRetries(cloudwatchClientMaxRetries).WithRegion(region)) + cloudwatchClient := cloudwatch.New(sess, aws.NewConfig().WithMaxRetries(cloudwatchClientMaxRetries).WithRegion(region)) // Build derived context derivedContext, cancel := context.WithCancel(ctx) diff --git a/test/integration/go.mod b/test/integration/go.mod index fd81626c48..29e5e08e7e 100644 --- a/test/integration/go.mod +++ b/test/integration/go.mod @@ -53,6 +53,7 @@ replace k8s.io/sample-controller => k8s.io/sample-controller v0.0.0-201908191433 require ( github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega v1.5.0 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect k8s.io/api v0.0.0 k8s.io/apimachinery v0.0.0 k8s.io/client-go v0.0.0