diff --git a/cmd/provider/main.go b/cmd/provider/main.go index bc86879b..0f35a16f 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -100,7 +100,7 @@ func main() { enableManagementPolicies = app.Flag("enable-management-policies", "Enable support for Management Policies.").Default("false").Envar("ENABLE_MANAGEMENT_POLICIES").Bool() autoPauseBucket = app.Flag("auto-pause-bucket", "Enable auto pause of reconciliation of ready buckets").Default("false").Envar("AUTO_PAUSE_BUCKET").Bool() - minReplicas = app.Flag("minimum-replicas", "Minimum number of replicas of a bucket before it is considered synced").Default("2").Envar("MINIMUM_REPLICAS").Uint() + minReplicas = app.Flag("minimum-replicas", "Minimum number of replicas of a bucket before it is considered Ready").Default("1").Envar("MINIMUM_REPLICAS").Uint() recreateMissingBucket = app.Flag("recreate-missing-bucket", "Recreates existing bucket if missing").Default("true").Envar("RECREATE_MISSING_BUCKET").Bool() assumeRoleArn = app.Flag("assume-role-arn", "Assume role ARN to be used for STS authentication").Default("").Envar("ASSUME_ROLE_ARN").String() diff --git a/e2e/tests/stable/chainsaw-test.yaml b/e2e/tests/stable/chainsaw-test.yaml index 0f0e92c3..fced3a4e 100755 --- a/e2e/tests/stable/chainsaw-test.yaml +++ b/e2e/tests/stable/chainsaw-test.yaml @@ -568,6 +568,25 @@ spec: - local-dev-control-plane:32568 entrypoint: ../../../hack/expect_bucket.sh + - name: Delete localstack-b-bucket. + try: + - command: + # We need to "unpause" auto-pause-bucket to allow deletion. + args: + - patch + - --type=merge + - buckets + - localstack-b-bucket + - -p + - '{"metadata":{"labels":{"crossplane.io/paused":"false"}}}' + entrypoint: kubectl + - command: + args: + - delete + - bucket + - localstack-b-bucket + entrypoint: kubectl + - name: Make localstack-a unreachable and therefore Unhealthy. try: - command: @@ -666,7 +685,8 @@ spec: autoPause: true forProvider: {} # Assert auto-pause-bucket is only created on localstack-b - # and localstack-c, as localstack-a is currently unhealthy. + # and localstack-c, as localstack-a is currently unhealthy + # and that it is not paused or Synced. - assert: resource: apiVersion: provider-ceph.ceph.crossplane.io/v1alpha1 @@ -676,7 +696,6 @@ spec: finalizers: - "finalizer.managedresource.crossplane.io" labels: - crossplane.io/paused: "true" provider-ceph.backends.localstack-a: "true" provider-ceph.backends.localstack-b: "true" provider-ceph.backends.localstack-c: "true" @@ -697,8 +716,8 @@ spec: - reason: Available status: "True" type: Ready - - reason: ReconcileSuccess - status: "True" + - reason: ReconcileError + status: "False" type: Synced # Assert avoid-localstack-c-bucket is only created on localstack-b, # as localstack-a is currently unhealthy and this bucket is not intended @@ -752,7 +771,7 @@ spec: - reason: HealthCheckSuccess status: "True" type: Ready - + # Assert auto-pause-bucket is created on localstack-a and that it is Synced and paused. - name: Assert auto-pause-bucket and avoid-localstack-c-bucket are created on localstack-a now that it is Healthy. try: - assert: diff --git a/internal/controller/bucket/delete.go b/internal/controller/bucket/delete.go index 2feb48c8..50d00c56 100644 --- a/internal/controller/bucket/delete.go +++ b/internal/controller/bucket/delete.go @@ -2,7 +2,6 @@ package bucket import ( "context" - "math" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -106,8 +105,7 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) error { // CR spec. If the deletion is successful or unsuccessful, the bucket CR status must be // updated. if err := c.updateBucketCR(ctx, bucket, func(bucketDeepCopy, bucketLatest *v1alpha1.Bucket) UpdateRequired { - // Bucket status is unavailable at this point. Use math.MaxUint as minReplicas is irrelevant in this scenario. - setBucketStatus(bucketLatest, bucketBackends, providerNames, math.MaxUint) + setBucketStatus(bucketLatest, bucketBackends, providerNames, c.minReplicas) return NeedsStatusUpdate }); err != nil { diff --git a/internal/controller/bucket/helpers.go b/internal/controller/bucket/helpers.go index 7775dee1..f2b49c74 100644 --- a/internal/controller/bucket/helpers.go +++ b/internal/controller/bucket/helpers.go @@ -3,7 +3,6 @@ package bucket import ( "context" "fmt" - "math" "slices" "strings" @@ -36,9 +35,15 @@ func isBucketPaused(bucket *v1alpha1.Bucket) bool { // isPauseRequired determines if the Bucket should be paused. // //nolint:gocyclo,cyclop // Function requires numerous checks. -func isPauseRequired(bucket *v1alpha1.Bucket, providerNames []string, minReplicas uint, c map[string]backendstore.S3Client, bb *bucketBackends, autopauseEnabled bool) bool { - // If the number of backends on which the bucket is available is less than the number of providerNames or minReplicas, then the bucket must not be paused. - if float64(bb.countBucketsAvailableOnBackends(bucket.Name, providerNames, c)) < math.Min(float64(len(providerNames)), float64(minReplicas)) { +func isPauseRequired(bucket *v1alpha1.Bucket, providerNames []string, c map[string]backendstore.S3Client, bb *bucketBackends, autopauseEnabled bool) bool { + // Avoid pausing if the Bucket CR is not Ready and Synced. + if !(bucket.Status.GetCondition(xpv1.TypeReady).Equal(xpv1.Available()) && + bucket.Status.GetCondition(xpv1.TypeSynced).Equal(xpv1.ReconcileSuccess())) { + return false + } + + // Avoid pausing if the number of backends on which the bucket is available is less than the number of providerNames. + if float64(bb.countBucketsAvailableOnBackends(bucket.Name, providerNames, c)) < float64(len(providerNames)) { return false } @@ -170,14 +175,15 @@ func setBucketStatus(bucket *v1alpha1.Bucket, bucketBackends *bucketBackends, pr } unavailableBackends = append(unavailableBackends, backendName) } - // The Bucket CR is considered Available if the bucket is available on any backend. - if ok > 0 { + // The Bucket CR is considered Available if the bucket is available on "minReplicas" + // number of backends (default = 1). + if ok >= int(minReplicas) { bucket.Status.SetConditions(xpv1.Available()) } // The Bucket CR is considered Synced (ReconcileSuccess) once the bucket is available - // on the lesser of all backends or minimum replicas. We also ensure that the overall - // Bucket CR is available (in a Ready state) - this should already be the case. - if float64(ok) >= math.Min(float64(len(providerNames)), float64(minReplicas)) && + // on all backends. We also ensure that the overall Bucket CR is available (in a Ready + // state) - this should already be the case. + if ok >= len(providerNames) && bucket.Status.GetCondition(xpv1.TypeReady).Equal(xpv1.Available()) { bucket.Status.SetConditions(xpv1.ReconcileSuccess()) diff --git a/internal/controller/bucket/helpers_test.go b/internal/controller/bucket/helpers_test.go new file mode 100644 index 00000000..f8eeff9f --- /dev/null +++ b/internal/controller/bucket/helpers_test.go @@ -0,0 +1,1377 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bucket + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/errors" + "github.com/crossplane/crossplane-runtime/pkg/meta" + "github.com/linode/provider-ceph/apis/provider-ceph/v1alpha1" + "github.com/linode/provider-ceph/internal/backendstore" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +//nolint:maintidx // Requires many scenarios for full coverage. +func TestIsPauseRequired(t *testing.T) { + t.Parallel() + available := xpv1.Available() + unavailable := xpv1.Unavailable() + vEnabled := v1alpha1.VersioningStatusEnabled + someErr := errors.New("some error") + type args struct { + bucket *v1alpha1.Bucket + providerNames []string + clients map[string]backendstore.S3Client + bucketBackends *bucketBackends + autoPauseEnabled bool + } + + type want struct { + pauseIsRequired bool + } + + cases := map[string]struct { + reason string + args args + want want + }{ + "Bucket Status has no conditions - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{}, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Bucket Status has Ready condition but no Synced condition - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + }, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Bucket Status has Synced condition but no Ready condition - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.ReconcileError(someErr), + }, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Bucket Status has not Ready and not Synced conditions - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Unavailable(), + xpv1.ReconcileError(someErr), + }, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Bucket Status has Ready but not Synced conditions - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileError(someErr), + }, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Bucket Status has Synced but not Ready conditions - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Unavailable(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + // All Buckets from this point are Ready and Synced. + "One backend unavailable in bucket backends - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Unavailable(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "One backend missing in bucket backends - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "All backends available in bucket backends but no autopause - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "All backends available in bucket backends and autopause enabled but pause label false - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "false", + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + autoPauseEnabled: true, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "All backends available in bucket backends and autopause enabled for bucket but pause label false - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "false", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "All backends available in bucket backends and autopause enabled and empty pause label - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + autoPauseEnabled: true, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "All backends available in bucket backends and autopause enabled for bucket and empty pause label - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "All backends available in bucket backends and autopause enabled and no pause label - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + "some": "label", + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + autoPauseEnabled: true, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "All backends available in bucket backends and autopause enabled for bucket and no pause label - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + "some": "label", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "Lifecycle config enabled and specified but unavailable on one backend - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + LifecycleConfiguration: &v1alpha1.BucketLifecycleConfiguration{ + Rules: []v1alpha1.LifecycleRule{ + { + Status: "Enabled", + }, + }, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &unavailable, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Lifecycle config enabled and specified but missing from one backend - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + LifecycleConfiguration: &v1alpha1.BucketLifecycleConfiguration{ + Rules: []v1alpha1.LifecycleRule{ + { + Status: "Enabled", + }, + }, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Lifecycle config enabled and specified and available on all backends - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + LifecycleConfiguration: &v1alpha1.BucketLifecycleConfiguration{ + Rules: []v1alpha1.LifecycleRule{ + { + Status: "Enabled", + }, + }, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "Lifecycle config disabled but not removed from all backends - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + LifecycleConfigurationDisabled: true, + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + LifecycleConfiguration: &v1alpha1.BucketLifecycleConfiguration{ + Rules: []v1alpha1.LifecycleRule{ + { + Status: "Enabled", + }, + }, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &unavailable, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Lifecycle config disabled and removed from all backends - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + LifecycleConfigurationDisabled: true, + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + LifecycleConfiguration: &v1alpha1.BucketLifecycleConfiguration{ + Rules: []v1alpha1.LifecycleRule{ + { + Status: "Enabled", + }, + }, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "Versioning config specified but unavailable on one backend - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + VersioningConfiguration: &v1alpha1.VersioningConfiguration{ + Status: &vEnabled, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &unavailable, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Versioning config specified but missing on one backend - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + VersioningConfiguration: &v1alpha1.VersioningConfiguration{ + Status: &vEnabled, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Versioning config specified and available on all backends - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + VersioningConfiguration: &v1alpha1.VersioningConfiguration{ + Status: &vEnabled, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "Versioning config not specified (suspended) but unavailable on one backend - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{}, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &unavailable, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Versioning config not specified (suspended) but missing on one backend - no pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{}, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: false, + }, + }, + "Versioning config not specified (suspended) and available on all backends - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{}, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + VersioningConfigurationCondition: &available, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "All subresources specified and available on all backends and autopause enabled for bucket - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + AutoPause: true, + ForProvider: v1alpha1.BucketParameters{ + LifecycleConfiguration: &v1alpha1.BucketLifecycleConfiguration{ + Rules: []v1alpha1.LifecycleRule{ + { + Status: "Enabled", + }, + }, + }, + VersioningConfiguration: &v1alpha1.VersioningConfiguration{ + Status: &vEnabled, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + VersioningConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + VersioningConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + VersioningConfigurationCondition: &available, + }, + }, + }, + }, + }, + want: want{ + pauseIsRequired: true, + }, + }, + "All subresources specified and available on all backends and autopause enabled - pause": { + args: args{ + bucket: &v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bucket", + Labels: map[string]string{ + meta.AnnotationKeyReconciliationPaused: "", + }, + }, + Spec: v1alpha1.BucketSpec{ + ForProvider: v1alpha1.BucketParameters{ + LifecycleConfiguration: &v1alpha1.BucketLifecycleConfiguration{ + Rules: []v1alpha1.LifecycleRule{ + { + Status: "Enabled", + }, + }, + }, + VersioningConfiguration: &v1alpha1.VersioningConfiguration{ + Status: &vEnabled, + }, + }, + }, + Status: v1alpha1.BucketStatus{ + ResourceStatus: xpv1.ResourceStatus{ + ConditionedStatus: xpv1.ConditionedStatus{ + Conditions: []xpv1.Condition{ + xpv1.Available(), + xpv1.ReconcileSuccess(), + }, + }, + }, + }, + }, + providerNames: []string{"s3-backend-1", "s3-backend-2", "s3-backend-3"}, + clients: map[string]backendstore.S3Client{ + "s3-backend-1": nil, + "s3-backend-2": nil, + "s3-backend-3": nil, + }, + bucketBackends: &bucketBackends{ + backends: map[string]v1alpha1.Backends{ + "bucket": { + "s3-backend-1": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + VersioningConfigurationCondition: &available, + }, + "s3-backend-2": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + VersioningConfigurationCondition: &available, + }, + "s3-backend-3": &v1alpha1.BackendInfo{ + BucketCondition: xpv1.Available(), + LifecycleConfigurationCondition: &available, + VersioningConfigurationCondition: &available, + }, + }, + }, + }, + autoPauseEnabled: true, + }, + want: want{ + pauseIsRequired: true, + }, + }, + } + for name, tc := range cases { + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := isPauseRequired(tc.args.bucket, + tc.args.providerNames, + tc.args.clients, + tc.args.bucketBackends, + tc.args.autoPauseEnabled, + ) + assert.Equal(t, tc.want.pauseIsRequired, got, "unexpected response") + }) + } +} diff --git a/internal/controller/bucket/update.go b/internal/controller/bucket/update.go index 47e2de52..f5fab669 100644 --- a/internal/controller/bucket/update.go +++ b/internal/controller/bucket/update.go @@ -108,7 +108,7 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext // criteria is met before pausing a Bucket CR. Otherwise we check to see if there are // backends that the bucket was not updated on and if so, we set the updateAllErr // which will be returned at the end of this function, triggering a requeue. - if isPauseRequired(bucketLatest, allBackendsToUpdateOn, c.minReplicas, cls, bucketBackends, c.autoPauseBucket) { + if isPauseRequired(bucketLatest, allBackendsToUpdateOn, cls, bucketBackends, c.autoPauseBucket) { c.log.Info("Auto pausing bucket", consts.KeyBucketName, bucket.Name) bucketLatest.Labels[meta.AnnotationKeyReconciliationPaused] = True } else if updateAllErr == nil && len(activeBackendsToUpdateOn) != len(allBackendsToUpdateOn) { diff --git a/internal/controller/bucket/update_test.go b/internal/controller/bucket/update_test.go index 750420a6..02f30f20 100644 --- a/internal/controller/bucket/update_test.go +++ b/internal/controller/bucket/update_test.go @@ -451,7 +451,7 @@ func TestUpdate(t *testing.T) { s3clienthandler.WithBackendStore(tc.fields.backendStore), s3clienthandler.WithKubeClient(cl)), autoPauseBucket: tc.fields.autoPauseBucket, - minReplicas: 2, + minReplicas: 1, log: logging.NewNopLogger(), } @@ -765,7 +765,7 @@ func TestUpdateLifecycleConfigSubResource(t *testing.T) { backendStore: tc.fields.backendStore, s3ClientHandler: s3ClientHandler, autoPauseBucket: tc.fields.autoPauseBucket, - minReplicas: 2, + minReplicas: 1, log: logging.NewNopLogger(), subresourceClients: NewSubresourceClients(tc.fields.backendStore, s3ClientHandler, logging.NewNopLogger()), } @@ -1066,7 +1066,7 @@ func TestUpdateVersioningConfigSubResource(t *testing.T) { backendStore: tc.fields.backendStore, s3ClientHandler: s3ClientHandler, autoPauseBucket: tc.fields.autoPauseBucket, - minReplicas: 2, + minReplicas: 1, log: logging.NewNopLogger(), subresourceClients: NewSubresourceClients(tc.fields.backendStore, s3ClientHandler, logging.NewNopLogger()), }