From bd77f69300f96bd473f6b8aad642067794ea6fbc Mon Sep 17 00:00:00 2001 From: Unnati Aggarwal <113070152+unnatiagg@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:27:49 -0400 Subject: [PATCH] [feat] add a v1alpha2 version of LinodeObjectStorageBucket (#427) * v1alpha2 for linodeObjectStorageBucket * converting cluster ID into regionID * fixing lint changes * Using conversion-gen to convert Linode OBJ api conversions * adding lint suggestions * handling region conversion from v1alpha2 -> v1alpha1 * fixing lint issue * changing hostname condition statement --------- Co-authored-by: Unnati Aggarwal Co-authored-by: Unnati Aggarwal --- PROJECT | 12 ++ api/v1alpha1/conversion.go | 29 ++++ .../linodeobjectstoragebucket_conversion.go | 65 +++++++ ...nodeobjectstoragebucket_conversion_test.go | 141 +++++++++++++++ api/v1alpha1/zz_generated.conversion.go | 160 +++++++++++++++++ .../linodeobjectstoragebucket_conversion.go | 20 +++ .../linodeobjectstoragebucket_types.go | 134 +++++++++++++++ .../linodeobjectstoragebucket_webhook.go | 112 ++++++++++++ .../linodeobjectstoragebucket_webhook_test.go | 90 ++++++++++ api/v1alpha2/webhook_helpers.go | 24 +++ api/v1alpha2/zz_generated.deepcopy.go | 135 +++++++++++++++ cloud/scope/object_storage_bucket.go | 8 +- cloud/scope/object_storage_bucket_test.go | 88 +++++----- cloud/services/object_storage_buckets.go | 8 +- cloud/services/object_storage_buckets_test.go | 76 ++++---- cmd/main.go | 4 + ...r.x-k8s.io_linodeobjectstoragebuckets.yaml | 162 ++++++++++++++++++ config/crd/kustomization.yaml | 2 + ...ontract_in_linodeobjectstoragebuckets.yaml | 2 +- config/rbac/kustomization.yaml | 2 + ...re_v1alpha2_linodeobjectstoragebucket.yaml | 9 + config/samples/kustomization.yaml | 1 + config/webhook/manifests.yaml | 21 +++ .../linodeobjectstoragebucket_controller.go | 15 +- ...nodeobjectstoragebucket_controller_test.go | 32 ++-- docs/src/topics/backups.md | 10 +- docs/src/topics/multi-tenancy.md | 2 +- .../invalid-linodeobjectstoragebucket.yaml | 4 +- .../minimal-linodemachine/chainsaw-test.yaml | 2 +- .../create-linodemachine.yaml | 4 +- .../vpc-integration/assert-vpc.yaml | 2 +- .../vpc-integration/chainsaw-test.yaml | 6 +- .../check-vpc-lm-deletion.yaml | 2 +- .../vpc-integration/create-cluster-vpc.yaml | 2 +- .../vpc-integration/create-linodemachine.yaml | 4 +- .../assert-keygen.yaml | 4 +- .../assert-obj-and-secret.yaml | 4 +- .../chainsaw-test.yaml | 6 +- .../check-obj-and-key-deletion.yaml | 2 +- .../create-linodeobjectstoragebucket.yaml | 4 +- .../patch-obj.yaml | 4 +- .../etcd-backup-restore/linode-obj.yaml | 4 +- 42 files changed, 1271 insertions(+), 147 deletions(-) create mode 100644 api/v1alpha1/linodeobjectstoragebucket_conversion.go create mode 100644 api/v1alpha1/linodeobjectstoragebucket_conversion_test.go create mode 100644 api/v1alpha2/linodeobjectstoragebucket_conversion.go create mode 100644 api/v1alpha2/linodeobjectstoragebucket_types.go create mode 100644 api/v1alpha2/linodeobjectstoragebucket_webhook.go create mode 100644 api/v1alpha2/linodeobjectstoragebucket_webhook_test.go create mode 100644 config/samples/infrastructure_v1alpha2_linodeobjectstoragebucket.yaml diff --git a/PROJECT b/PROJECT index 06dcb928..6b1308ab 100644 --- a/PROJECT +++ b/PROJECT @@ -141,4 +141,16 @@ resources: webhooks: conversion: true webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + domain: cluster.x-k8s.io + group: infrastructure + kind: LinodeObjectStorageBucket + path: github.com/linode/cluster-api-provider-linode/api/v1alpha2 + version: v1alpha2 + webhooks: + conversion: true + validation: true + webhookVersion: v1 version: "3" diff --git a/api/v1alpha1/conversion.go b/api/v1alpha1/conversion.go index d64d33d7..35f0a593 100644 --- a/api/v1alpha1/conversion.go +++ b/api/v1alpha1/conversion.go @@ -17,6 +17,8 @@ limitations under the License. package v1alpha1 import ( + "strings" + "k8s.io/apimachinery/pkg/conversion" infrastructurev1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" @@ -43,3 +45,30 @@ func Convert_v1alpha2_LinodeMachineSpec_To_v1alpha1_LinodeMachineSpec(in *infras // Ok to use the auto-generated conversion function, it simply drops the PlacementGroupRef, and copies everything else return autoConvert_v1alpha2_LinodeMachineSpec_To_v1alpha1_LinodeMachineSpec(in, out, s) } + +func Convert_v1alpha1_LinodeObjectStorageBucketSpec_To_v1alpha2_LinodeObjectStorageBucketSpec(in *LinodeObjectStorageBucketSpec, out *infrastructurev1alpha2.LinodeObjectStorageBucketSpec, s conversion.Scope) error { + // WARNING: in.Cluster requires manual conversion: does not exist in peer-type + out.Region = in.Cluster + out.CredentialsRef = in.CredentialsRef + out.KeyGeneration = in.KeyGeneration + out.SecretType = in.SecretType + return nil +} + +func Convert_v1alpha2_LinodeObjectStorageBucketSpec_To_v1alpha1_LinodeObjectStorageBucketSpec(in *infrastructurev1alpha2.LinodeObjectStorageBucketSpec, out *LinodeObjectStorageBucketSpec, s conversion.Scope) error { + // WARNING: in.Region requires manual conversion: does not exist in peer-type + out.Cluster = in.Region + out.CredentialsRef = in.CredentialsRef + out.KeyGeneration = in.KeyGeneration + out.SecretType = in.SecretType + return nil +} + +func Convert_v1alpha2_LinodeObjectStorageBucket_To_v1alpha1_LinodeObjectStorageBucket(in *infrastructurev1alpha2.LinodeObjectStorageBucket, out *LinodeObjectStorageBucket, scope conversion.Scope) error { + if in.Status.Hostname != nil && *in.Status.Hostname != "" { + in.Spec.Region = strings.Split(*in.Status.Hostname, ".")[1] + } else { + in.Spec.Region += "-1" + } + return autoConvert_v1alpha2_LinodeObjectStorageBucket_To_v1alpha1_LinodeObjectStorageBucket(in, out, scope) +} diff --git a/api/v1alpha1/linodeobjectstoragebucket_conversion.go b/api/v1alpha1/linodeobjectstoragebucket_conversion.go new file mode 100644 index 00000000..49d6f5a4 --- /dev/null +++ b/api/v1alpha1/linodeobjectstoragebucket_conversion.go @@ -0,0 +1,65 @@ +/* +Copyright 2023 Akamai Technologies, Inc. + +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 v1alpha1 + +import ( + "errors" + + utilconversion "sigs.k8s.io/cluster-api/util/conversion" + "sigs.k8s.io/controller-runtime/pkg/conversion" + + infrastructurev1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" +) + +// ConvertTo converts this LinodeObjectStorageBucket to the Hub version (v1alpha2). +func (src *LinodeObjectStorageBucket) ConvertTo(dstRaw conversion.Hub) error { + dst, ok := dstRaw.(*infrastructurev1alpha2.LinodeObjectStorageBucket) + if !ok { + return errors.New("failed to convert LinodeObjectStorageBucket version from v1alpha1 to v1alpha2") + } + + if err := Convert_v1alpha1_LinodeObjectStorageBucket_To_v1alpha2_LinodeObjectStorageBucket(src, dst, nil); err != nil { + return err + } + + // Manually restore data from annotations + restored := &LinodeObjectStorageBucket{} + if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok { + return err + } + + return nil +} + +// ConvertFrom converts from the Hub version (v1alpha2) to this version. +func (dst *LinodeObjectStorageBucket) ConvertFrom(srcRaw conversion.Hub) error { + src, ok := srcRaw.(*infrastructurev1alpha2.LinodeObjectStorageBucket) + if !ok { + return errors.New("failed to convert LinodeObjectStorageBucket version from v1alpha2 to v1alpha1") + } + + if err := Convert_v1alpha2_LinodeObjectStorageBucket_To_v1alpha1_LinodeObjectStorageBucket(src, dst, nil); err != nil { + return err + } + + // Preserve Hub data on down-conversion. + if err := utilconversion.MarshalData(src, dst); err != nil { + return err + } + + return nil +} diff --git a/api/v1alpha1/linodeobjectstoragebucket_conversion_test.go b/api/v1alpha1/linodeobjectstoragebucket_conversion_test.go new file mode 100644 index 00000000..5863efc2 --- /dev/null +++ b/api/v1alpha1/linodeobjectstoragebucket_conversion_test.go @@ -0,0 +1,141 @@ +/* +Copyright 2023 Akamai Technologies, Inc. + +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 v1alpha1 + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" + "github.com/linode/cluster-api-provider-linode/mock" + + . "github.com/linode/cluster-api-provider-linode/mock/mocktest" +) + +const ( + ConversionDataAnnotation = "cluster.x-k8s.io/conversion-data" +) + +func TestLinodeObjectStorageBucketConvertTo(t *testing.T) { + t.Parallel() + + src := &LinodeObjectStorageBucket{ + ObjectMeta: metav1.ObjectMeta{Name: "test-bucket"}, + Spec: LinodeObjectStorageBucketSpec{ + Cluster: "us-mia-1", + CredentialsRef: &corev1.SecretReference{ + Namespace: "default", + Name: "cred-secret", + }, + KeyGeneration: ptr.To(1), + SecretType: "Opaque", + }, + Status: LinodeObjectStorageBucketStatus{}, + } + expectedDst := &infrav1alpha2.LinodeObjectStorageBucket{ + ObjectMeta: metav1.ObjectMeta{Name: "test-bucket"}, + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "us-mia-1", + CredentialsRef: &corev1.SecretReference{ + Namespace: "default", + Name: "cred-secret", + }, + KeyGeneration: ptr.To(1), + SecretType: "Opaque", + }, + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{}, + } + dst := &infrav1alpha2.LinodeObjectStorageBucket{} + + NewSuite(t, mock.MockLinodeClient{}).Run( + OneOf( + Path( + Call("convert v1alpha1 to v1alpha2", func(ctx context.Context, mck Mock) { + err := src.ConvertTo(dst) + if err != nil { + t.Fatalf("ConvertTo failed: %v", err) + } + }), + Result("conversion succeeded", func(ctx context.Context, mck Mock) { + if diff := cmp.Diff(expectedDst, dst); diff != "" { + t.Errorf("ConvertTo() mismatch (-expected +got):\n%s", diff) + } + }), + ), + ), + ) +} + +func TestLinodeObjectStorageBucketFrom(t *testing.T) { + t.Parallel() + + src := &infrav1alpha2.LinodeObjectStorageBucket{ + ObjectMeta: metav1.ObjectMeta{Name: "test-bucket"}, + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "us-mia", + CredentialsRef: &corev1.SecretReference{ + Namespace: "default", + Name: "cred-secret", + }, + KeyGeneration: ptr.To(1), + SecretType: "Opaque", + }, + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{}, + } + expectedDst := &LinodeObjectStorageBucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-bucket", + Annotations: map[string]string{ + ConversionDataAnnotation: `{"spec":{"credentialsRef":{"name":"cred-secret","namespace":"default"},"keyGeneration":1,"region":"us-mia-1","secretType":"Opaque"},"status":{"ready":false}}`, + }, + }, + Spec: LinodeObjectStorageBucketSpec{ + Cluster: "us-mia-1", + CredentialsRef: &corev1.SecretReference{ + Namespace: "default", + Name: "cred-secret", + }, + KeyGeneration: ptr.To(1), + SecretType: "Opaque", + }, + Status: LinodeObjectStorageBucketStatus{}, + } + dst := &LinodeObjectStorageBucket{} + + NewSuite(t, mock.MockLinodeClient{}).Run( + OneOf( + Path( + Call("convert v1alpha2 to v1alpha1", func(ctx context.Context, mck Mock) { + err := dst.ConvertFrom(src) + if err != nil { + t.Fatalf("ConvertFrom failed: %v", err) + } + }), + Result("conversion succeeded", func(ctx context.Context, mck Mock) { + if diff := cmp.Diff(expectedDst, dst); diff != "" { + t.Errorf("ConvertFrom() mismatch (-expected +got):\n%s", diff) + } + }), + ), + ), + ) +} diff --git a/api/v1alpha1/zz_generated.conversion.go b/api/v1alpha1/zz_generated.conversion.go index 80549ec0..309baa6d 100644 --- a/api/v1alpha1/zz_generated.conversion.go +++ b/api/v1alpha1/zz_generated.conversion.go @@ -26,6 +26,7 @@ import ( v1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" linodego "github.com/linode/linodego" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" v1beta1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -224,6 +225,31 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*LinodeObjectStorageBucket)(nil), (*v1alpha2.LinodeObjectStorageBucket)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_LinodeObjectStorageBucket_To_v1alpha2_LinodeObjectStorageBucket(a.(*LinodeObjectStorageBucket), b.(*v1alpha2.LinodeObjectStorageBucket), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*LinodeObjectStorageBucketList)(nil), (*v1alpha2.LinodeObjectStorageBucketList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_LinodeObjectStorageBucketList_To_v1alpha2_LinodeObjectStorageBucketList(a.(*LinodeObjectStorageBucketList), b.(*v1alpha2.LinodeObjectStorageBucketList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.LinodeObjectStorageBucketList)(nil), (*LinodeObjectStorageBucketList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_LinodeObjectStorageBucketList_To_v1alpha1_LinodeObjectStorageBucketList(a.(*v1alpha2.LinodeObjectStorageBucketList), b.(*LinodeObjectStorageBucketList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*LinodeObjectStorageBucketStatus)(nil), (*v1alpha2.LinodeObjectStorageBucketStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_LinodeObjectStorageBucketStatus_To_v1alpha2_LinodeObjectStorageBucketStatus(a.(*LinodeObjectStorageBucketStatus), b.(*v1alpha2.LinodeObjectStorageBucketStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1alpha2.LinodeObjectStorageBucketStatus)(nil), (*LinodeObjectStorageBucketStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_LinodeObjectStorageBucketStatus_To_v1alpha1_LinodeObjectStorageBucketStatus(a.(*v1alpha2.LinodeObjectStorageBucketStatus), b.(*LinodeObjectStorageBucketStatus), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*LinodeVPC)(nil), (*v1alpha2.LinodeVPC)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_LinodeVPC_To_v1alpha2_LinodeVPC(a.(*LinodeVPC), b.(*v1alpha2.LinodeVPC), scope) }); err != nil { @@ -284,6 +310,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*LinodeObjectStorageBucketSpec)(nil), (*v1alpha2.LinodeObjectStorageBucketSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_LinodeObjectStorageBucketSpec_To_v1alpha2_LinodeObjectStorageBucketSpec(a.(*LinodeObjectStorageBucketSpec), b.(*v1alpha2.LinodeObjectStorageBucketSpec), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*NetworkSpec)(nil), (*v1alpha2.NetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_NetworkSpec_To_v1alpha2_NetworkSpec(a.(*NetworkSpec), b.(*v1alpha2.NetworkSpec), scope) }); err != nil { @@ -294,6 +325,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1alpha2.LinodeObjectStorageBucketSpec)(nil), (*LinodeObjectStorageBucketSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_LinodeObjectStorageBucketSpec_To_v1alpha1_LinodeObjectStorageBucketSpec(a.(*v1alpha2.LinodeObjectStorageBucketSpec), b.(*LinodeObjectStorageBucketSpec), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*v1alpha2.LinodeObjectStorageBucket)(nil), (*LinodeObjectStorageBucket)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_LinodeObjectStorageBucket_To_v1alpha1_LinodeObjectStorageBucket(a.(*v1alpha2.LinodeObjectStorageBucket), b.(*LinodeObjectStorageBucket), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1alpha2.NetworkSpec)(nil), (*NetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha2_NetworkSpec_To_v1alpha1_NetworkSpec(a.(*v1alpha2.NetworkSpec), b.(*NetworkSpec), scope) }); err != nil { @@ -900,6 +941,125 @@ func Convert_v1alpha2_LinodeMachineTemplateSpec_To_v1alpha1_LinodeMachineTemplat return autoConvert_v1alpha2_LinodeMachineTemplateSpec_To_v1alpha1_LinodeMachineTemplateSpec(in, out, s) } +func autoConvert_v1alpha1_LinodeObjectStorageBucket_To_v1alpha2_LinodeObjectStorageBucket(in *LinodeObjectStorageBucket, out *v1alpha2.LinodeObjectStorageBucket, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_LinodeObjectStorageBucketSpec_To_v1alpha2_LinodeObjectStorageBucketSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha1_LinodeObjectStorageBucketStatus_To_v1alpha2_LinodeObjectStorageBucketStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_LinodeObjectStorageBucket_To_v1alpha2_LinodeObjectStorageBucket is an autogenerated conversion function. +func Convert_v1alpha1_LinodeObjectStorageBucket_To_v1alpha2_LinodeObjectStorageBucket(in *LinodeObjectStorageBucket, out *v1alpha2.LinodeObjectStorageBucket, s conversion.Scope) error { + return autoConvert_v1alpha1_LinodeObjectStorageBucket_To_v1alpha2_LinodeObjectStorageBucket(in, out, s) +} + +func autoConvert_v1alpha2_LinodeObjectStorageBucket_To_v1alpha1_LinodeObjectStorageBucket(in *v1alpha2.LinodeObjectStorageBucket, out *LinodeObjectStorageBucket, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha2_LinodeObjectStorageBucketSpec_To_v1alpha1_LinodeObjectStorageBucketSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1alpha2_LinodeObjectStorageBucketStatus_To_v1alpha1_LinodeObjectStorageBucketStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_LinodeObjectStorageBucketList_To_v1alpha2_LinodeObjectStorageBucketList(in *LinodeObjectStorageBucketList, out *v1alpha2.LinodeObjectStorageBucketList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1alpha2.LinodeObjectStorageBucket, len(*in)) + for i := range *in { + if err := Convert_v1alpha1_LinodeObjectStorageBucket_To_v1alpha2_LinodeObjectStorageBucket(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha1_LinodeObjectStorageBucketList_To_v1alpha2_LinodeObjectStorageBucketList is an autogenerated conversion function. +func Convert_v1alpha1_LinodeObjectStorageBucketList_To_v1alpha2_LinodeObjectStorageBucketList(in *LinodeObjectStorageBucketList, out *v1alpha2.LinodeObjectStorageBucketList, s conversion.Scope) error { + return autoConvert_v1alpha1_LinodeObjectStorageBucketList_To_v1alpha2_LinodeObjectStorageBucketList(in, out, s) +} + +func autoConvert_v1alpha2_LinodeObjectStorageBucketList_To_v1alpha1_LinodeObjectStorageBucketList(in *v1alpha2.LinodeObjectStorageBucketList, out *LinodeObjectStorageBucketList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]LinodeObjectStorageBucket, len(*in)) + for i := range *in { + if err := Convert_v1alpha2_LinodeObjectStorageBucket_To_v1alpha1_LinodeObjectStorageBucket(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha2_LinodeObjectStorageBucketList_To_v1alpha1_LinodeObjectStorageBucketList is an autogenerated conversion function. +func Convert_v1alpha2_LinodeObjectStorageBucketList_To_v1alpha1_LinodeObjectStorageBucketList(in *v1alpha2.LinodeObjectStorageBucketList, out *LinodeObjectStorageBucketList, s conversion.Scope) error { + return autoConvert_v1alpha2_LinodeObjectStorageBucketList_To_v1alpha1_LinodeObjectStorageBucketList(in, out, s) +} + +func autoConvert_v1alpha1_LinodeObjectStorageBucketSpec_To_v1alpha2_LinodeObjectStorageBucketSpec(in *LinodeObjectStorageBucketSpec, out *v1alpha2.LinodeObjectStorageBucketSpec, s conversion.Scope) error { + // WARNING: in.Cluster requires manual conversion: does not exist in peer-type + out.CredentialsRef = (*v1.SecretReference)(unsafe.Pointer(in.CredentialsRef)) + out.KeyGeneration = (*int)(unsafe.Pointer(in.KeyGeneration)) + out.SecretType = in.SecretType + return nil +} + +func autoConvert_v1alpha2_LinodeObjectStorageBucketSpec_To_v1alpha1_LinodeObjectStorageBucketSpec(in *v1alpha2.LinodeObjectStorageBucketSpec, out *LinodeObjectStorageBucketSpec, s conversion.Scope) error { + // WARNING: in.Region requires manual conversion: does not exist in peer-type + out.CredentialsRef = (*v1.SecretReference)(unsafe.Pointer(in.CredentialsRef)) + out.KeyGeneration = (*int)(unsafe.Pointer(in.KeyGeneration)) + out.SecretType = in.SecretType + return nil +} + +func autoConvert_v1alpha1_LinodeObjectStorageBucketStatus_To_v1alpha2_LinodeObjectStorageBucketStatus(in *LinodeObjectStorageBucketStatus, out *v1alpha2.LinodeObjectStorageBucketStatus, s conversion.Scope) error { + out.Ready = in.Ready + out.FailureMessage = (*string)(unsafe.Pointer(in.FailureMessage)) + out.Conditions = *(*v1beta1.Conditions)(unsafe.Pointer(&in.Conditions)) + out.Hostname = (*string)(unsafe.Pointer(in.Hostname)) + out.CreationTime = (*metav1.Time)(unsafe.Pointer(in.CreationTime)) + out.LastKeyGeneration = (*int)(unsafe.Pointer(in.LastKeyGeneration)) + out.KeySecretName = (*string)(unsafe.Pointer(in.KeySecretName)) + out.AccessKeyRefs = *(*[]int)(unsafe.Pointer(&in.AccessKeyRefs)) + return nil +} + +// Convert_v1alpha1_LinodeObjectStorageBucketStatus_To_v1alpha2_LinodeObjectStorageBucketStatus is an autogenerated conversion function. +func Convert_v1alpha1_LinodeObjectStorageBucketStatus_To_v1alpha2_LinodeObjectStorageBucketStatus(in *LinodeObjectStorageBucketStatus, out *v1alpha2.LinodeObjectStorageBucketStatus, s conversion.Scope) error { + return autoConvert_v1alpha1_LinodeObjectStorageBucketStatus_To_v1alpha2_LinodeObjectStorageBucketStatus(in, out, s) +} + +func autoConvert_v1alpha2_LinodeObjectStorageBucketStatus_To_v1alpha1_LinodeObjectStorageBucketStatus(in *v1alpha2.LinodeObjectStorageBucketStatus, out *LinodeObjectStorageBucketStatus, s conversion.Scope) error { + out.Ready = in.Ready + out.FailureMessage = (*string)(unsafe.Pointer(in.FailureMessage)) + out.Conditions = *(*v1beta1.Conditions)(unsafe.Pointer(&in.Conditions)) + out.Hostname = (*string)(unsafe.Pointer(in.Hostname)) + out.CreationTime = (*metav1.Time)(unsafe.Pointer(in.CreationTime)) + out.LastKeyGeneration = (*int)(unsafe.Pointer(in.LastKeyGeneration)) + out.KeySecretName = (*string)(unsafe.Pointer(in.KeySecretName)) + out.AccessKeyRefs = *(*[]int)(unsafe.Pointer(&in.AccessKeyRefs)) + return nil +} + +// Convert_v1alpha2_LinodeObjectStorageBucketStatus_To_v1alpha1_LinodeObjectStorageBucketStatus is an autogenerated conversion function. +func Convert_v1alpha2_LinodeObjectStorageBucketStatus_To_v1alpha1_LinodeObjectStorageBucketStatus(in *v1alpha2.LinodeObjectStorageBucketStatus, out *LinodeObjectStorageBucketStatus, s conversion.Scope) error { + return autoConvert_v1alpha2_LinodeObjectStorageBucketStatus_To_v1alpha1_LinodeObjectStorageBucketStatus(in, out, s) +} + func autoConvert_v1alpha1_LinodeVPC_To_v1alpha2_LinodeVPC(in *LinodeVPC, out *v1alpha2.LinodeVPC, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1alpha1_LinodeVPCSpec_To_v1alpha2_LinodeVPCSpec(&in.Spec, &out.Spec, s); err != nil { diff --git a/api/v1alpha2/linodeobjectstoragebucket_conversion.go b/api/v1alpha2/linodeobjectstoragebucket_conversion.go new file mode 100644 index 00000000..a911f388 --- /dev/null +++ b/api/v1alpha2/linodeobjectstoragebucket_conversion.go @@ -0,0 +1,20 @@ +/* +Copyright 2023 Akamai Technologies, Inc. + +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 v1alpha2 + +// Hub marks LinodeObjectStorageBucketS as a conversion hub. +func (*LinodeObjectStorageBucket) Hub() {} diff --git a/api/v1alpha2/linodeobjectstoragebucket_types.go b/api/v1alpha2/linodeobjectstoragebucket_types.go new file mode 100644 index 00000000..91e59957 --- /dev/null +++ b/api/v1alpha2/linodeobjectstoragebucket_types.go @@ -0,0 +1,134 @@ +/* +Copyright 2023 Akamai Technologies, Inc. + +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 v1alpha2 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" +) + +const ( + // ObjectStorageBucketFinalizer allows ReconcileLinodeObjectStorageBucket to clean up Linode resources associated + // with LinodeObjectStorageBucket before removing it from the apiserver. + ObjectStorageBucketFinalizer = "linodeobjectstoragebucket.infrastructure.cluster.x-k8s.io" +) + +// LinodeObjectStorageBucketSpec defines the desired state of LinodeObjectStorageBucket +type LinodeObjectStorageBucketSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Region is the ID of the Object Storage region for the bucket. + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + Region string `json:"region"` + + // CredentialsRef is a reference to a Secret that contains the credentials to use for provisioning the bucket. + // If not supplied then the credentials of the controller will be used. + // +optional + CredentialsRef *corev1.SecretReference `json:"credentialsRef"` + + // KeyGeneration may be modified to trigger rotations of access keys created for the bucket. + // +optional + // +kubebuilder:default=0 + KeyGeneration *int `json:"keyGeneration,omitempty"` + + // SecretType sets the type for the bucket-details secret that will be generated by the controller. + // +optional + // +kubebuilder:default=addons.cluster.x-k8s.io/resource-set + SecretType string `json:"secretType,omitempty"` +} + +// LinodeObjectStorageBucketStatus defines the observed state of LinodeObjectStorageBucket +type LinodeObjectStorageBucketStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Ready denotes that the bucket has been provisioned along with access keys. + // +optional + // +kubebuilder:default=false + Ready bool `json:"ready"` + + // FailureMessage will be set in the event that there is a terminal problem + // reconciling the Object Storage Bucket and will contain a verbose string + // suitable for logging and human consumption. + // +optional + FailureMessage *string `json:"failureMessage,omitempty"` + + // Conditions specify the service state of the LinodeObjectStorageBucket. + // +optional + Conditions clusterv1.Conditions `json:"conditions,omitempty"` + + // Hostname is the address assigned to the bucket. + // +optional + Hostname *string `json:"hostname,omitempty"` + + // CreationTime specifies the creation timestamp for the bucket. + // +optional + CreationTime *metav1.Time `json:"creationTime,omitempty"` + + // LastKeyGeneration tracks the last known value of .spec.keyGeneration. + // +optional + LastKeyGeneration *int `json:"lastKeyGeneration,omitempty"` + + // KeySecretName specifies the name of the Secret containing access keys for the bucket. + // +optional + KeySecretName *string `json:"keySecretName,omitempty"` + + // AccessKeyRefs stores IDs for Object Storage keys provisioned along with the bucket. + // +optional + AccessKeyRefs []int `json:"accessKeyRefs,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +kubebuilder:resource:path=linodeobjectstoragebuckets,scope=Namespaced,categories=cluster-api,shortName=lobj +// +kubebuilder:subresource:status +// +kubebuilder:metadata:labels="clusterctl.cluster.x-k8s.io/move-hierarchy=true" +// +kubebuilder:printcolumn:name="Label",type="string",JSONPath=".spec.label",description="The name of the bucket" +// +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".spec.region",description="The ID of the Object Storage region for the bucket" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready",description="Bucket and keys have been provisioned" + +// LinodeObjectStorageBucket is the Schema for the linodeobjectstoragebuckets API +type LinodeObjectStorageBucket struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec LinodeObjectStorageBucketSpec `json:"spec,omitempty"` + Status LinodeObjectStorageBucketStatus `json:"status,omitempty"` +} + +func (b *LinodeObjectStorageBucket) GetConditions() clusterv1.Conditions { + return b.Status.Conditions +} + +func (b *LinodeObjectStorageBucket) SetConditions(conditions clusterv1.Conditions) { + b.Status.Conditions = conditions +} + +// +kubebuilder:object:root=true + +// LinodeObjectStorageBucketList contains a list of LinodeObjectStorageBucket +type LinodeObjectStorageBucketList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []LinodeObjectStorageBucket `json:"items"` +} + +func init() { + SchemeBuilder.Register(&LinodeObjectStorageBucket{}, &LinodeObjectStorageBucketList{}) +} diff --git a/api/v1alpha2/linodeobjectstoragebucket_webhook.go b/api/v1alpha2/linodeobjectstoragebucket_webhook.go new file mode 100644 index 00000000..5305dc54 --- /dev/null +++ b/api/v1alpha2/linodeobjectstoragebucket_webhook.go @@ -0,0 +1,112 @@ +/* +Copyright 2023 Akamai Technologies, Inc. + +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 v1alpha2 + +import ( + "context" + "slices" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + . "github.com/linode/cluster-api-provider-linode/clients" +) + +var ( + // The capability string indicating a region supports Object Storage: [Object Storage Availability] + // + // [Object Storage Availability]: https://www.linode.com/docs/products/storage/object-storage/#availability + LinodeObjectStorageCapability = "Object Storage" +) + +// log is for logging in this package. +var linodeobjectstoragebucketlog = logf.Log.WithName("linodeobjectstoragebucket-resource") + +// SetupWebhookWithManager will setup the manager to manage the webhooks +func (r *LinodeObjectStorageBucket) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable update and deletion validation. +//+kubebuilder:webhook:path=/validate-infrastructure-cluster-x-k8s-io-v1alpha2-linodeobjectstoragebucket,mutating=false,failurePolicy=fail,sideEffects=None,groups=infrastructure.cluster.x-k8s.io,resources=linodeobjectstoragebuckets,verbs=create,versions=v1alpha2,name=validation.linodeobjectstoragebucket.infrastructure.cluster.x-k8s.io,admissionReviewVersions=v1;v1alpha1;v1alpha2 + +var _ webhook.Validator = &LinodeObjectStorageBucket{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *LinodeObjectStorageBucket) ValidateCreate() (admission.Warnings, error) { + linodeobjectstoragebucketlog.Info("validate create", "name", r.Name) + + ctx, cancel := context.WithTimeout(context.Background(), defaultWebhookTimeout) + defer cancel() + + return nil, r.validateLinodeObjectStorageBucket(ctx, &defaultLinodeClient) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *LinodeObjectStorageBucket) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + linodeobjectstoragebucketlog.Info("validate update", "name", r.Name) + + // TODO(user): fill in your validation logic upon object update. + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *LinodeObjectStorageBucket) ValidateDelete() (admission.Warnings, error) { + linodeobjectstoragebucketlog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +func (r *LinodeObjectStorageBucket) validateLinodeObjectStorageBucket(ctx context.Context, client LinodeClient) error { + // TODO: instrument with tracing, might need refactor to preserve readibility + var errs field.ErrorList + + if err := r.validateLinodeObjectStorageBucketSpec(ctx, client); err != nil { + errs = slices.Concat(errs, err) + } + + if len(errs) == 0 { + return nil + } + return apierrors.NewInvalid( + schema.GroupKind{Group: "infrastructure.cluster.x-k8s.io", Kind: "LinodeObjectStorageBucket"}, + r.Name, errs) +} + +func (r *LinodeObjectStorageBucket) validateLinodeObjectStorageBucketSpec(ctx context.Context, client LinodeClient) field.ErrorList { + // TODO: instrument with tracing, might need refactor to preserve readibility + // Handle this + var errs field.ErrorList + + if err := validateObjectStorageRegion(ctx, client, r.Spec.Region, field.NewPath("spec").Child("cluster")); err != nil { + errs = append(errs, err) + } + + if len(errs) == 0 { + return nil + } + return errs +} diff --git a/api/v1alpha2/linodeobjectstoragebucket_webhook_test.go b/api/v1alpha2/linodeobjectstoragebucket_webhook_test.go new file mode 100644 index 00000000..dc47ddd7 --- /dev/null +++ b/api/v1alpha2/linodeobjectstoragebucket_webhook_test.go @@ -0,0 +1,90 @@ +/* +Copyright 2023 Akamai Technologies, Inc. + +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 v1alpha2 + +import ( + "context" + "slices" + "testing" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/linode/cluster-api-provider-linode/mock" + + . "github.com/linode/cluster-api-provider-linode/mock/mocktest" +) + +func TestValidateLinodeObjectStorageBucket(t *testing.T) { + t.Parallel() + + var ( + bucket = LinodeObjectStorageBucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "example", + }, + Spec: LinodeObjectStorageBucketSpec{ + Region: "example", + }, + } + region = linodego.Region{ID: "test"} + capabilities = []string{LinodeObjectStorageCapability} + capabilities_zero = []string{} + ) + + NewSuite(t, mock.MockLinodeClient{}).Run( + OneOf( + Path( + Call("valid", func(ctx context.Context, mck Mock) { + region := region + region.Capabilities = slices.Clone(capabilities) + mck.LinodeClient.EXPECT().GetRegion(gomock.Any(), gomock.Any()).Return(®ion, nil).AnyTimes() + }), + Result("success", func(ctx context.Context, mck Mock) { + assert.NoError(t, bucket.validateLinodeObjectStorageBucket(ctx, mck.LinodeClient)) + }), + ), + ), + OneOf( + Path( + Call("invalid cluster format", func(ctx context.Context, mck Mock) { + region := region + region.Capabilities = slices.Clone(capabilities) + mck.LinodeClient.EXPECT().GetRegion(gomock.Any(), gomock.Any()).Return(®ion, nil).AnyTimes() + }), + Result("error", func(ctx context.Context, mck Mock) { + bucket := bucket + bucket.Spec.Region = "123invalid" + assert.Error(t, bucket.validateLinodeObjectStorageBucket(ctx, mck.LinodeClient)) + }), + ), + Path( + Call("region not supported", func(ctx context.Context, mck Mock) { + region := region + region.Capabilities = slices.Clone(capabilities_zero) + mck.LinodeClient.EXPECT().GetRegion(gomock.Any(), gomock.Any()).Return(®ion, nil).AnyTimes() + }), + Result("error", func(ctx context.Context, mck Mock) { + assert.Error(t, bucket.validateLinodeObjectStorageBucket(ctx, mck.LinodeClient)) + }), + ), + ), + ) +} diff --git a/api/v1alpha2/webhook_helpers.go b/api/v1alpha2/webhook_helpers.go index c07957e5..ffb1f431 100644 --- a/api/v1alpha2/webhook_helpers.go +++ b/api/v1alpha2/webhook_helpers.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "net/http" + "regexp" "slices" "time" @@ -71,3 +72,26 @@ func validateLinodeType(ctx context.Context, client LinodeClient, id string, pat return plan, nil } + +// validateObjectStorageRegion validates an Object Storage deployment's region ID via the following rules: +// - The Region ID is in the form: REGION_ID. +// - The region has Object Storage support. +// +// NOTE: This implementation intended to bypass the authentication requirement for the [Clusters List] and [Cluster +// View] endpoints in the Linode API, thereby reusing a [github.com/linode/linodego.Client] (and its caching if enabled) +// across many admission requests. +// +// [Clusters List]: https://www.linode.com/docs/api/object-storage/#clusters-list +// [Cluster View]: https://www.linode.com/docs/api/object-storage/#cluster-view +func validateObjectStorageRegion(ctx context.Context, client LinodeClient, id string, path *field.Path) *field.Error { + // TODO: instrument with tracing, might need refactor to preserve readibility + + cexp := regexp.MustCompile("^(([[:lower:]]+-)*[[:lower:]]+)$") + cexp1 := regexp.MustCompile(`^(([[:lower:]]+-)*[[:lower:]]+)-\d+$`) + if !cexp.MatchString(id) && !cexp1.MatchString(id) { + return field.Invalid(path, id, "must be in form: region_id or region_id-ordinal") + } + + region := cexp.FindStringSubmatch(id)[1] + return validateRegion(ctx, client, region, path, LinodeObjectStorageCapability) +} diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index 12b9f104..fa9e821b 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -611,6 +611,141 @@ func (in *LinodeNBPortConfig) DeepCopy() *LinodeNBPortConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LinodeObjectStorageBucket) DeepCopyInto(out *LinodeObjectStorageBucket) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LinodeObjectStorageBucket. +func (in *LinodeObjectStorageBucket) DeepCopy() *LinodeObjectStorageBucket { + if in == nil { + return nil + } + out := new(LinodeObjectStorageBucket) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *LinodeObjectStorageBucket) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LinodeObjectStorageBucketList) DeepCopyInto(out *LinodeObjectStorageBucketList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]LinodeObjectStorageBucket, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LinodeObjectStorageBucketList. +func (in *LinodeObjectStorageBucketList) DeepCopy() *LinodeObjectStorageBucketList { + if in == nil { + return nil + } + out := new(LinodeObjectStorageBucketList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *LinodeObjectStorageBucketList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LinodeObjectStorageBucketSpec) DeepCopyInto(out *LinodeObjectStorageBucketSpec) { + *out = *in + if in.CredentialsRef != nil { + in, out := &in.CredentialsRef, &out.CredentialsRef + *out = new(v1.SecretReference) + **out = **in + } + if in.KeyGeneration != nil { + in, out := &in.KeyGeneration, &out.KeyGeneration + *out = new(int) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LinodeObjectStorageBucketSpec. +func (in *LinodeObjectStorageBucketSpec) DeepCopy() *LinodeObjectStorageBucketSpec { + if in == nil { + return nil + } + out := new(LinodeObjectStorageBucketSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LinodeObjectStorageBucketStatus) DeepCopyInto(out *LinodeObjectStorageBucketStatus) { + *out = *in + if in.FailureMessage != nil { + in, out := &in.FailureMessage, &out.FailureMessage + *out = new(string) + **out = **in + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(v1beta1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(string) + **out = **in + } + if in.CreationTime != nil { + in, out := &in.CreationTime, &out.CreationTime + *out = (*in).DeepCopy() + } + if in.LastKeyGeneration != nil { + in, out := &in.LastKeyGeneration, &out.LastKeyGeneration + *out = new(int) + **out = **in + } + if in.KeySecretName != nil { + in, out := &in.KeySecretName, &out.KeySecretName + *out = new(string) + **out = **in + } + if in.AccessKeyRefs != nil { + in, out := &in.AccessKeyRefs, &out.AccessKeyRefs + *out = make([]int, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LinodeObjectStorageBucketStatus. +func (in *LinodeObjectStorageBucketStatus) DeepCopy() *LinodeObjectStorageBucketStatus { + if in == nil { + return nil + } + out := new(LinodeObjectStorageBucketStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LinodePlacementGroup) DeepCopyInto(out *LinodePlacementGroup) { *out = *in diff --git a/cloud/scope/object_storage_bucket.go b/cloud/scope/object_storage_bucket.go index d915e77f..3ace4818 100644 --- a/cloud/scope/object_storage_bucket.go +++ b/cloud/scope/object_storage_bucket.go @@ -15,7 +15,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - infrav1alpha1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1" + infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" . "github.com/linode/cluster-api-provider-linode/clients" ) @@ -36,13 +36,13 @@ stringData: type ObjectStorageBucketScopeParams struct { Client K8sClient - Bucket *infrav1alpha1.LinodeObjectStorageBucket + Bucket *infrav1alpha2.LinodeObjectStorageBucket Logger *logr.Logger } type ObjectStorageBucketScope struct { Client K8sClient - Bucket *infrav1alpha1.LinodeObjectStorageBucket + Bucket *infrav1alpha2.LinodeObjectStorageBucket Logger logr.Logger LinodeClient LinodeClient PatchHelper *patch.Helper @@ -111,7 +111,7 @@ func (s *ObjectStorageBucketScope) Close(ctx context.Context) error { // AddFinalizer adds a finalizer if not present and immediately patches the // object to avoid any race conditions. func (s *ObjectStorageBucketScope) AddFinalizer(ctx context.Context) error { - if controllerutil.AddFinalizer(s.Bucket, infrav1alpha1.ObjectStorageBucketFinalizer) { + if controllerutil.AddFinalizer(s.Bucket, infrav1alpha2.ObjectStorageBucketFinalizer) { return s.Close(ctx) } diff --git a/cloud/scope/object_storage_bucket_test.go b/cloud/scope/object_storage_bucket_test.go index ace73ec8..e2ecb4e4 100644 --- a/cloud/scope/object_storage_bucket_test.go +++ b/cloud/scope/object_storage_bucket_test.go @@ -21,7 +21,7 @@ import ( "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/client" - infrav1alpha1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1" + infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/mock" . "github.com/linode/cluster-api-provider-linode/clients" @@ -38,7 +38,7 @@ func TestValidateObjectStorageBucketScopeParams(t *testing.T) { { name: "Success - Valid ObjectStorageBucketScopeParams", params: ObjectStorageBucketScopeParams{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{}, + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{}, Logger: &logr.Logger{}, }, expectedErr: nil, @@ -46,7 +46,7 @@ func TestValidateObjectStorageBucketScopeParams(t *testing.T) { { name: "Failure - Invalid ObjectStorageBucketScopeParams. Logger is nil", params: ObjectStorageBucketScopeParams{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{}, + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{}, Logger: nil, }, expectedErr: fmt.Errorf("logger is required when creating an ObjectStorageBucketScope"), @@ -93,7 +93,7 @@ func TestNewObjectStorageBucketScope(t *testing.T) { apiKey: "apikey", params: ObjectStorageBucketScopeParams{ Client: nil, - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{}, + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{}, Logger: &logr.Logger{}, }, }, @@ -101,7 +101,7 @@ func TestNewObjectStorageBucketScope(t *testing.T) { expects: func(k8s *mock.MockK8sClient) { k8s.EXPECT().Scheme().DoAndReturn(func() *runtime.Scheme { s := runtime.NewScheme() - infrav1alpha1.AddToScheme(s) + infrav1alpha2.AddToScheme(s) return s }) }, @@ -112,8 +112,8 @@ func TestNewObjectStorageBucketScope(t *testing.T) { apiKey: "apikey", params: ObjectStorageBucketScopeParams{ Client: nil, - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ CredentialsRef: &corev1.SecretReference{ Name: "example", Namespace: "test", @@ -127,7 +127,7 @@ func TestNewObjectStorageBucketScope(t *testing.T) { expects: func(k8s *mock.MockK8sClient) { k8s.EXPECT().Scheme().DoAndReturn(func() *runtime.Scheme { s := runtime.NewScheme() - infrav1alpha1.AddToScheme(s) + infrav1alpha2.AddToScheme(s) return s }) k8s.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, name types.NamespacedName, obj *corev1.Secret, opts ...client.GetOption) error { @@ -156,7 +156,7 @@ func TestNewObjectStorageBucketScope(t *testing.T) { apiKey: "apikey", params: ObjectStorageBucketScopeParams{ Client: nil, - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{}, + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{}, Logger: &logr.Logger{}, }, }, @@ -171,8 +171,8 @@ func TestNewObjectStorageBucketScope(t *testing.T) { apiKey: "test-key", params: ObjectStorageBucketScopeParams{ Client: nil, - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ CredentialsRef: &corev1.SecretReference{ Name: "example", Namespace: "test", @@ -193,7 +193,7 @@ func TestNewObjectStorageBucketScope(t *testing.T) { apiKey: "", params: ObjectStorageBucketScopeParams{ Client: nil, - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{}, + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{}, Logger: &logr.Logger{}, }, }, @@ -230,16 +230,16 @@ func TestObjectStorageBucketScopeMethods(t *testing.T) { t.Parallel() tests := []struct { name string - Bucket *infrav1alpha1.LinodeObjectStorageBucket + Bucket *infrav1alpha2.LinodeObjectStorageBucket expects func(mock *mock.MockK8sClient) }{ { name: "Success - finalizer should be added to the Linode Object Storage Bucket object", - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{}, + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{}, expects: func(mock *mock.MockK8sClient) { mock.EXPECT().Scheme().DoAndReturn(func() *runtime.Scheme { s := runtime.NewScheme() - infrav1alpha1.AddToScheme(s) + infrav1alpha2.AddToScheme(s) return s }).Times(2) mock.EXPECT().Patch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) @@ -247,15 +247,15 @@ func TestObjectStorageBucketScopeMethods(t *testing.T) { }, { name: "Failure - finalizer should not be added to the Bucket object. Function returns nil since it was already present", - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ - Finalizers: []string{infrav1alpha1.ObjectStorageBucketFinalizer}, + Finalizers: []string{infrav1alpha2.ObjectStorageBucketFinalizer}, }, }, expects: func(mock *mock.MockK8sClient) { mock.EXPECT().Scheme().DoAndReturn(func() *runtime.Scheme { s := runtime.NewScheme() - infrav1alpha1.AddToScheme(s) + infrav1alpha2.AddToScheme(s) return s }).Times(1) }, @@ -289,7 +289,7 @@ func TestObjectStorageBucketScopeMethods(t *testing.T) { t.Errorf("ClusterScope.AddFinalizer() error = %v", err) } - if objScope.Bucket.Finalizers[0] != infrav1alpha1.ObjectStorageBucketFinalizer { + if objScope.Bucket.Finalizers[0] != infrav1alpha2.ObjectStorageBucketFinalizer { t.Errorf("Finalizer was not added") } }) @@ -300,19 +300,19 @@ func TestGenerateKeySecret(t *testing.T) { t.Parallel() tests := []struct { name string - Bucket *infrav1alpha1.LinodeObjectStorageBucket + Bucket *infrav1alpha2.LinodeObjectStorageBucket keys [NumAccessKeys]*linodego.ObjectStorageKey expectedErr error expects func(mock *mock.MockK8sClient) }{ { name: "happy path", - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", Namespace: "test-namespace", }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ KeySecretName: ptr.To("test-bucket-bucket-details"), }, }, @@ -349,7 +349,7 @@ func TestGenerateKeySecret(t *testing.T) { expects: func(mock *mock.MockK8sClient) { mock.EXPECT().Scheme().DoAndReturn(func() *runtime.Scheme { s := runtime.NewScheme() - infrav1alpha1.AddToScheme(s) + infrav1alpha2.AddToScheme(s) return s }).Times(1) }, @@ -357,12 +357,12 @@ func TestGenerateKeySecret(t *testing.T) { }, { name: "missing one or more keys", - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", Namespace: "test-namespace", }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ KeySecretName: ptr.To("test-bucket-bucket-details"), }, }, @@ -385,8 +385,8 @@ func TestGenerateKeySecret(t *testing.T) { expectedErr: errors.New("expected two non-nil object storage keys"), }, { - name: "client scheme does not have infrav1alpha1", - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + name: "client scheme does not have infrav1alpha2", + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", Namespace: "test-namespace", @@ -464,16 +464,16 @@ func TestShouldInitKeys(t *testing.T) { tests := []struct { name string want bool - Bucket *infrav1alpha1.LinodeObjectStorageBucket + Bucket *infrav1alpha2.LinodeObjectStorageBucket }{ { name: "should init keys", want: true, - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ KeyGeneration: ptr.To(1), }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ LastKeyGeneration: nil, }, }, @@ -506,16 +506,16 @@ func TestShouldRotateKeys(t *testing.T) { tests := []struct { name string want bool - Bucket *infrav1alpha1.LinodeObjectStorageBucket + Bucket *infrav1alpha2.LinodeObjectStorageBucket }{ { name: "should rotate keys", want: true, - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ KeyGeneration: ptr.To(1), }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ LastKeyGeneration: ptr.To(0), }, }, @@ -547,15 +547,15 @@ func TestShouldRestoreKeySecret(t *testing.T) { t.Parallel() tests := []struct { name string - bucket *infrav1alpha1.LinodeObjectStorageBucket + bucket *infrav1alpha2.LinodeObjectStorageBucket expects func(k8s *mock.MockK8sClient) want bool expectedErr error }{ { name: "status has no secret name", - bucket: &infrav1alpha1.LinodeObjectStorageBucket{ - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + bucket: &infrav1alpha2.LinodeObjectStorageBucket{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ KeySecretName: nil, }, }, @@ -563,12 +563,12 @@ func TestShouldRestoreKeySecret(t *testing.T) { }, { name: "status has secret name and secret exists", - bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "bucket", Namespace: "ns", }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ KeySecretName: ptr.To("secret"), }, }, @@ -581,12 +581,12 @@ func TestShouldRestoreKeySecret(t *testing.T) { }, { name: "status has secret name and secret is missing", - bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "bucket", Namespace: "ns", }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ KeySecretName: ptr.To("secret"), }, }, @@ -599,12 +599,12 @@ func TestShouldRestoreKeySecret(t *testing.T) { }, { name: "non-404 api error", - bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "bucket", Namespace: "ns", }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ KeySecretName: ptr.To("secret"), }, }, diff --git a/cloud/services/object_storage_buckets.go b/cloud/services/object_storage_buckets.go index 38cf8ccd..cb118ef8 100644 --- a/cloud/services/object_storage_buckets.go +++ b/cloud/services/object_storage_buckets.go @@ -16,11 +16,11 @@ import ( func EnsureObjectStorageBucket(ctx context.Context, bScope *scope.ObjectStorageBucketScope) (*linodego.ObjectStorageBucket, error) { bucket, err := bScope.LinodeClient.GetObjectStorageBucket( ctx, - bScope.Bucket.Spec.Cluster, + bScope.Bucket.Spec.Region, bScope.Bucket.Name, ) if util.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { - return nil, fmt.Errorf("failed to get bucket from cluster %s: %w", bScope.Bucket.Spec.Cluster, err) + return nil, fmt.Errorf("failed to get bucket from region %s: %w", bScope.Bucket.Spec.Region, err) } if bucket != nil { bScope.Logger.Info("Bucket exists") @@ -29,7 +29,7 @@ func EnsureObjectStorageBucket(ctx context.Context, bScope *scope.ObjectStorageB } opts := linodego.ObjectStorageBucketCreateOptions{ - Region: bScope.Bucket.Spec.Cluster, + Region: bScope.Bucket.Spec.Region, Label: bScope.Bucket.Name, ACL: linodego.ACLPrivate, } @@ -78,7 +78,7 @@ func createObjectStorageKey(ctx context.Context, bScope *scope.ObjectStorageBuck BucketAccess: &[]linodego.ObjectStorageKeyBucketAccess{ { BucketName: bScope.Bucket.Name, - Region: bScope.Bucket.Spec.Cluster, + Region: bScope.Bucket.Spec.Region, Permissions: permission, }, }, diff --git a/cloud/services/object_storage_buckets_test.go b/cloud/services/object_storage_buckets_test.go index 7e09e726..554c50a8 100644 --- a/cloud/services/object_storage_buckets_test.go +++ b/cloud/services/object_storage_buckets_test.go @@ -15,7 +15,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" - infrav1alpha1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1" + infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/cloud/scope" "github.com/linode/cluster-api-provider-linode/mock" ) @@ -33,12 +33,12 @@ func TestEnsureObjectStorageBucket(t *testing.T) { { name: "Success - Successfully get the OBJ bucket", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ - Cluster: "test-cluster", + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "test-region", }, }, }, @@ -54,29 +54,29 @@ func TestEnsureObjectStorageBucket(t *testing.T) { { name: "Error - Unable to get the OBJ bucket", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ - Cluster: "test-cluster", + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "test-region", }, }, }, expects: func(c *mock.MockLinodeClient) { c.EXPECT().GetObjectStorageBucket(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error in getting object storage bucket")) }, - expectedError: fmt.Errorf("failed to get bucket from cluster"), + expectedError: fmt.Errorf("failed to get bucket from region"), }, { name: "Success - Successfully create the OBJ bucket", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ - Cluster: "test-cluster", + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "test-region", }, }, }, @@ -93,12 +93,12 @@ func TestEnsureObjectStorageBucket(t *testing.T) { { name: "Error - unable to create the OBJ bucket", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ - Cluster: "test-cluster", + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "test-region", }, }, }, @@ -146,15 +146,15 @@ func TestRotateObjectStorageKeysCreation(t *testing.T) { { name: "Creates new access keys", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ - Cluster: "test-cluster", + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "test-region", KeyGeneration: ptr.To(1), }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ LastKeyGeneration: ptr.To(0), AccessKeyRefs: []int{ 11, @@ -188,12 +188,12 @@ func TestRotateObjectStorageKeysCreation(t *testing.T) { { name: "Error creating keys", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "test-bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ - Cluster: "test-cluster", + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "test-region", }, }, }, @@ -233,19 +233,19 @@ func TestRotateObjectStorageKeysRevocation(t *testing.T) { tests := []struct { name string - bucket *infrav1alpha1.LinodeObjectStorageBucket + bucket *infrav1alpha2.LinodeObjectStorageBucket expects func(*mock.MockLinodeClient) }{ { name: "should revoke existing keys", - bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ KeyGeneration: ptr.To(1), }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ LastKeyGeneration: ptr.To(0), AccessKeyRefs: []int{0, 1}, }, @@ -261,39 +261,39 @@ func TestRotateObjectStorageKeysRevocation(t *testing.T) { }, { name: "shouldInitKeys", - bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "bucket", }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ LastKeyGeneration: nil, }, }, }, { name: "not shouldRotateKeys", - bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ KeyGeneration: ptr.To(1), }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ LastKeyGeneration: ptr.To(1), }, }, }, { name: "unable to revoke keys", - bucket: &infrav1alpha1.LinodeObjectStorageBucket{ + bucket: &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "bucket", }, - Spec: infrav1alpha1.LinodeObjectStorageBucketSpec{ + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ KeyGeneration: ptr.To(1), }, - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ LastKeyGeneration: ptr.To(0), AccessKeyRefs: []int{0, 1}, }, @@ -349,8 +349,8 @@ func TestGetObjectStorageKeys(t *testing.T) { { name: "happy path", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ AccessKeyRefs: []int{0, 1}, }, }, @@ -371,15 +371,15 @@ func TestGetObjectStorageKeys(t *testing.T) { { name: "not two key refs in status", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{}, + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{}, }, wantErr: "expected two object storage key IDs in .status.accessKeyRefs", }, { name: "one client error", bScope: &scope.ObjectStorageBucketScope{ - Bucket: &infrav1alpha1.LinodeObjectStorageBucket{ - Status: infrav1alpha1.LinodeObjectStorageBucketStatus{ + Bucket: &infrav1alpha2.LinodeObjectStorageBucket{ + Status: infrav1alpha2.LinodeObjectStorageBucketStatus{ AccessKeyRefs: []int{0, 1}, }, }, diff --git a/cmd/main.go b/cmd/main.go index 55efa284..5828ed28 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -280,6 +280,10 @@ func setupWebhooks(mgr manager.Manager) { setupLog.Error(err, "unable to create webhook", "webhook", "LinodeVPC") os.Exit(1) } + if err = (&infrastructurev1alpha2.LinodeObjectStorageBucket{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "LinodeObjectStorageBucket") + os.Exit(1) + } if err = (&infrastructurev1alpha1.LinodeObjectStorageBucket{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "LinodeObjectStorageBucket") os.Exit(1) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeobjectstoragebuckets.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeobjectstoragebuckets.yaml index 1b2989c1..475cc4ce 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeobjectstoragebuckets.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeobjectstoragebuckets.yaml @@ -179,6 +179,168 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - description: The name of the bucket + jsonPath: .spec.label + name: Label + type: string + - description: The ID of the Object Storage region for the bucket + jsonPath: .spec.region + name: Region + type: string + - description: Bucket and keys have been provisioned + jsonPath: .status.ready + name: Ready + type: string + name: v1alpha2 + schema: + openAPIV3Schema: + description: LinodeObjectStorageBucket is the Schema for the linodeobjectstoragebuckets + API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: LinodeObjectStorageBucketSpec defines the desired state of + LinodeObjectStorageBucket + properties: + credentialsRef: + description: |- + CredentialsRef is a reference to a Secret that contains the credentials to use for provisioning the bucket. + If not supplied then the credentials of the controller will be used. + properties: + name: + description: name is unique within a namespace to reference a + secret resource. + type: string + namespace: + description: namespace defines the space within which the secret + name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + keyGeneration: + default: 0 + description: KeyGeneration may be modified to trigger rotations of + access keys created for the bucket. + type: integer + region: + description: Region is the ID of the Object Storage region for the + bucket. + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + secretType: + default: addons.cluster.x-k8s.io/resource-set + description: SecretType sets the type for the bucket-details secret + that will be generated by the controller. + type: string + required: + - region + type: object + status: + description: LinodeObjectStorageBucketStatus defines the observed state + of LinodeObjectStorageBucket + properties: + accessKeyRefs: + description: AccessKeyRefs stores IDs for Object Storage keys provisioned + along with the bucket. + items: + type: integer + type: array + conditions: + description: Conditions specify the service state of the LinodeObjectStorageBucket. + items: + description: Condition defines an observation of a Cluster API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + A human readable message indicating details about the transition. + This field may be empty. + type: string + reason: + description: |- + The reason for the condition's last transition in CamelCase. + The specific API may choose whether or not this field is considered a guaranteed API. + This field may not be empty. + type: string + severity: + description: |- + Severity provides an explicit classification of Reason code, so the users or machines can immediately + understand the current situation and act accordingly. + The Severity field MUST be set only when Status=False. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: |- + Type of condition in CamelCase or in foo.example.com/CamelCase. + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability to deconflict is important. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + creationTime: + description: CreationTime specifies the creation timestamp for the + bucket. + format: date-time + type: string + failureMessage: + description: |- + FailureMessage will be set in the event that there is a terminal problem + reconciling the Object Storage Bucket and will contain a verbose string + suitable for logging and human consumption. + type: string + hostname: + description: Hostname is the address assigned to the bucket. + type: string + keySecretName: + description: KeySecretName specifies the name of the Secret containing + access keys for the bucket. + type: string + lastKeyGeneration: + description: LastKeyGeneration tracks the last known value of .spec.keyGeneration. + type: integer + ready: + default: false + description: Ready denotes that the bucket has been provisioned along + with access keys. + type: boolean + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 660d564d..c1fd8a61 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -32,6 +32,7 @@ patches: - path: patches/webhook_in_linodemachines.yaml - path: patches/webhook_in_linodevpcs.yaml - path: patches/webhook_in_linodevpcs.yaml +- path: patches/webhook_in_linodeobjectstoragebuckets.yaml - path: patches/webhook_in_linodeclustertemplates.yaml - path: patches/webhook_in_linodemachinetemplates.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch @@ -54,6 +55,7 @@ patches: - path: patches/cainjection_in_infrastructure_linodeclusters.yaml #- path: patches/cainjection_in_linodemachines.yaml #- path: patches/cainjection_in_linodevpcs.yaml +#- path: patches/cainjection_in_linodeobjectstoragebuckets.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # [VALIDATION] diff --git a/config/crd/patches/capicontract_in_linodeobjectstoragebuckets.yaml b/config/crd/patches/capicontract_in_linodeobjectstoragebuckets.yaml index 49426ee4..cc8d8762 100644 --- a/config/crd/patches/capicontract_in_linodeobjectstoragebuckets.yaml +++ b/config/crd/patches/capicontract_in_linodeobjectstoragebuckets.yaml @@ -3,4 +3,4 @@ kind: CustomResourceDefinition metadata: name: linodeobjectstoragebuckets.infrastructure.cluster.x-k8s.io labels: - cluster.x-k8s.io/v1beta1: v1alpha1 + cluster.x-k8s.io/v1beta1: v1alpha2 diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index a65a526e..d822a1f4 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -20,6 +20,8 @@ resources: # default, aiding admins in cluster management. Those roles are # not used by the Project itself. You can comment the following lines # if you do not want those helpers be installed with your Project. +- linodeobjectstoragebucket_editor_role.yaml +- linodeobjectstoragebucket_viewer_role.yaml - linodemachinetemplate_editor_role.yaml - linodemachinetemplate_viewer_role.yaml - linodeclustertemplate_editor_role.yaml diff --git a/config/samples/infrastructure_v1alpha2_linodeobjectstoragebucket.yaml b/config/samples/infrastructure_v1alpha2_linodeobjectstoragebucket.yaml new file mode 100644 index 00000000..d9d4ac24 --- /dev/null +++ b/config/samples/infrastructure_v1alpha2_linodeobjectstoragebucket.yaml @@ -0,0 +1,9 @@ +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: LinodeObjectStorageBucket +metadata: + labels: + app.kubernetes.io/name: cluster-api-provider-linode + app.kubernetes.io/managed-by: kustomize + name: linodeobjectstoragebucket-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index c1575ff4..db283218 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -9,6 +9,7 @@ resources: - infrastructure_v1alpha2_linodecluster.yaml - infrastructure_v1alpha2_linodemachine.yaml - infrastructure_v1alpha2_linodevpc.yaml +- infrastructure_v1alpha2_linodeobjectstoragebucket.yaml - infrastructure_v1alpha2_linodeclustertemplate.yaml - infrastructure_v1alpha2_linodemachinetemplate.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 028856d3..5fa6fc07 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -46,6 +46,27 @@ webhooks: resources: - linodemachines sideEffects: None +- admissionReviewVersions: + - v1 + - v1alpha1 + - v1alpha2 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-infrastructure-cluster-x-k8s-io-v1alpha2-linodeobjectstoragebucket + failurePolicy: Fail + name: validation.linodeobjectstoragebucket.infrastructure.cluster.x-k8s.io + rules: + - apiGroups: + - infrastructure.cluster.x-k8s.io + apiVersions: + - v1alpha2 + operations: + - CREATE + resources: + - linodeobjectstoragebuckets + sideEffects: None - admissionReviewVersions: - v1 - v1alpha2 diff --git a/controller/linodeobjectstoragebucket_controller.go b/controller/linodeobjectstoragebucket_controller.go index 4041af71..15ab1eb3 100644 --- a/controller/linodeobjectstoragebucket_controller.go +++ b/controller/linodeobjectstoragebucket_controller.go @@ -41,7 +41,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" - infrav1alpha1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1" + infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/cloud/scope" "github.com/linode/cluster-api-provider-linode/cloud/services" wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient" @@ -82,7 +82,7 @@ func (r *LinodeObjectStorageBucketReconciler) Reconcile(ctx context.Context, req logger := r.Logger.WithValues("name", req.NamespacedName.String()) - objectStorageBucket := &infrav1alpha1.LinodeObjectStorageBucket{} + objectStorageBucket := &infrav1alpha2.LinodeObjectStorageBucket{} if err := r.TracedClient().Get(ctx, req.NamespacedName, objectStorageBucket); err != nil { if err = client.IgnoreNotFound(err); err != nil { logger.Error(err, "Failed to fetch LinodeObjectStorageBucket", "name", req.NamespacedName.String()) @@ -242,7 +242,7 @@ func (r *LinodeObjectStorageBucketReconciler) reconcileDelete(ctx context.Contex return err } - if !controllerutil.RemoveFinalizer(bScope.Bucket, infrav1alpha1.ObjectStorageBucketFinalizer) { + if !controllerutil.RemoveFinalizer(bScope.Bucket, infrav1alpha2.ObjectStorageBucketFinalizer) { err := errors.New("failed to remove finalizer from bucket; unable to delete") bScope.Logger.Error(err, "controllerutil.RemoveFinalizer") r.setFailure(bScope, err) @@ -250,8 +250,8 @@ func (r *LinodeObjectStorageBucketReconciler) reconcileDelete(ctx context.Contex return err } // TODO: remove this check and removal later - if controllerutil.ContainsFinalizer(bScope.Bucket, infrav1alpha1.GroupVersion.String()) { - controllerutil.RemoveFinalizer(bScope.Bucket, infrav1alpha1.GroupVersion.String()) + if controllerutil.ContainsFinalizer(bScope.Bucket, infrav1alpha2.GroupVersion.String()) { + controllerutil.RemoveFinalizer(bScope.Bucket, infrav1alpha2.GroupVersion.String()) } r.Recorder.Event(bScope.Bucket, clusterv1.DeletedReason, "Revoked", "Object storage keys revoked") @@ -263,15 +263,16 @@ func (r *LinodeObjectStorageBucketReconciler) reconcileDelete(ctx context.Contex func (r *LinodeObjectStorageBucketReconciler) SetupWithManager(mgr ctrl.Manager, options crcontroller.Options) error { linodeObjectStorageBucketMapper, err := kutil.ClusterToTypedObjectsMapper( r.TracedClient(), - &infrav1alpha1.LinodeObjectStorageBucketList{}, + &infrav1alpha2.LinodeObjectStorageBucketList{}, mgr.GetScheme(), ) + if err != nil { return fmt.Errorf("failed to create mapper for LinodeObjectStorageBuckets: %w", err) } err = ctrl.NewControllerManagedBy(mgr). - For(&infrav1alpha1.LinodeObjectStorageBucket{}). + For(&infrav1alpha2.LinodeObjectStorageBucket{}). WithOptions(options). Owns(&corev1.Secret{}). WithEventFilter(predicate.And( diff --git a/controller/linodeobjectstoragebucket_controller_test.go b/controller/linodeobjectstoragebucket_controller_test.go index 124eb07b..7a649c40 100644 --- a/controller/linodeobjectstoragebucket_controller_test.go +++ b/controller/linodeobjectstoragebucket_controller_test.go @@ -37,7 +37,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/yaml" - infrav1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1" + infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/cloud/scope" "github.com/linode/cluster-api-provider-linode/mock" "github.com/linode/cluster-api-provider-linode/util" @@ -68,13 +68,13 @@ type accessKeySecret struct { var _ = Describe("lifecycle", Ordered, Label("bucket", "lifecycle"), func() { suite := NewControllerSuite(GinkgoT(), mock.MockLinodeClient{}) - obj := infrav1.LinodeObjectStorageBucket{ + obj := infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "lifecycle", Namespace: "default", }, - Spec: infrav1.LinodeObjectStorageBucketSpec{ - Cluster: "cluster", + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "region", }, } @@ -106,19 +106,19 @@ var _ = Describe("lifecycle", Ordered, Label("bucket", "lifecycle"), func() { suite.Run( OneOf( Path(Call("bucket is created", func(ctx context.Context, mck Mock) { - getBucket := mck.LinodeClient.EXPECT().GetObjectStorageBucket(gomock.Any(), obj.Spec.Cluster, gomock.Any()).Return(nil, nil) + getBucket := mck.LinodeClient.EXPECT().GetObjectStorageBucket(gomock.Any(), obj.Spec.Region, gomock.Any()).Return(nil, nil) mck.LinodeClient.EXPECT().CreateObjectStorageBucket(gomock.Any(), gomock.Any()). After(getBucket). Return(&linodego.ObjectStorageBucket{ Label: obj.Name, - Region: obj.Spec.Cluster, + Region: obj.Spec.Region, Created: util.Pointer(time.Now()), Hostname: "hostname", }, nil) })), Path( Call("bucket is not created", func(ctx context.Context, mck Mock) { - getBucket := mck.LinodeClient.EXPECT().GetObjectStorageBucket(gomock.Any(), obj.Spec.Cluster, gomock.Any()).Return(nil, nil) + getBucket := mck.LinodeClient.EXPECT().GetObjectStorageBucket(gomock.Any(), obj.Spec.Region, gomock.Any()).Return(nil, nil) mck.LinodeClient.EXPECT().CreateObjectStorageBucket(gomock.Any(), gomock.Any()).After(getBucket).Return(nil, errors.New("create bucket error")) }), Result("error", func(ctx context.Context, mck Mock) { @@ -177,7 +177,7 @@ var _ = Describe("lifecycle", Ordered, Label("bucket", "lifecycle"), func() { var key accessKeySecret Expect(yaml.Unmarshal(secret.Data["bucket-details-secret.yaml"], &key)).NotTo(HaveOccurred()) Expect(key.StringData.BucketName).To(Equal("lifecycle")) - Expect(key.StringData.BucketRegion).To(Equal("cluster")) + Expect(key.StringData.BucketRegion).To(Equal("region")) Expect(key.StringData.BucketEndpoint).To(Equal("hostname")) Expect(key.StringData.AccessKeyRW).To(Equal("access-key-0")) Expect(key.StringData.SecretKeyRW).To(Equal("secret-key-0")) @@ -199,17 +199,17 @@ var _ = Describe("lifecycle", Ordered, Label("bucket", "lifecycle"), func() { }), OneOf( Path(Call("bucket is retrieved on update", func(ctx context.Context, mck Mock) { - mck.LinodeClient.EXPECT().GetObjectStorageBucket(gomock.Any(), obj.Spec.Cluster, gomock.Any()). + mck.LinodeClient.EXPECT().GetObjectStorageBucket(gomock.Any(), obj.Spec.Region, gomock.Any()). Return(&linodego.ObjectStorageBucket{ Label: obj.Name, - Region: obj.Spec.Cluster, + Region: obj.Spec.Region, Created: util.Pointer(time.Now()), Hostname: "hostname", }, nil) })), Path( Call("bucket is not retrieved on update", func(ctx context.Context, mck Mock) { - mck.LinodeClient.EXPECT().GetObjectStorageBucket(gomock.Any(), obj.Spec.Cluster, gomock.Any()).Return(nil, errors.New("get bucket error")) + mck.LinodeClient.EXPECT().GetObjectStorageBucket(gomock.Any(), obj.Spec.Region, gomock.Any()).Return(nil, errors.New("get bucket error")) }), Result("error", func(ctx context.Context, mck Mock) { bScope.LinodeClient = mck.LinodeClient @@ -300,7 +300,7 @@ var _ = Describe("lifecycle", Ordered, Label("bucket", "lifecycle"), func() { var key accessKeySecret Expect(yaml.Unmarshal(secret.Data["bucket-details-secret.yaml"], &key)).NotTo(HaveOccurred()) Expect(key.StringData.BucketName).To(Equal("lifecycle")) - Expect(key.StringData.BucketRegion).To(Equal("cluster")) + Expect(key.StringData.BucketRegion).To(Equal("region")) Expect(key.StringData.BucketEndpoint).To(Equal("hostname")) Expect(key.StringData.AccessKeyRW).To(Equal("access-key-2")) Expect(key.StringData.SecretKeyRW).To(Equal("secret-key-2")) @@ -372,14 +372,14 @@ var _ = Describe("errors", Label("bucket", "errors"), func() { // Reset obj to base state to be modified in each test path. // We can use a consistent name since these tests are stateless. - bScope.Bucket = &infrav1.LinodeObjectStorageBucket{ + bScope.Bucket = &infrav1alpha2.LinodeObjectStorageBucket{ ObjectMeta: metav1.ObjectMeta{ Name: "mock", Namespace: "default", UID: "12345", }, - Spec: infrav1.LinodeObjectStorageBucketSpec{ - Cluster: "cluster", + Spec: infrav1alpha2.LinodeObjectStorageBucketSpec{ + Region: "region", }, } }) @@ -425,7 +425,7 @@ var _ = Describe("errors", Label("bucket", "errors"), func() { Expect(err.Error()).To(ContainSubstring("failed to create object storage bucket scope")) Expect(mck.Logs()).To(ContainSubstring("Failed to create object storage bucket scope")) }), - Call("scheme with no infrav1alpha1", func(ctx context.Context, mck Mock) { + Call("scheme with no infrav1alpha2", func(ctx context.Context, mck Mock) { prev := mck.K8sClient.EXPECT().Scheme().Return(scheme.Scheme) mck.K8sClient.EXPECT().Scheme().After(prev).Return(runtime.NewScheme()).Times(2) }), diff --git a/docs/src/topics/backups.md b/docs/src/topics/backups.md index 32953fe0..2660dcee 100644 --- a/docs/src/topics/backups.md +++ b/docs/src/topics/backups.md @@ -31,13 +31,13 @@ Using this feature requires enabling Object Storage in the account where the res The following is the minimal required configuration needed to provision an Object Storage bucket and set of access keys. ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: namespace: spec: - cluster: + region: secretType: Opaque ``` @@ -58,7 +58,7 @@ metadata: name: -bucket-details namespace: ownerReferences: - - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket name: controller: true @@ -80,7 +80,7 @@ The bucket-details secret is owned and managed by CAPL during the life of the `L The following configuration with `keyGeneration` set to a new value (different from `.status.lastKeyGeneration`) will instruct CAPL to rotate the access keys. ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: @@ -98,7 +98,7 @@ spec: Upon successful provisioning of a bucket and keys, the `LinodeObjectStorageBucket` resource's status will resemble the following: ```yaml -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: diff --git a/docs/src/topics/multi-tenancy.md b/docs/src/topics/multi-tenancy.md index 6609d2b5..c67b0b94 100644 --- a/docs/src/topics/multi-tenancy.md +++ b/docs/src/topics/multi-tenancy.md @@ -53,7 +53,7 @@ spec: ... --- # Example: LinodeObjectStorageBucket -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: test-bucket diff --git a/e2e/admission-webhooks/validating/invalid-linodeobjectstoragebucket.yaml b/e2e/admission-webhooks/validating/invalid-linodeobjectstoragebucket.yaml index 0c7772dd..d16c36f7 100644 --- a/e2e/admission-webhooks/validating/invalid-linodeobjectstoragebucket.yaml +++ b/e2e/admission-webhooks/validating/invalid-linodeobjectstoragebucket.yaml @@ -1,7 +1,7 @@ --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: ($name) spec: - cluster: invalid-1 + region: invalid diff --git a/e2e/linodemachine-controller/minimal-linodemachine/chainsaw-test.yaml b/e2e/linodemachine-controller/minimal-linodemachine/chainsaw-test.yaml index d3acd722..6dc870d9 100755 --- a/e2e/linodemachine-controller/minimal-linodemachine/chainsaw-test.yaml +++ b/e2e/linodemachine-controller/minimal-linodemachine/chainsaw-test.yaml @@ -39,7 +39,7 @@ spec: file: assert-linodemachine.yaml catch: - describe: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeMachineTemplate - describe: apiVersion: controlplane.cluster.x-k8s.io/v1beta1 diff --git a/e2e/linodemachine-controller/minimal-linodemachine/create-linodemachine.yaml b/e2e/linodemachine-controller/minimal-linodemachine/create-linodemachine.yaml index af9b6d71..e68673e9 100644 --- a/e2e/linodemachine-controller/minimal-linodemachine/create-linodemachine.yaml +++ b/e2e/linodemachine-controller/minimal-linodemachine/create-linodemachine.yaml @@ -14,13 +14,13 @@ spec: cloud-provider: external machineTemplate: infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeMachineTemplate name: ($cluster) replicas: 1 version: 1.29.1 --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeMachineTemplate metadata: name: ($cluster) diff --git a/e2e/linodemachine-controller/vpc-integration/assert-vpc.yaml b/e2e/linodemachine-controller/vpc-integration/assert-vpc.yaml index 116e1ad0..7d5158e9 100644 --- a/e2e/linodemachine-controller/vpc-integration/assert-vpc.yaml +++ b/e2e/linodemachine-controller/vpc-integration/assert-vpc.yaml @@ -1,4 +1,4 @@ -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeVPC metadata: name: ($vpc) diff --git a/e2e/linodemachine-controller/vpc-integration/chainsaw-test.yaml b/e2e/linodemachine-controller/vpc-integration/chainsaw-test.yaml index 45fea621..f346c442 100755 --- a/e2e/linodemachine-controller/vpc-integration/chainsaw-test.yaml +++ b/e2e/linodemachine-controller/vpc-integration/chainsaw-test.yaml @@ -38,10 +38,10 @@ spec: apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster - describe: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeCluster - describe: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeVPC - name: Create LinodeMachine resource try: @@ -113,7 +113,7 @@ spec: name: ($cluster) - delete: ref: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeVPC name: ($vpc) - error: diff --git a/e2e/linodemachine-controller/vpc-integration/check-vpc-lm-deletion.yaml b/e2e/linodemachine-controller/vpc-integration/check-vpc-lm-deletion.yaml index df95a604..7fc64212 100644 --- a/e2e/linodemachine-controller/vpc-integration/check-vpc-lm-deletion.yaml +++ b/e2e/linodemachine-controller/vpc-integration/check-vpc-lm-deletion.yaml @@ -4,7 +4,7 @@ kind: LinodeMachine metadata: name: ($cluster) --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeVPC metadata: name: ($vpc) diff --git a/e2e/linodemachine-controller/vpc-integration/create-cluster-vpc.yaml b/e2e/linodemachine-controller/vpc-integration/create-cluster-vpc.yaml index cc7871dc..e268019f 100644 --- a/e2e/linodemachine-controller/vpc-integration/create-cluster-vpc.yaml +++ b/e2e/linodemachine-controller/vpc-integration/create-cluster-vpc.yaml @@ -23,7 +23,7 @@ spec: kind: LinodeVPC name: ($vpc) --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeVPC metadata: name: ($vpc) diff --git a/e2e/linodemachine-controller/vpc-integration/create-linodemachine.yaml b/e2e/linodemachine-controller/vpc-integration/create-linodemachine.yaml index 8b5c27f2..3a3d0cde 100644 --- a/e2e/linodemachine-controller/vpc-integration/create-linodemachine.yaml +++ b/e2e/linodemachine-controller/vpc-integration/create-linodemachine.yaml @@ -14,13 +14,13 @@ spec: cloud-provider: external machineTemplate: infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeMachineTemplate name: ($cluster) replicas: 1 version: 1.29.1 --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeMachineTemplate metadata: name: ($cluster) diff --git a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/assert-keygen.yaml b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/assert-keygen.yaml index fc6d9ba1..002d2aee 100644 --- a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/assert-keygen.yaml +++ b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/assert-keygen.yaml @@ -1,9 +1,9 @@ -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: ($bucket) spec: - cluster: us-sea-1 + region: us-sea keyGeneration: 1 status: ready: true diff --git a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/assert-obj-and-secret.yaml b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/assert-obj-and-secret.yaml index 081428e5..c6532c45 100644 --- a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/assert-obj-and-secret.yaml +++ b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/assert-obj-and-secret.yaml @@ -1,10 +1,10 @@ --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: ($bucket) spec: - cluster: us-sea-1 + region: us-sea keyGeneration: 0 status: ready: true diff --git a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/chainsaw-test.yaml b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/chainsaw-test.yaml index b5499c1d..0b5a3e27 100755 --- a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/chainsaw-test.yaml +++ b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/chainsaw-test.yaml @@ -33,7 +33,7 @@ spec: file: assert-obj-and-secret.yaml catch: - describe: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket - describe: apiVersion: v1 @@ -62,7 +62,7 @@ spec: file: assert-keygen.yaml catch: - describe: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket - name: Delete LinodeObjectStorageBucket details Secret try: @@ -83,7 +83,7 @@ spec: try: - delete: ref: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket name: ($bucket) - script: diff --git a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/check-obj-and-key-deletion.yaml b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/check-obj-and-key-deletion.yaml index 57ae7457..a7a73182 100644 --- a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/check-obj-and-key-deletion.yaml +++ b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/check-obj-and-key-deletion.yaml @@ -1,4 +1,4 @@ -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: ($bucket) diff --git a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/create-linodeobjectstoragebucket.yaml b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/create-linodeobjectstoragebucket.yaml index b3d4cfda..7292e0b2 100644 --- a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/create-linodeobjectstoragebucket.yaml +++ b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/create-linodeobjectstoragebucket.yaml @@ -1,6 +1,6 @@ -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: ($bucket) spec: - cluster: us-sea-1 + region: us-sea \ No newline at end of file diff --git a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/patch-obj.yaml b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/patch-obj.yaml index c63182bb..67f07c12 100644 --- a/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/patch-obj.yaml +++ b/e2e/linodeobjectstoragebucket-controller/minimal-linodeobjectstoragebucket/patch-obj.yaml @@ -1,7 +1,7 @@ -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: name: ($bucket) spec: - cluster: us-sea-1 + region: us-sea keyGeneration: 1 diff --git a/templates/addons/etcd-backup-restore/linode-obj.yaml b/templates/addons/etcd-backup-restore/linode-obj.yaml index 1412b384..a25f0a15 100644 --- a/templates/addons/etcd-backup-restore/linode-obj.yaml +++ b/templates/addons/etcd-backup-restore/linode-obj.yaml @@ -1,5 +1,5 @@ --- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1 +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 kind: LinodeObjectStorageBucket metadata: labels: @@ -11,7 +11,7 @@ metadata: cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} name: ${CLUSTER_NAME}-etcd-backup spec: - cluster: ${OBJ_BUCKET_REGION:=${LINODE_REGION}-1} + region: ${OBJ_BUCKET_REGION:=${LINODE_REGION}} --- apiVersion: addons.cluster.x-k8s.io/v1beta1 kind: ClusterResourceSet