diff --git a/modules/aws/s3.go b/modules/aws/s3.go index 6fc21ad20..42a93e731 100644 --- a/modules/aws/s3.go +++ b/modules/aws/s3.go @@ -420,6 +420,33 @@ func GetS3BucketPolicyE(t testing.TestingT, awsRegion string, bucket string) (st return aws.ToString(res.Policy), nil } +func GetS3BucketOwnershipControls(t testing.TestingT, awsRegion, bucket string) []string { + rules, err := GetS3BucketOwnershipControlsE(t, awsRegion, bucket) + require.NoError(t, err) + + return rules +} + +func GetS3BucketOwnershipControlsE(t testing.TestingT, awsRegion, bucket string) ([]string, error) { + s3Client, err := NewS3ClientE(t, awsRegion) + if err != nil { + return nil, err + } + + out, err := s3Client.GetBucketOwnershipControls(context.Background(), &s3.GetBucketOwnershipControlsInput{ + Bucket: &bucket, + }) + if err != nil { + return nil, err + } + + rules := make([]string, 0, len(out.OwnershipControls.Rules)) + for _, rule := range out.OwnershipControls.Rules { + rules = append(rules, string(rule.ObjectOwnership)) + } + return rules, nil +} + // AssertS3BucketExists checks if the given S3 bucket exists in the given region and fail the test if it does not. func AssertS3BucketExists(t testing.TestingT, region string, name string) { err := AssertS3BucketExistsE(t, region, name) @@ -478,39 +505,6 @@ func AssertS3BucketPolicyExistsE(t testing.TestingT, region string, bucketName s return nil } -// AssertS3BucketServerSideEncryption checks if the given S3 bucket has a server side encryption configured using the given algorithm and fail the test if it does not -func AssertS3BucketServerSideEncryption(t testing.TestingT, region string, bucketName string, algorithm types.ServerSideEncryption) { - err := AssertS3BucketServerSideEncryptionE(t, region, bucketName, algorithm) - require.NoError(t, err) -} - -// AssertS3BucketServerSideEncryptionE checks if the given S3 bucket has a server side encryption configured using the given algorithm and returns an error if it does not -func AssertS3BucketServerSideEncryptionE(t testing.TestingT, region string, bucketName string, algorithm types.ServerSideEncryption) (err error) { - s3Client, err := NewS3ClientE(t, region) - if err != nil { - return err - } - input := &s3.GetBucketEncryptionInput{ - Bucket: aws.String(bucketName), - } - c, err := s3Client.GetBucketEncryption(context.Background(), input) - if err != nil { - return err - } - - err = fmt.Errorf("SSE is not enabled for bucket %s in region %s", bucketName, region) - for _, rule := range c.ServerSideEncryptionConfiguration.Rules { - if rule.ApplyServerSideEncryptionByDefault == nil { - continue - } - if rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm == algorithm { - return nil - } - } - return - -} - // NewS3Client creates an S3 client. func NewS3Client(t testing.TestingT, region string) *s3.Client { client, err := NewS3ClientE(t, region) diff --git a/modules/aws/s3_test.go b/modules/aws/s3_test.go index c375e8a90..63a37f4a5 100644 --- a/modules/aws/s3_test.go +++ b/modules/aws/s3_test.go @@ -268,41 +268,47 @@ func testEmptyBucket(t *testing.T, s3Client *s3.Client, region string, s3BucketN require.Equal(t, 0, len((*bucketObjects).Contents)) } -func TestAssertS3BucketServerSideEncryptionE(t *testing.T) { +func TestGetS3BucketOwnershipControls(t *testing.T) { t.Parallel() region := GetRandomStableRegion(t, nil, nil) - s3client := NewS3Client(t, region) - id := random.UniqueId() logger.Default.Logf(t, "Random values selected. Region = %s, Id = %s\n", region, id) - table := []types.ServerSideEncryption{ - types.ServerSideEncryptionAes256, - types.ServerSideEncryptionAwsKms, - } - for i, tt := range table { - t.Run(fmt.Sprintf("%s", tt), func(t *testing.T) { - s3BucketName := fmt.Sprintf("gruntwork-terratest-sse-%d-%s", i, strings.ToLower(id)) - CreateS3Bucket(t, region, s3BucketName) - t.Cleanup(func() { DeleteS3Bucket(t, region, s3BucketName) }) + s3BucketName := "gruntwork-terratest-" + strings.ToLower(id) + CreateS3Bucket(t, region, s3BucketName) + t.Cleanup(func() { + DeleteS3Bucket(t, region, s3BucketName) + }) - input := &s3.PutBucketEncryptionInput{ - Bucket: aws.String(s3BucketName), - ServerSideEncryptionConfiguration: &types.ServerSideEncryptionConfiguration{ - Rules: []types.ServerSideEncryptionRule{ - { - ApplyServerSideEncryptionByDefault: &types.ServerSideEncryptionByDefault{ - SSEAlgorithm: tt, - }, - }, + t.Run("Exist", func(t *testing.T) { + s3Client, err := NewS3ClientE(t, region) + require.NoError(t, err) + _, err = s3Client.PutBucketOwnershipControls(context.Background(), &s3.PutBucketOwnershipControlsInput{ + Bucket: &s3BucketName, + OwnershipControls: &types.OwnershipControls{ + Rules: []types.OwnershipControlsRule{ + { + ObjectOwnership: types.ObjectOwnershipBucketOwnerEnforced, }, }, - } - _, err := s3client.PutBucketEncryption(context.Background(), input) + }, + }) + require.NoError(t, err) + t.Cleanup(func() { + _, err := s3Client.DeleteBucketOwnershipControls(context.Background(), &s3.DeleteBucketOwnershipControlsInput{ + Bucket: &s3BucketName, + }) require.NoError(t, err) - - AssertS3BucketServerSideEncryption(t, region, s3BucketName, tt) }) - } + + controls := GetS3BucketOwnershipControls(t, region, s3BucketName) + assert.Equal(t, 1, len(controls)) + assert.Equal(t, string(types.ObjectOwnershipBucketOwnerEnforced), controls[0]) + }) + + t.Run("NotExist", func(t *testing.T) { + _, err := GetS3BucketOwnershipControlsE(t, region, s3BucketName) + assert.Error(t, err) + }) } diff --git a/modules/k8s/kubectl.go b/modules/k8s/kubectl.go index a2647017b..fdc5bc508 100644 --- a/modules/k8s/kubectl.go +++ b/modules/k8s/kubectl.go @@ -34,6 +34,9 @@ func RunKubectlAndGetOutputE(t testing.TestingT, options *KubectlOptions, args . if options.Namespace != "" { cmdArgs = append(cmdArgs, "--namespace", options.Namespace) } + if options.RequestTimeout > 0 { + cmdArgs = append(cmdArgs, "--request-timeout", options.RequestTimeout.String()) + } cmdArgs = append(cmdArgs, args...) command := shell.Command{ Command: "kubectl", diff --git a/modules/k8s/kubectl_options.go b/modules/k8s/kubectl_options.go index 731a455bf..4a102b5b1 100644 --- a/modules/k8s/kubectl_options.go +++ b/modules/k8s/kubectl_options.go @@ -1,6 +1,8 @@ package k8s import ( + "time" + "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/testing" "k8s.io/client-go/rest" @@ -8,13 +10,14 @@ import ( // KubectlOptions represents common options necessary to specify for all Kubectl calls type KubectlOptions struct { - ContextName string - ConfigPath string - Namespace string - Env map[string]string - InClusterAuth bool - RestConfig *rest.Config - Logger *logger.Logger + ContextName string + ConfigPath string + Namespace string + Env map[string]string + InClusterAuth bool + RestConfig *rest.Config + Logger *logger.Logger + RequestTimeout time.Duration } // NewKubectlOptions will return a pointer to new instance of KubectlOptions with the configured options diff --git a/modules/k8s/kubectl_test.go b/modules/k8s/kubectl_test.go index bc12bb2be..fbe05c6a6 100644 --- a/modules/k8s/kubectl_test.go +++ b/modules/k8s/kubectl_test.go @@ -11,10 +11,14 @@ package k8s import ( "fmt" + "net/http" + "net/http/httptest" "strings" "testing" + "time" "github.com/gruntwork-io/terratest/modules/random" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -26,3 +30,62 @@ func TestRunKubectlAndGetOutputReturnsOutput(t *testing.T) { require.NoError(t, err) require.Equal(t, output, "yes") } + +func TestKubectlRequestTimeout(t *testing.T) { + t.Parallel() + + var parsedTimeout time.Duration + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + parsedTimeout, _ = time.ParseDuration(r.URL.Query().Get("timeout")) + select { + case <-time.After(3 * time.Second): + case <-r.Context().Done(): + } + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("dummy-error")) + })) + + config := fmt.Sprintf(` +apiVersion: v1 +kind: Config +clusters: +- name: dummy-cluster + cluster: + server: %s +users: +- name: dummy-user + user: + token: dummy-token +contexts: +- name: dummy-context + context: + cluster: dummy-cluster + user: dummy-user +current-context: dummy-context +`, server.URL) + + t.Run("WithoutTimeout", func(t *testing.T) { + options := &KubectlOptions{ + ContextName: "dummy-context", + ConfigPath: StoreConfigToTempFile(t, config), + } + _, err := RunKubectlAndGetOutputE(t, options, "get", "pods") + require.Error(t, err) + assert.Contains(t, err.Error(), "dummy-error") + assert.NotContains(t, err.Error(), "Client.Timeout exceeded while awaiting headers") + }) + + t.Run("WithTimeout", func(t *testing.T) { + options := &KubectlOptions{ + ContextName: "dummy-context", + ConfigPath: StoreConfigToTempFile(t, config), + RequestTimeout: time.Second, + } + _, err := RunKubectlAndGetOutputE(t, options, "get", "pods") + require.Error(t, err) + assert.Equal(t, options.RequestTimeout, parsedTimeout) + assert.NotContains(t, err.Error(), "dummy-error") + assert.Contains(t, err.Error(), "Client.Timeout exceeded while awaiting headers") + }) + +} diff --git a/modules/terraform/cmd.go b/modules/terraform/cmd.go index ea4baf511..bce32658d 100644 --- a/modules/terraform/cmd.go +++ b/modules/terraform/cmd.go @@ -122,7 +122,7 @@ func RunTerraformCommandAndGetStdoutE(t testing.TestingT, additionalOptions *Opt cmd := generateCommand(options, args...) description := fmt.Sprintf("%s %v", options.TerraformBinary, args) return retry.DoWithRetryableErrorsE(t, description, options.RetryableTerraformErrors, options.MaxRetries, options.TimeBetweenRetries, func() (string, error) { - s, err := shell.RunCommandAndGetOutputE(t, cmd) + s, err := shell.RunCommandAndGetStdOutE(t, cmd) if err != nil { return s, err }