From f29f02fb97a8e67d8d8a10d745cc397e63862c89 Mon Sep 17 00:00:00 2001 From: Ace Eldeib Date: Wed, 7 Aug 2019 04:27:28 +0000 Subject: [PATCH 01/19] feat: implement keyvault controller --- PROJECT | 3 + api/v1/keyvault_types.go | 60 +++++++ api/v1/zz_generated.deepcopy.go | 89 ++++++++++ .../bases/azure.microsoft.com_keyvaults.yaml | 60 +++++++ config/crd/kustomization.yaml | 3 + .../crd/patches/cainjection_in_keyvaults.yaml | 8 + config/crd/patches/webhook_in_keyvaults.yaml | 17 ++ config/rbac/role.yaml | 20 +++ config/samples/azure_v1_keyvault.yaml | 7 + controllers/keyvault_controller.go | 154 ++++++++++++++++++ controllers/suite_test.go | 3 + go.mod | 2 +- go.sum | 2 + helpers/stringhelper.go | 35 ++++ main.go | 12 ++ resourcemanager/keyvaults/keyvaults.go | 81 +++++++++ 16 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 api/v1/keyvault_types.go create mode 100644 config/crd/bases/azure.microsoft.com_keyvaults.yaml create mode 100644 config/crd/patches/cainjection_in_keyvaults.yaml create mode 100644 config/crd/patches/webhook_in_keyvaults.yaml create mode 100644 config/samples/azure_v1_keyvault.yaml create mode 100644 controllers/keyvault_controller.go create mode 100644 resourcemanager/keyvaults/keyvaults.go diff --git a/PROJECT b/PROJECT index b5b46c4c224..be5d1060e35 100644 --- a/PROJECT +++ b/PROJECT @@ -11,3 +11,6 @@ resources: - group: azure version: v1 kind: EventhubNamespace +- group: azure + version: v1 + kind: KeyVault diff --git a/api/v1/keyvault_types.go b/api/v1/keyvault_types.go new file mode 100644 index 00000000000..385ff12c4fc --- /dev/null +++ b/api/v1/keyvault_types.go @@ -0,0 +1,60 @@ +/* + +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 v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// KeyVaultSpec defines the desired state of KeyVault +type KeyVaultSpec struct { + Location string `json:"location"` + ResourceGroupName string `json:"resourceGroup"` +} + +// KeyVaultStatus defines the observed state of KeyVault +type KeyVaultStatus struct { + Provisioning bool `json:"provisioning,omitempty"` + Provisioned bool `json:"provisioned,omitempty"` +} + +// +kubebuilder:object:root=true + +// KeyVault is the Schema for the keyvaults API +type KeyVault struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KeyVaultSpec `json:"spec,omitempty"` + Status KeyVaultStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// KeyVaultList contains a list of KeyVault +type KeyVaultList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KeyVault `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KeyVault{}, &KeyVaultList{}) +} + +func (keyVault *KeyVault) IsSubmitted() bool { + return keyVault.Status.Provisioning || keyVault.Status.Provisioned +} diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 09a40a797cf..cc8243b3678 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -287,6 +287,95 @@ func (in *EventhubStatus) DeepCopy() *EventhubStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeyVault) DeepCopyInto(out *KeyVault) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyVault. +func (in *KeyVault) DeepCopy() *KeyVault { + if in == nil { + return nil + } + out := new(KeyVault) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KeyVault) 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 *KeyVaultList) DeepCopyInto(out *KeyVaultList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KeyVault, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyVaultList. +func (in *KeyVaultList) DeepCopy() *KeyVaultList { + if in == nil { + return nil + } + out := new(KeyVaultList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KeyVaultList) 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 *KeyVaultSpec) DeepCopyInto(out *KeyVaultSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyVaultSpec. +func (in *KeyVaultSpec) DeepCopy() *KeyVaultSpec { + if in == nil { + return nil + } + out := new(KeyVaultSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeyVaultStatus) DeepCopyInto(out *KeyVaultStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyVaultStatus. +func (in *KeyVaultStatus) DeepCopy() *KeyVaultStatus { + if in == nil { + return nil + } + out := new(KeyVaultStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceGroup) DeepCopyInto(out *ResourceGroup) { *out = *in diff --git a/config/crd/bases/azure.microsoft.com_keyvaults.yaml b/config/crd/bases/azure.microsoft.com_keyvaults.yaml new file mode 100644 index 00000000000..1b176033709 --- /dev/null +++ b/config/crd/bases/azure.microsoft.com_keyvaults.yaml @@ -0,0 +1,60 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: keyvaults.azure.microsoft.com +spec: + group: azure.microsoft.com + names: + kind: KeyVault + plural: keyvaults + scope: "" + validation: + openAPIV3Schema: + description: KeyVault is the Schema for the keyvaults 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/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/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: KeyVaultSpec defines the desired state of KeyVault + properties: + location: + type: string + resourceGroup: + type: string + required: + - location + - resourceGroup + type: object + status: + description: KeyVaultStatus defines the observed state of KeyVault + properties: + provisioned: + type: boolean + provisioning: + type: boolean + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index ab172b85771..11ac6f5386c 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -5,6 +5,7 @@ resources: - bases/azure.microsoft.com_eventhubs.yaml - bases/azure.microsoft.com_resourcegroups.yaml - bases/azure.microsoft.com_eventhubnamespaces.yaml +- bases/azure.microsoft.com_keyvaults.yaml # +kubebuilder:scaffold:crdkustomizeresource patches: @@ -12,12 +13,14 @@ patches: #- patches/webhook_in_eventhubs.yaml #- patches/webhook_in_resourcegroups.yaml #- patches/webhook_in_eventhubnamespaces.yaml +#- patches/webhook_in_keyvaults.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CAINJECTION] patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_eventhubs.yaml #- patches/cainjection_in_resourcegroups.yaml #- patches/cainjection_in_eventhubnamespaces.yaml +#- patches/cainjection_in_keyvaults.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_keyvaults.yaml b/config/crd/patches/cainjection_in_keyvaults.yaml new file mode 100644 index 00000000000..992c7aaeedd --- /dev/null +++ b/config/crd/patches/cainjection_in_keyvaults.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + certmanager.k8s.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: keyvaults.azure.microsoft.com diff --git a/config/crd/patches/webhook_in_keyvaults.yaml b/config/crd/patches/webhook_in_keyvaults.yaml new file mode 100644 index 00000000000..ee66acef7dc --- /dev/null +++ b/config/crd/patches/webhook_in_keyvaults.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: keyvaults.azure.microsoft.com +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index a27d53b8efa..76adc48e2e5 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -73,6 +73,26 @@ rules: verbs: - create - patch +- apiGroups: + - azure.microsoft.com + resources: + - keyvaults + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - azure.microsoft.com + resources: + - keyvaults/status + verbs: + - get + - patch + - update - apiGroups: - azure.microsoft.com resources: diff --git a/config/samples/azure_v1_keyvault.yaml b/config/samples/azure_v1_keyvault.yaml new file mode 100644 index 00000000000..506cd180d4b --- /dev/null +++ b/config/samples/azure_v1_keyvault.yaml @@ -0,0 +1,7 @@ +apiVersion: azure.microsoft.com/v1 +kind: KeyVault +metadata: + name: ace-kv-sample +spec: + resourceGroup: ace-crd + location: westus2 diff --git a/controllers/keyvault_controller.go b/controllers/keyvault_controller.go new file mode 100644 index 00000000000..aeb28187004 --- /dev/null +++ b/controllers/keyvault_controller.go @@ -0,0 +1,154 @@ +/* + +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 controllers + +import ( + "context" + "fmt" + "os" + "strconv" + "time" + + "github.com/Azure/azure-service-operator/resourcemanager/keyvaults" + azurev1 "github.com/Azure/azure-service-operator/api/v1" + helpers "github.com/Azure/azure-service-operator/helpers" + "github.com/go-logr/logr" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const keyVaultFinalizerName = "keyvault.finalizers.azure.com" + +// KeyVaultReconciler reconciles a KeyVault object +type KeyVaultReconciler struct { + client.Client + Log logr.Logger + Recorder record.EventRecorder +} + +// +kubebuilder:rbac:groups=azure.microsoft.com,resources=keyvaults,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=azure.microsoft.com,resources=keyvaults/status,verbs=get;update;patch + +func (r *KeyVaultReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + ctx := context.Background() + log := r.Log.WithValues("keyvault", req.NamespacedName) + + var instance azurev1.KeyVault + if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { + log.Error(err, "unable to fetch KeyVault") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + if helpers.IsBeingDeleted(&instance) { + if helpers.HasFinalizer(&instance, keyVaultFinalizerName) { + if err := r.deleteExternal(&instance); err != nil { + return ctrl.Result{}, err + } + + helpers.RemoveFinalizer(&instance, keyVaultFinalizerName) + if err := r.Update(context.Background(), &instance); err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil + } + + if !helpers.HasFinalizer(&instance, keyVaultFinalizerName) { + if err := r.addFinalizer(&instance); err != nil { + return ctrl.Result{}, err + } + } + + if !instance.IsSubmitted() { + if err := r.reconcileExternal(&instance); err != nil { + return ctrl.Result{}, fmt.Errorf("error reconciling keyvault in azure: %v", err) + } + return ctrl.Result{}, nil + } + + requeueAfter, err := strconv.Atoi(os.Getenv("REQUEUE_AFTER")) + if err != nil { + requeueAfter = 30 + } + + return ctrl.Result{ + RequeueAfter: time.Second * time.Duration(requeueAfter), + }, nil +} + +func (r *KeyVaultReconciler) addFinalizer(instance *azurev1.KeyVault) error { + helpers.AddFinalizer(instance, keyVaultFinalizerName) + err := r.Update(context.Background(), instance) + if err != nil { + return fmt.Errorf("failed to update finalizer: %v", err) + } + r.Recorder.Event(instance, "Normal", "Updated", fmt.Sprintf("finalizer %s added", keyVaultFinalizerName)) + return nil +} + +func (r *KeyVaultReconciler) reconcileExternal(instance *azurev1.KeyVault) error { + ctx := context.Background() + location := instance.Spec.Location + name := instance.ObjectMeta.Name + groupName := instance.Spec.ResourceGroupName + + // write information back to instance + instance.Status.Provisioning = true + + if err := r.Update(ctx, instance); err != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } + + _, err := keyvaults.CreateVaultAndWait(ctx, groupName, name, location) + if err != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Couldn't create resource in azure") + instance.Status.Provisioning = false + errUpdate := r.Update(ctx, instance) + if errUpdate != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } + return err + } + + instance.Status.Provisioning = false + instance.Status.Provisioned = true + + if err = r.Update(ctx, instance); err != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } + + r.Recorder.Event(instance, "Normal", "Updated", name+" provisioned") + return nil +} + +func (r *KeyVaultReconciler) deleteExternal(instance *azurev1.KeyVault) error { + ctx := context.Background() + name := instance.ObjectMeta.Name + groupName := instance.Spec.ResourceGroupName + _, err := keyvaults.DeleteVault(ctx, groupName, name) + if err != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Couldn't delete resouce in azure") + return err + } + return nil +} + +func (r *KeyVaultReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&azurev1.KeyVault{}). + Complete(r) +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 9be0f70a23e..ed626743c73 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -81,6 +81,9 @@ var _ = BeforeSuite(func(done Done) { err = azurev1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = azurev1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + // +kubebuilder:scaffold:scheme k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme.Scheme, diff --git a/go.mod b/go.mod index 520b33b96f8..eff3883d5ec 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/modern-go/reflect2 v1.0.1 // indirect github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega v1.5.0 - github.com/spf13/pflag v1.0.3 // indirect + github.com/satori/go.uuid v1.2.0 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 // indirect golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/sys v0.0.0-20190621203818-d432491b9138 // indirect diff --git a/go.sum b/go.sum index 530c896dbc7..c2febe3287c 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,8 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.1.0 h1:g0fH8RicVgNl+zVZDCDfbdWxAWoAEJyI7I3TZYXFiig= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= diff --git a/helpers/stringhelper.go b/helpers/stringhelper.go index c4a88551e03..7c04b18f779 100644 --- a/helpers/stringhelper.go +++ b/helpers/stringhelper.go @@ -3,6 +3,8 @@ package helpers import ( "math/rand" "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const charset = "abcdefghijklmnopqrstuvwxyz" @@ -38,3 +40,36 @@ func randomStringWithCharset(length int, charset string) string { func RandomString(length int) string { return randomStringWithCharset(length, charset) } + +// IsBeingDeleted returns true if the current object is being deleted from the API server. +func IsBeingDeleted(o metav1.Object) bool { + return !o.GetDeletionTimestamp().IsZero() +} + +// AddFinalizer accepts a metav1 object and adds the provided finalizer if not present. +func AddFinalizer(o metav1.Object, finalizer string) { + f := o.GetFinalizers() + for _, e := range f { + if e == finalizer { + return + } + } + o.SetFinalizers(append(f, finalizer)) +} + +// RemoveFinalizer accepts a metav1 object and removes the provided finalizer if present. +func RemoveFinalizer(o metav1.Object, finalizer string) { + f := o.GetFinalizers() + for i, e := range f { + if e == finalizer { + f = append(f[:i], f[i+1:]...) + o.SetFinalizers(f) + return + } + } +} + +// HasFinalizer accepts a metav1 object and returns true if the the object has the provided finalizer. +func HasFinalizer(o metav1.Object, finalizer string) bool { + return ContainsString(o.GetFinalizers(), finalizer) +} diff --git a/main.go b/main.go index 72e4ff0616d..f54895b7034 100644 --- a/main.go +++ b/main.go @@ -40,6 +40,7 @@ func init() { azurev1.AddToScheme(scheme) kscheme.AddToScheme(scheme) + _ = azurev1.AddToScheme(scheme) // +kubebuilder:scaffold:scheme } @@ -102,6 +103,15 @@ func main() { os.Exit(1) } + if err = (&controllers.KeyVaultReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("KeyVault"), + Recorder: mgr.GetEventRecorderFor("KeyVault-controller"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "KeyVault") + os.Exit(1) + } + if err = (&azurev1.EventhubNamespace{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "EventhubNamespace") os.Exit(1) @@ -111,7 +121,9 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "Eventhub") os.Exit(1) } + // +kubebuilder:scaffold:builder + setupLog.Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { setupLog.Error(err, "problem running manager") diff --git a/resourcemanager/keyvaults/keyvaults.go b/resourcemanager/keyvaults/keyvaults.go new file mode 100644 index 00000000000..08d0949a9bd --- /dev/null +++ b/resourcemanager/keyvaults/keyvaults.go @@ -0,0 +1,81 @@ +/* + +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 keyvaults + +import ( + "context" + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" + "github.com/Azure/azure-service-operator/resourcemanager/config" + "github.com/Azure/azure-service-operator/resourcemanager/iam" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/to" + uuid "github.com/satori/go.uuid" +) + +func getVaultsClient() keyvault.VaultsClient { + vaultsClient := keyvault.NewVaultsClient(config.SubscriptionID()) + a, err := iam.GetResourceManagementAuthorizer() + if err != nil { + log.Fatalf("failed to initialize authorizer: %v\n", err) + } + vaultsClient.Authorizer = a + vaultsClient.AddToUserAgent(config.UserAgent()) + return vaultsClient +} + +// CreateVaultAndWait creates a new resource group named by env var +func CreateVaultAndWait(ctx context.Context, groupName string, vaultName string, location string) (keyvault.Vault, error) { + vaultsClient := getVaultsClient() + id, err := uuid.FromString(config.TenantID()) + if err != nil { + return keyvault.Vault{}, err + } + + params := keyvault.VaultCreateOrUpdateParameters{ + Properties: &keyvault.VaultProperties{ + TenantID: &id, + AccessPolicies: &[]keyvault.AccessPolicyEntry{}, + Sku: &keyvault.Sku{ + Family: to.StringPtr("A"), + Name: keyvault.Standard, + }, + }, + Location: to.StringPtr(location), + } + + log.Println(fmt.Sprintf("creating keyvault '%s' in resource group '%s' and location: %v", vaultName, groupName, location)) + future, err := vaultsClient.CreateOrUpdate(ctx, groupName, vaultName, params) + if err != nil { + return keyvault.Vault{}, err + } + + err = future.WaitForCompletionRef(ctx, vaultsClient.Client) + if err != nil { + return keyvault.Vault{}, err + } + + result, err := future.Result(vaultsClient) + return result, err +} + +// DeleteVault removes the resource group named by env var +func DeleteVault(ctx context.Context, groupName string, vaultName string) (result autorest.Response, err error) { + vaultsClient := getVaultsClient() + return vaultsClient.Delete(ctx, groupName, vaultName) +} From 30d70a088faa5020deff980f76ef7941a80821ee Mon Sep 17 00:00:00 2001 From: jananivMS Date: Wed, 14 Aug 2019 11:14:43 -0600 Subject: [PATCH 02/19] Ace's KV changes with updates --- ...zure.microsoft.com_eventhubnamespaces.yaml | 369 +++++++++++++++++ .../bases/azure.microsoft.com_eventhubs.yaml | 373 ++++++++++++++++++ .../bases/azure.microsoft.com_keyvaults.yaml | 370 ++++++++++++++++- .../azure.microsoft.com_resourcegroups.yaml | 369 +++++++++++++++++ config/default/kustomization.yaml | 2 +- config/default/manager_image_patch.yaml | 2 +- config/default/manager_image_patch.yaml-e | 36 ++ config/rbac/role.yaml | 64 +-- config/samples/azure_v1_keyvault.yaml | 6 +- config/samples/creator_v1_eventhub.yaml | 6 +- .../samples/creator_v1_eventhubnamespace.yaml | 4 +- .../creator_v1_resourcegroup copy.yaml | 6 + config/samples/creator_v1_resourcegroup.yaml | 2 +- controllers/eventhub_controller.go | 1 + controllers/eventhubnamespace_controller.go | 1 + controllers/keyvault_controller.go | 49 ++- controllers/resourcegroup_controller.go | 2 + go.mod | 7 +- go.sum | 12 + main.go | 8 +- pkg/errhelp/errhelp.go | 25 ++ resourcemanager/keyvaults/keyvaults.go | 15 +- 22 files changed, 1647 insertions(+), 82 deletions(-) create mode 100644 config/default/manager_image_patch.yaml-e create mode 100644 config/samples/creator_v1_resourcegroup copy.yaml create mode 100644 pkg/errhelp/errhelp.go diff --git a/config/crd/bases/azure.microsoft.com_eventhubnamespaces.yaml b/config/crd/bases/azure.microsoft.com_eventhubnamespaces.yaml index 2e019952162..f05791e430e 100644 --- a/config/crd/bases/azure.microsoft.com_eventhubnamespaces.yaml +++ b/config/crd/bases/azure.microsoft.com_eventhubnamespaces.yaml @@ -26,6 +26,375 @@ spec: submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' type: string metadata: + description: ObjectMeta is metadata that all persisted resources must have, + which includes all objects users must create. + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. They are not queryable and should be preserved + when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + clusterName: + description: The name of the cluster which the object belongs to. This + is used to distinguish resources with same name and namespace in different + clusters. This field is not set anywhere right now and apiserver is + going to ignore it if set in create or update request. + type: string + creationTimestamp: + description: "CreationTimestamp is a timestamp representing the server + time when this object was created. It is not guaranteed to be set + in happens-before order across separate operations. Clients may not + set this value. It is represented in RFC3339 form and is in UTC. \n + Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + deletionGracePeriodSeconds: + description: Number of seconds allowed for this object to gracefully + terminate before it will be removed from the system. Only set when + deletionTimestamp is also set. May only be shortened. Read-only. + format: int64 + type: integer + deletionTimestamp: + description: "DeletionTimestamp is RFC 3339 date and time at which this + resource will be deleted. This field is set by the server when a graceful + deletion is requested by the user, and is not directly settable by + a client. The resource is expected to be deleted (no longer visible + from resource lists, and not reachable by name) after the time in + this field, once the finalizers list is empty. As long as the finalizers + list contains items, deletion is blocked. Once the deletionTimestamp + is set, this value may not be unset or be set further into the future, + although it may be shortened or the resource may be deleted prior + to this time. For example, a user may request that a pod is deleted + in 30 seconds. The Kubelet will react by sending a graceful termination + signal to the containers in the pod. After that 30 seconds, the Kubelet + will send a hard termination signal (SIGKILL) to the container and + after cleanup, remove the pod from the API. In the presence of network + partitions, this object may still exist after this timestamp, until + an administrator or automated process can determine the resource is + fully terminated. If not set, graceful deletion of the object has + not been requested. \n Populated by the system when a graceful deletion + is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + finalizers: + description: Must be empty before the object is deleted from the registry. + Each entry is an identifier for the responsible component that will + remove the entry from the list. If the deletionTimestamp of the object + is non-nil, entries in this list can only be removed. + items: + type: string + type: array + generateName: + description: "GenerateName is an optional prefix, used by the server, + to generate a unique name ONLY IF the Name field has not been provided. + If this field is used, the name returned to the client will be different + than the name passed. This value will also be combined with a unique + suffix. The provided value has the same validation rules as the Name + field, and may be truncated by the length of the suffix required to + make the value unique on the server. \n If this field is specified + and the generated name exists, the server will NOT return a 409 - + instead, it will either return 201 Created or 500 with Reason ServerTimeout + indicating a unique name could not be found in the time allotted, + and the client should retry (optionally after the time indicated in + the Retry-After header). \n Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency" + type: string + generation: + description: A sequence number representing a specific generation of + the desired state. Populated by the system. Read-only. + format: int64 + type: integer + initializers: + description: "An initializer is a controller which enforces some system + invariant at object creation time. This field is a list of initializers + that have not yet acted on this object. If nil or empty, this object + has been completely initialized. Otherwise, the object is considered + uninitialized and is hidden (in list/watch and get calls) from clients + that haven't explicitly asked to observe uninitialized objects. \n + When an object is created, the system will populate this list with + the current set of initializers. Only privileged users may set or + modify this list. Once it is empty, it may not be modified further + by any user. \n DEPRECATED - initializers are an alpha field and will + be removed in v1.15." + properties: + pending: + description: Pending is a list of initializers that must execute + in order before this object is visible. When the last pending + initializer is removed, and no failing result is set, the initializers + struct will be set to nil and the object is considered as initialized + and visible to all clients. + items: + description: Initializer is information about an initializer that + has not yet completed. + properties: + name: + description: name of the process that is responsible for initializing + this object. + type: string + required: + - name + type: object + type: array + result: + description: If result is set with the Failure field, the object + will be persisted to storage and then deleted, ensuring that other + clients can observe the deletion. + 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/api-conventions.md#resources' + type: string + code: + description: Suggested HTTP return code for this status, 0 if + not set. + format: int32 + type: integer + details: + description: Extended data associated with the reason. Each + reason may define its own extended details. This field is + optional and the data returned is not guaranteed to conform + to any schema except that defined by the reason type. + properties: + causes: + description: The Causes array includes more details associated + with the StatusReason failure. Not all StatusReasons may + provide detailed causes. + items: + description: StatusCause provides more information about + an api.Status failure, including cases when multiple + errors are encountered. + properties: + field: + description: "The field of the resource that has caused + this error, as named by its JSON serialization. + May include dot and postfix notation for nested + attributes. Arrays are zero-indexed. Fields may + appear more than once in an array of causes due + to fields having multiple errors. Optional. \n Examples: + \ \"name\" - the field \"name\" on the current + resource \"items[0].name\" - the field \"name\" + on the first array entry in \"items\"" + type: string + message: + description: A human-readable description of the cause + of the error. This field may be presented as-is + to a reader. + type: string + reason: + description: A machine-readable description of the + cause of the error. If this value is empty there + is no information available. + type: string + type: object + type: array + group: + description: The group attribute of the resource associated + with the status StatusReason. + type: string + kind: + description: 'The kind attribute of the resource associated + with the status StatusReason. On some operations may differ + from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: The name attribute of the resource associated + with the status StatusReason (when there is a single name + which can be described). + type: string + retryAfterSeconds: + description: If specified, the time in seconds before the + operation should be retried. Some errors may indicate + the client must take an alternate action - for those errors + this field may indicate how long to wait before taking + the alternate action. + format: int32 + type: integer + uid: + description: 'UID of the resource. (when there is a single + resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + type: object + 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/api-conventions.md#types-kinds' + type: string + message: + description: A human-readable description of the status of this + operation. + type: string + metadata: + description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + properties: + continue: + description: continue may be set if the user set a limit + on the number of items returned, and indicates that the + server has more data available. The value is opaque and + may be used to issue another request to the endpoint that + served this list to retrieve the next set of available + objects. Continuing a consistent list may not be possible + if the server configuration has changed or more than a + few minutes have passed. The resourceVersion field returned + when using this continue value will be identical to the + value in the first response, unless you have received + this token from an error message. + type: string + resourceVersion: + description: 'String that identifies the server''s internal + version of this object that can be used by clients to + determine when objects have changed. Value must be treated + as opaque by clients and passed unmodified back to the + server. Populated by the system. Read-only. More info: + https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' + type: string + selfLink: + description: selfLink is a URL representing this object. + Populated by the system. Read-only. + type: string + type: object + reason: + description: A machine-readable description of why this operation + is in the "Failure" status. If this value is empty there is + no information available. A Reason clarifies an HTTP status + code but does not override it. + type: string + status: + description: 'Status of the operation. One of: "Success" or + "Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status' + type: string + type: object + required: + - pending + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can be used to organize + and categorize (scope and select) objects. May match selectors of + replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + managedFields: + description: "ManagedFields maps workflow-id and version to the set + of fields that are managed by that workflow. This is mostly for internal + housekeeping, and users typically shouldn't need to set or understand + this field. A workflow can be the user's name, a controller's name, + or the name of a specific apply path like \"ci-cd\". The set of fields + is always in the version that the workflow used when modifying the + object. \n This field is alpha and can be changed or removed without + notice." + items: + description: ManagedFieldsEntry is a workflow-id, a FieldSet and the + group version of the resource that the fieldset applies to. + properties: + apiVersion: + description: APIVersion defines the version of this resource that + this field set applies to. The format is "group/version" just + like the top-level APIVersion field. It is necessary to track + the version of a field set because it cannot be automatically + converted. + type: string + fields: + additionalProperties: true + description: Fields identifies a set of fields. + type: object + manager: + description: Manager is an identifier of the workflow managing + these fields. + type: string + operation: + description: Operation is the type of operation which lead to + this ManagedFieldsEntry being created. The only valid values + for this field are 'Apply' and 'Update'. + type: string + time: + description: Time is timestamp of when these fields were set. + It should always be empty if Operation is 'Apply' + format: date-time + type: string + type: object + type: array + name: + description: 'Name must be unique within a namespace. Is required when + creating resources, although some resources may allow a client to + request the generation of an appropriate name automatically. Name + is primarily intended for creation idempotence and configuration definition. + Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: "Namespace defines the space within each name must be unique. + An empty namespace is equivalent to the \"default\" namespace, but + \"default\" is the canonical representation. Not all objects are required + to be scoped to a namespace - the value of this field for those objects + will be empty. \n Must be a DNS_LABEL. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/namespaces" + type: string + ownerReferences: + description: List of objects depended by this object. If ALL objects + in the list have been deleted, this object will be garbage collected. + If this object is managed by a controller, then an entry in this list + will point to this controller, with the controller field set to true. + There cannot be more than one managing controller. + items: + description: OwnerReference contains enough information to let you + identify an owning object. An owning object must be in the same + namespace as the dependent, or be cluster-scoped, so there is no + namespace field. + properties: + apiVersion: + description: API version of the referent. + type: string + blockOwnerDeletion: + description: If true, AND if the owner has the "foregroundDeletion" + finalizer, then the owner cannot be deleted from the key-value + store until this reference is removed. Defaults to false. To + set this field, a user needs "delete" permission of the owner, + otherwise 422 (Unprocessable Entity) will be returned. + type: boolean + controller: + description: If true, this reference points to the managing controller. + type: boolean + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + uid: + description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + required: + - apiVersion + - kind + - name + - uid + type: object + type: array + resourceVersion: + description: "An opaque value that represents the internal version of + this object that can be used by clients to determine when objects + have changed. May be used for optimistic concurrency, change detection, + and the watch operation on a resource or set of resources. Clients + must treat these values as opaque and passed unmodified back to the + server. They may only be valid for a particular resource or set of + resources. \n Populated by the system. Read-only. Value must be treated + as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency" + type: string + selfLink: + description: SelfLink is a URL representing this object. Populated by + the system. Read-only. + type: string + uid: + description: "UID is the unique in time and space value for this object. + It is typically generated by the server on successful creation of + a resource and is not allowed to change on PUT operations. \n Populated + by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids" + type: string type: object spec: description: EventhubNamespaceSpec defines the desired state of EventhubNamespace diff --git a/config/crd/bases/azure.microsoft.com_eventhubs.yaml b/config/crd/bases/azure.microsoft.com_eventhubs.yaml index f8ba91fab2b..76c5fd857ec 100644 --- a/config/crd/bases/azure.microsoft.com_eventhubs.yaml +++ b/config/crd/bases/azure.microsoft.com_eventhubs.yaml @@ -26,6 +26,375 @@ spec: submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' type: string metadata: + description: ObjectMeta is metadata that all persisted resources must have, + which includes all objects users must create. + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. They are not queryable and should be preserved + when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + clusterName: + description: The name of the cluster which the object belongs to. This + is used to distinguish resources with same name and namespace in different + clusters. This field is not set anywhere right now and apiserver is + going to ignore it if set in create or update request. + type: string + creationTimestamp: + description: "CreationTimestamp is a timestamp representing the server + time when this object was created. It is not guaranteed to be set + in happens-before order across separate operations. Clients may not + set this value. It is represented in RFC3339 form and is in UTC. \n + Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + deletionGracePeriodSeconds: + description: Number of seconds allowed for this object to gracefully + terminate before it will be removed from the system. Only set when + deletionTimestamp is also set. May only be shortened. Read-only. + format: int64 + type: integer + deletionTimestamp: + description: "DeletionTimestamp is RFC 3339 date and time at which this + resource will be deleted. This field is set by the server when a graceful + deletion is requested by the user, and is not directly settable by + a client. The resource is expected to be deleted (no longer visible + from resource lists, and not reachable by name) after the time in + this field, once the finalizers list is empty. As long as the finalizers + list contains items, deletion is blocked. Once the deletionTimestamp + is set, this value may not be unset or be set further into the future, + although it may be shortened or the resource may be deleted prior + to this time. For example, a user may request that a pod is deleted + in 30 seconds. The Kubelet will react by sending a graceful termination + signal to the containers in the pod. After that 30 seconds, the Kubelet + will send a hard termination signal (SIGKILL) to the container and + after cleanup, remove the pod from the API. In the presence of network + partitions, this object may still exist after this timestamp, until + an administrator or automated process can determine the resource is + fully terminated. If not set, graceful deletion of the object has + not been requested. \n Populated by the system when a graceful deletion + is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + finalizers: + description: Must be empty before the object is deleted from the registry. + Each entry is an identifier for the responsible component that will + remove the entry from the list. If the deletionTimestamp of the object + is non-nil, entries in this list can only be removed. + items: + type: string + type: array + generateName: + description: "GenerateName is an optional prefix, used by the server, + to generate a unique name ONLY IF the Name field has not been provided. + If this field is used, the name returned to the client will be different + than the name passed. This value will also be combined with a unique + suffix. The provided value has the same validation rules as the Name + field, and may be truncated by the length of the suffix required to + make the value unique on the server. \n If this field is specified + and the generated name exists, the server will NOT return a 409 - + instead, it will either return 201 Created or 500 with Reason ServerTimeout + indicating a unique name could not be found in the time allotted, + and the client should retry (optionally after the time indicated in + the Retry-After header). \n Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency" + type: string + generation: + description: A sequence number representing a specific generation of + the desired state. Populated by the system. Read-only. + format: int64 + type: integer + initializers: + description: "An initializer is a controller which enforces some system + invariant at object creation time. This field is a list of initializers + that have not yet acted on this object. If nil or empty, this object + has been completely initialized. Otherwise, the object is considered + uninitialized and is hidden (in list/watch and get calls) from clients + that haven't explicitly asked to observe uninitialized objects. \n + When an object is created, the system will populate this list with + the current set of initializers. Only privileged users may set or + modify this list. Once it is empty, it may not be modified further + by any user. \n DEPRECATED - initializers are an alpha field and will + be removed in v1.15." + properties: + pending: + description: Pending is a list of initializers that must execute + in order before this object is visible. When the last pending + initializer is removed, and no failing result is set, the initializers + struct will be set to nil and the object is considered as initialized + and visible to all clients. + items: + description: Initializer is information about an initializer that + has not yet completed. + properties: + name: + description: name of the process that is responsible for initializing + this object. + type: string + required: + - name + type: object + type: array + result: + description: If result is set with the Failure field, the object + will be persisted to storage and then deleted, ensuring that other + clients can observe the deletion. + 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/api-conventions.md#resources' + type: string + code: + description: Suggested HTTP return code for this status, 0 if + not set. + format: int32 + type: integer + details: + description: Extended data associated with the reason. Each + reason may define its own extended details. This field is + optional and the data returned is not guaranteed to conform + to any schema except that defined by the reason type. + properties: + causes: + description: The Causes array includes more details associated + with the StatusReason failure. Not all StatusReasons may + provide detailed causes. + items: + description: StatusCause provides more information about + an api.Status failure, including cases when multiple + errors are encountered. + properties: + field: + description: "The field of the resource that has caused + this error, as named by its JSON serialization. + May include dot and postfix notation for nested + attributes. Arrays are zero-indexed. Fields may + appear more than once in an array of causes due + to fields having multiple errors. Optional. \n Examples: + \ \"name\" - the field \"name\" on the current + resource \"items[0].name\" - the field \"name\" + on the first array entry in \"items\"" + type: string + message: + description: A human-readable description of the cause + of the error. This field may be presented as-is + to a reader. + type: string + reason: + description: A machine-readable description of the + cause of the error. If this value is empty there + is no information available. + type: string + type: object + type: array + group: + description: The group attribute of the resource associated + with the status StatusReason. + type: string + kind: + description: 'The kind attribute of the resource associated + with the status StatusReason. On some operations may differ + from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: The name attribute of the resource associated + with the status StatusReason (when there is a single name + which can be described). + type: string + retryAfterSeconds: + description: If specified, the time in seconds before the + operation should be retried. Some errors may indicate + the client must take an alternate action - for those errors + this field may indicate how long to wait before taking + the alternate action. + format: int32 + type: integer + uid: + description: 'UID of the resource. (when there is a single + resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + type: object + 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/api-conventions.md#types-kinds' + type: string + message: + description: A human-readable description of the status of this + operation. + type: string + metadata: + description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + properties: + continue: + description: continue may be set if the user set a limit + on the number of items returned, and indicates that the + server has more data available. The value is opaque and + may be used to issue another request to the endpoint that + served this list to retrieve the next set of available + objects. Continuing a consistent list may not be possible + if the server configuration has changed or more than a + few minutes have passed. The resourceVersion field returned + when using this continue value will be identical to the + value in the first response, unless you have received + this token from an error message. + type: string + resourceVersion: + description: 'String that identifies the server''s internal + version of this object that can be used by clients to + determine when objects have changed. Value must be treated + as opaque by clients and passed unmodified back to the + server. Populated by the system. Read-only. More info: + https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' + type: string + selfLink: + description: selfLink is a URL representing this object. + Populated by the system. Read-only. + type: string + type: object + reason: + description: A machine-readable description of why this operation + is in the "Failure" status. If this value is empty there is + no information available. A Reason clarifies an HTTP status + code but does not override it. + type: string + status: + description: 'Status of the operation. One of: "Success" or + "Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status' + type: string + type: object + required: + - pending + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can be used to organize + and categorize (scope and select) objects. May match selectors of + replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + managedFields: + description: "ManagedFields maps workflow-id and version to the set + of fields that are managed by that workflow. This is mostly for internal + housekeeping, and users typically shouldn't need to set or understand + this field. A workflow can be the user's name, a controller's name, + or the name of a specific apply path like \"ci-cd\". The set of fields + is always in the version that the workflow used when modifying the + object. \n This field is alpha and can be changed or removed without + notice." + items: + description: ManagedFieldsEntry is a workflow-id, a FieldSet and the + group version of the resource that the fieldset applies to. + properties: + apiVersion: + description: APIVersion defines the version of this resource that + this field set applies to. The format is "group/version" just + like the top-level APIVersion field. It is necessary to track + the version of a field set because it cannot be automatically + converted. + type: string + fields: + additionalProperties: true + description: Fields identifies a set of fields. + type: object + manager: + description: Manager is an identifier of the workflow managing + these fields. + type: string + operation: + description: Operation is the type of operation which lead to + this ManagedFieldsEntry being created. The only valid values + for this field are 'Apply' and 'Update'. + type: string + time: + description: Time is timestamp of when these fields were set. + It should always be empty if Operation is 'Apply' + format: date-time + type: string + type: object + type: array + name: + description: 'Name must be unique within a namespace. Is required when + creating resources, although some resources may allow a client to + request the generation of an appropriate name automatically. Name + is primarily intended for creation idempotence and configuration definition. + Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: "Namespace defines the space within each name must be unique. + An empty namespace is equivalent to the \"default\" namespace, but + \"default\" is the canonical representation. Not all objects are required + to be scoped to a namespace - the value of this field for those objects + will be empty. \n Must be a DNS_LABEL. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/namespaces" + type: string + ownerReferences: + description: List of objects depended by this object. If ALL objects + in the list have been deleted, this object will be garbage collected. + If this object is managed by a controller, then an entry in this list + will point to this controller, with the controller field set to true. + There cannot be more than one managing controller. + items: + description: OwnerReference contains enough information to let you + identify an owning object. An owning object must be in the same + namespace as the dependent, or be cluster-scoped, so there is no + namespace field. + properties: + apiVersion: + description: API version of the referent. + type: string + blockOwnerDeletion: + description: If true, AND if the owner has the "foregroundDeletion" + finalizer, then the owner cannot be deleted from the key-value + store until this reference is removed. Defaults to false. To + set this field, a user needs "delete" permission of the owner, + otherwise 422 (Unprocessable Entity) will be returned. + type: boolean + controller: + description: If true, this reference points to the managing controller. + type: boolean + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + uid: + description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + required: + - apiVersion + - kind + - name + - uid + type: object + type: array + resourceVersion: + description: "An opaque value that represents the internal version of + this object that can be used by clients to determine when objects + have changed. May be used for optimistic concurrency, change detection, + and the watch operation on a resource or set of resources. Clients + must treat these values as opaque and passed unmodified back to the + server. They may only be valid for a particular resource or set of + resources. \n Populated by the system. Read-only. Value must be treated + as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency" + type: string + selfLink: + description: SelfLink is a URL representing this object. Populated by + the system. Read-only. + type: string + uid: + description: "UID is the unique in time and space value for this object. + It is typically generated by the server on successful creation of + a resource and is not allowed to change on PUT operations. \n Populated + by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids" + type: string type: object spec: description: EventhubSpec defines the desired state of Eventhub @@ -56,11 +425,15 @@ spec: description: MessageRetentionInDays - Number of days to retain the events for this Event Hub, value should be 1 to 7 days format: int32 + maximum: 7 + minimum: 1 type: integer partitioncount: description: PartitionCount - Number of partitions created for the Event Hub, allowed values are from 1 to 32 partitions. format: int32 + maximum: 32 + minimum: 1 type: integer type: object resourcegroup: diff --git a/config/crd/bases/azure.microsoft.com_keyvaults.yaml b/config/crd/bases/azure.microsoft.com_keyvaults.yaml index 1b176033709..2391eb8aff7 100644 --- a/config/crd/bases/azure.microsoft.com_keyvaults.yaml +++ b/config/crd/bases/azure.microsoft.com_keyvaults.yaml @@ -26,6 +26,375 @@ spec: submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' type: string metadata: + description: ObjectMeta is metadata that all persisted resources must have, + which includes all objects users must create. + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. They are not queryable and should be preserved + when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + clusterName: + description: The name of the cluster which the object belongs to. This + is used to distinguish resources with same name and namespace in different + clusters. This field is not set anywhere right now and apiserver is + going to ignore it if set in create or update request. + type: string + creationTimestamp: + description: "CreationTimestamp is a timestamp representing the server + time when this object was created. It is not guaranteed to be set + in happens-before order across separate operations. Clients may not + set this value. It is represented in RFC3339 form and is in UTC. \n + Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + deletionGracePeriodSeconds: + description: Number of seconds allowed for this object to gracefully + terminate before it will be removed from the system. Only set when + deletionTimestamp is also set. May only be shortened. Read-only. + format: int64 + type: integer + deletionTimestamp: + description: "DeletionTimestamp is RFC 3339 date and time at which this + resource will be deleted. This field is set by the server when a graceful + deletion is requested by the user, and is not directly settable by + a client. The resource is expected to be deleted (no longer visible + from resource lists, and not reachable by name) after the time in + this field, once the finalizers list is empty. As long as the finalizers + list contains items, deletion is blocked. Once the deletionTimestamp + is set, this value may not be unset or be set further into the future, + although it may be shortened or the resource may be deleted prior + to this time. For example, a user may request that a pod is deleted + in 30 seconds. The Kubelet will react by sending a graceful termination + signal to the containers in the pod. After that 30 seconds, the Kubelet + will send a hard termination signal (SIGKILL) to the container and + after cleanup, remove the pod from the API. In the presence of network + partitions, this object may still exist after this timestamp, until + an administrator or automated process can determine the resource is + fully terminated. If not set, graceful deletion of the object has + not been requested. \n Populated by the system when a graceful deletion + is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + finalizers: + description: Must be empty before the object is deleted from the registry. + Each entry is an identifier for the responsible component that will + remove the entry from the list. If the deletionTimestamp of the object + is non-nil, entries in this list can only be removed. + items: + type: string + type: array + generateName: + description: "GenerateName is an optional prefix, used by the server, + to generate a unique name ONLY IF the Name field has not been provided. + If this field is used, the name returned to the client will be different + than the name passed. This value will also be combined with a unique + suffix. The provided value has the same validation rules as the Name + field, and may be truncated by the length of the suffix required to + make the value unique on the server. \n If this field is specified + and the generated name exists, the server will NOT return a 409 - + instead, it will either return 201 Created or 500 with Reason ServerTimeout + indicating a unique name could not be found in the time allotted, + and the client should retry (optionally after the time indicated in + the Retry-After header). \n Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency" + type: string + generation: + description: A sequence number representing a specific generation of + the desired state. Populated by the system. Read-only. + format: int64 + type: integer + initializers: + description: "An initializer is a controller which enforces some system + invariant at object creation time. This field is a list of initializers + that have not yet acted on this object. If nil or empty, this object + has been completely initialized. Otherwise, the object is considered + uninitialized and is hidden (in list/watch and get calls) from clients + that haven't explicitly asked to observe uninitialized objects. \n + When an object is created, the system will populate this list with + the current set of initializers. Only privileged users may set or + modify this list. Once it is empty, it may not be modified further + by any user. \n DEPRECATED - initializers are an alpha field and will + be removed in v1.15." + properties: + pending: + description: Pending is a list of initializers that must execute + in order before this object is visible. When the last pending + initializer is removed, and no failing result is set, the initializers + struct will be set to nil and the object is considered as initialized + and visible to all clients. + items: + description: Initializer is information about an initializer that + has not yet completed. + properties: + name: + description: name of the process that is responsible for initializing + this object. + type: string + required: + - name + type: object + type: array + result: + description: If result is set with the Failure field, the object + will be persisted to storage and then deleted, ensuring that other + clients can observe the deletion. + 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/api-conventions.md#resources' + type: string + code: + description: Suggested HTTP return code for this status, 0 if + not set. + format: int32 + type: integer + details: + description: Extended data associated with the reason. Each + reason may define its own extended details. This field is + optional and the data returned is not guaranteed to conform + to any schema except that defined by the reason type. + properties: + causes: + description: The Causes array includes more details associated + with the StatusReason failure. Not all StatusReasons may + provide detailed causes. + items: + description: StatusCause provides more information about + an api.Status failure, including cases when multiple + errors are encountered. + properties: + field: + description: "The field of the resource that has caused + this error, as named by its JSON serialization. + May include dot and postfix notation for nested + attributes. Arrays are zero-indexed. Fields may + appear more than once in an array of causes due + to fields having multiple errors. Optional. \n Examples: + \ \"name\" - the field \"name\" on the current + resource \"items[0].name\" - the field \"name\" + on the first array entry in \"items\"" + type: string + message: + description: A human-readable description of the cause + of the error. This field may be presented as-is + to a reader. + type: string + reason: + description: A machine-readable description of the + cause of the error. If this value is empty there + is no information available. + type: string + type: object + type: array + group: + description: The group attribute of the resource associated + with the status StatusReason. + type: string + kind: + description: 'The kind attribute of the resource associated + with the status StatusReason. On some operations may differ + from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: The name attribute of the resource associated + with the status StatusReason (when there is a single name + which can be described). + type: string + retryAfterSeconds: + description: If specified, the time in seconds before the + operation should be retried. Some errors may indicate + the client must take an alternate action - for those errors + this field may indicate how long to wait before taking + the alternate action. + format: int32 + type: integer + uid: + description: 'UID of the resource. (when there is a single + resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + type: object + 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/api-conventions.md#types-kinds' + type: string + message: + description: A human-readable description of the status of this + operation. + type: string + metadata: + description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + properties: + continue: + description: continue may be set if the user set a limit + on the number of items returned, and indicates that the + server has more data available. The value is opaque and + may be used to issue another request to the endpoint that + served this list to retrieve the next set of available + objects. Continuing a consistent list may not be possible + if the server configuration has changed or more than a + few minutes have passed. The resourceVersion field returned + when using this continue value will be identical to the + value in the first response, unless you have received + this token from an error message. + type: string + resourceVersion: + description: 'String that identifies the server''s internal + version of this object that can be used by clients to + determine when objects have changed. Value must be treated + as opaque by clients and passed unmodified back to the + server. Populated by the system. Read-only. More info: + https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' + type: string + selfLink: + description: selfLink is a URL representing this object. + Populated by the system. Read-only. + type: string + type: object + reason: + description: A machine-readable description of why this operation + is in the "Failure" status. If this value is empty there is + no information available. A Reason clarifies an HTTP status + code but does not override it. + type: string + status: + description: 'Status of the operation. One of: "Success" or + "Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status' + type: string + type: object + required: + - pending + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can be used to organize + and categorize (scope and select) objects. May match selectors of + replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + managedFields: + description: "ManagedFields maps workflow-id and version to the set + of fields that are managed by that workflow. This is mostly for internal + housekeeping, and users typically shouldn't need to set or understand + this field. A workflow can be the user's name, a controller's name, + or the name of a specific apply path like \"ci-cd\". The set of fields + is always in the version that the workflow used when modifying the + object. \n This field is alpha and can be changed or removed without + notice." + items: + description: ManagedFieldsEntry is a workflow-id, a FieldSet and the + group version of the resource that the fieldset applies to. + properties: + apiVersion: + description: APIVersion defines the version of this resource that + this field set applies to. The format is "group/version" just + like the top-level APIVersion field. It is necessary to track + the version of a field set because it cannot be automatically + converted. + type: string + fields: + additionalProperties: true + description: Fields identifies a set of fields. + type: object + manager: + description: Manager is an identifier of the workflow managing + these fields. + type: string + operation: + description: Operation is the type of operation which lead to + this ManagedFieldsEntry being created. The only valid values + for this field are 'Apply' and 'Update'. + type: string + time: + description: Time is timestamp of when these fields were set. + It should always be empty if Operation is 'Apply' + format: date-time + type: string + type: object + type: array + name: + description: 'Name must be unique within a namespace. Is required when + creating resources, although some resources may allow a client to + request the generation of an appropriate name automatically. Name + is primarily intended for creation idempotence and configuration definition. + Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: "Namespace defines the space within each name must be unique. + An empty namespace is equivalent to the \"default\" namespace, but + \"default\" is the canonical representation. Not all objects are required + to be scoped to a namespace - the value of this field for those objects + will be empty. \n Must be a DNS_LABEL. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/namespaces" + type: string + ownerReferences: + description: List of objects depended by this object. If ALL objects + in the list have been deleted, this object will be garbage collected. + If this object is managed by a controller, then an entry in this list + will point to this controller, with the controller field set to true. + There cannot be more than one managing controller. + items: + description: OwnerReference contains enough information to let you + identify an owning object. An owning object must be in the same + namespace as the dependent, or be cluster-scoped, so there is no + namespace field. + properties: + apiVersion: + description: API version of the referent. + type: string + blockOwnerDeletion: + description: If true, AND if the owner has the "foregroundDeletion" + finalizer, then the owner cannot be deleted from the key-value + store until this reference is removed. Defaults to false. To + set this field, a user needs "delete" permission of the owner, + otherwise 422 (Unprocessable Entity) will be returned. + type: boolean + controller: + description: If true, this reference points to the managing controller. + type: boolean + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + uid: + description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + required: + - apiVersion + - kind + - name + - uid + type: object + type: array + resourceVersion: + description: "An opaque value that represents the internal version of + this object that can be used by clients to determine when objects + have changed. May be used for optimistic concurrency, change detection, + and the watch operation on a resource or set of resources. Clients + must treat these values as opaque and passed unmodified back to the + server. They may only be valid for a particular resource or set of + resources. \n Populated by the system. Read-only. Value must be treated + as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency" + type: string + selfLink: + description: SelfLink is a URL representing this object. Populated by + the system. Read-only. + type: string + uid: + description: "UID is the unique in time and space value for this object. + It is typically generated by the server on successful creation of + a resource and is not allowed to change on PUT operations. \n Populated + by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids" + type: string type: object spec: description: KeyVaultSpec defines the desired state of KeyVault @@ -47,7 +416,6 @@ spec: type: boolean type: object type: object - version: v1 versions: - name: v1 served: true diff --git a/config/crd/bases/azure.microsoft.com_resourcegroups.yaml b/config/crd/bases/azure.microsoft.com_resourcegroups.yaml index dedd874e10e..34ed824ad8f 100644 --- a/config/crd/bases/azure.microsoft.com_resourcegroups.yaml +++ b/config/crd/bases/azure.microsoft.com_resourcegroups.yaml @@ -28,6 +28,375 @@ spec: submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' type: string metadata: + description: ObjectMeta is metadata that all persisted resources must have, + which includes all objects users must create. + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. They are not queryable and should be preserved + when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + clusterName: + description: The name of the cluster which the object belongs to. This + is used to distinguish resources with same name and namespace in different + clusters. This field is not set anywhere right now and apiserver is + going to ignore it if set in create or update request. + type: string + creationTimestamp: + description: "CreationTimestamp is a timestamp representing the server + time when this object was created. It is not guaranteed to be set + in happens-before order across separate operations. Clients may not + set this value. It is represented in RFC3339 form and is in UTC. \n + Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + deletionGracePeriodSeconds: + description: Number of seconds allowed for this object to gracefully + terminate before it will be removed from the system. Only set when + deletionTimestamp is also set. May only be shortened. Read-only. + format: int64 + type: integer + deletionTimestamp: + description: "DeletionTimestamp is RFC 3339 date and time at which this + resource will be deleted. This field is set by the server when a graceful + deletion is requested by the user, and is not directly settable by + a client. The resource is expected to be deleted (no longer visible + from resource lists, and not reachable by name) after the time in + this field, once the finalizers list is empty. As long as the finalizers + list contains items, deletion is blocked. Once the deletionTimestamp + is set, this value may not be unset or be set further into the future, + although it may be shortened or the resource may be deleted prior + to this time. For example, a user may request that a pod is deleted + in 30 seconds. The Kubelet will react by sending a graceful termination + signal to the containers in the pod. After that 30 seconds, the Kubelet + will send a hard termination signal (SIGKILL) to the container and + after cleanup, remove the pod from the API. In the presence of network + partitions, this object may still exist after this timestamp, until + an administrator or automated process can determine the resource is + fully terminated. If not set, graceful deletion of the object has + not been requested. \n Populated by the system when a graceful deletion + is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + finalizers: + description: Must be empty before the object is deleted from the registry. + Each entry is an identifier for the responsible component that will + remove the entry from the list. If the deletionTimestamp of the object + is non-nil, entries in this list can only be removed. + items: + type: string + type: array + generateName: + description: "GenerateName is an optional prefix, used by the server, + to generate a unique name ONLY IF the Name field has not been provided. + If this field is used, the name returned to the client will be different + than the name passed. This value will also be combined with a unique + suffix. The provided value has the same validation rules as the Name + field, and may be truncated by the length of the suffix required to + make the value unique on the server. \n If this field is specified + and the generated name exists, the server will NOT return a 409 - + instead, it will either return 201 Created or 500 with Reason ServerTimeout + indicating a unique name could not be found in the time allotted, + and the client should retry (optionally after the time indicated in + the Retry-After header). \n Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency" + type: string + generation: + description: A sequence number representing a specific generation of + the desired state. Populated by the system. Read-only. + format: int64 + type: integer + initializers: + description: "An initializer is a controller which enforces some system + invariant at object creation time. This field is a list of initializers + that have not yet acted on this object. If nil or empty, this object + has been completely initialized. Otherwise, the object is considered + uninitialized and is hidden (in list/watch and get calls) from clients + that haven't explicitly asked to observe uninitialized objects. \n + When an object is created, the system will populate this list with + the current set of initializers. Only privileged users may set or + modify this list. Once it is empty, it may not be modified further + by any user. \n DEPRECATED - initializers are an alpha field and will + be removed in v1.15." + properties: + pending: + description: Pending is a list of initializers that must execute + in order before this object is visible. When the last pending + initializer is removed, and no failing result is set, the initializers + struct will be set to nil and the object is considered as initialized + and visible to all clients. + items: + description: Initializer is information about an initializer that + has not yet completed. + properties: + name: + description: name of the process that is responsible for initializing + this object. + type: string + required: + - name + type: object + type: array + result: + description: If result is set with the Failure field, the object + will be persisted to storage and then deleted, ensuring that other + clients can observe the deletion. + 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/api-conventions.md#resources' + type: string + code: + description: Suggested HTTP return code for this status, 0 if + not set. + format: int32 + type: integer + details: + description: Extended data associated with the reason. Each + reason may define its own extended details. This field is + optional and the data returned is not guaranteed to conform + to any schema except that defined by the reason type. + properties: + causes: + description: The Causes array includes more details associated + with the StatusReason failure. Not all StatusReasons may + provide detailed causes. + items: + description: StatusCause provides more information about + an api.Status failure, including cases when multiple + errors are encountered. + properties: + field: + description: "The field of the resource that has caused + this error, as named by its JSON serialization. + May include dot and postfix notation for nested + attributes. Arrays are zero-indexed. Fields may + appear more than once in an array of causes due + to fields having multiple errors. Optional. \n Examples: + \ \"name\" - the field \"name\" on the current + resource \"items[0].name\" - the field \"name\" + on the first array entry in \"items\"" + type: string + message: + description: A human-readable description of the cause + of the error. This field may be presented as-is + to a reader. + type: string + reason: + description: A machine-readable description of the + cause of the error. If this value is empty there + is no information available. + type: string + type: object + type: array + group: + description: The group attribute of the resource associated + with the status StatusReason. + type: string + kind: + description: 'The kind attribute of the resource associated + with the status StatusReason. On some operations may differ + from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: The name attribute of the resource associated + with the status StatusReason (when there is a single name + which can be described). + type: string + retryAfterSeconds: + description: If specified, the time in seconds before the + operation should be retried. Some errors may indicate + the client must take an alternate action - for those errors + this field may indicate how long to wait before taking + the alternate action. + format: int32 + type: integer + uid: + description: 'UID of the resource. (when there is a single + resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + type: object + 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/api-conventions.md#types-kinds' + type: string + message: + description: A human-readable description of the status of this + operation. + type: string + metadata: + description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + properties: + continue: + description: continue may be set if the user set a limit + on the number of items returned, and indicates that the + server has more data available. The value is opaque and + may be used to issue another request to the endpoint that + served this list to retrieve the next set of available + objects. Continuing a consistent list may not be possible + if the server configuration has changed or more than a + few minutes have passed. The resourceVersion field returned + when using this continue value will be identical to the + value in the first response, unless you have received + this token from an error message. + type: string + resourceVersion: + description: 'String that identifies the server''s internal + version of this object that can be used by clients to + determine when objects have changed. Value must be treated + as opaque by clients and passed unmodified back to the + server. Populated by the system. Read-only. More info: + https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' + type: string + selfLink: + description: selfLink is a URL representing this object. + Populated by the system. Read-only. + type: string + type: object + reason: + description: A machine-readable description of why this operation + is in the "Failure" status. If this value is empty there is + no information available. A Reason clarifies an HTTP status + code but does not override it. + type: string + status: + description: 'Status of the operation. One of: "Success" or + "Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status' + type: string + type: object + required: + - pending + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can be used to organize + and categorize (scope and select) objects. May match selectors of + replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + managedFields: + description: "ManagedFields maps workflow-id and version to the set + of fields that are managed by that workflow. This is mostly for internal + housekeeping, and users typically shouldn't need to set or understand + this field. A workflow can be the user's name, a controller's name, + or the name of a specific apply path like \"ci-cd\". The set of fields + is always in the version that the workflow used when modifying the + object. \n This field is alpha and can be changed or removed without + notice." + items: + description: ManagedFieldsEntry is a workflow-id, a FieldSet and the + group version of the resource that the fieldset applies to. + properties: + apiVersion: + description: APIVersion defines the version of this resource that + this field set applies to. The format is "group/version" just + like the top-level APIVersion field. It is necessary to track + the version of a field set because it cannot be automatically + converted. + type: string + fields: + additionalProperties: true + description: Fields identifies a set of fields. + type: object + manager: + description: Manager is an identifier of the workflow managing + these fields. + type: string + operation: + description: Operation is the type of operation which lead to + this ManagedFieldsEntry being created. The only valid values + for this field are 'Apply' and 'Update'. + type: string + time: + description: Time is timestamp of when these fields were set. + It should always be empty if Operation is 'Apply' + format: date-time + type: string + type: object + type: array + name: + description: 'Name must be unique within a namespace. Is required when + creating resources, although some resources may allow a client to + request the generation of an appropriate name automatically. Name + is primarily intended for creation idempotence and configuration definition. + Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: "Namespace defines the space within each name must be unique. + An empty namespace is equivalent to the \"default\" namespace, but + \"default\" is the canonical representation. Not all objects are required + to be scoped to a namespace - the value of this field for those objects + will be empty. \n Must be a DNS_LABEL. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/namespaces" + type: string + ownerReferences: + description: List of objects depended by this object. If ALL objects + in the list have been deleted, this object will be garbage collected. + If this object is managed by a controller, then an entry in this list + will point to this controller, with the controller field set to true. + There cannot be more than one managing controller. + items: + description: OwnerReference contains enough information to let you + identify an owning object. An owning object must be in the same + namespace as the dependent, or be cluster-scoped, so there is no + namespace field. + properties: + apiVersion: + description: API version of the referent. + type: string + blockOwnerDeletion: + description: If true, AND if the owner has the "foregroundDeletion" + finalizer, then the owner cannot be deleted from the key-value + store until this reference is removed. Defaults to false. To + set this field, a user needs "delete" permission of the owner, + otherwise 422 (Unprocessable Entity) will be returned. + type: boolean + controller: + description: If true, this reference points to the managing controller. + type: boolean + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + uid: + description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + required: + - apiVersion + - kind + - name + - uid + type: object + type: array + resourceVersion: + description: "An opaque value that represents the internal version of + this object that can be used by clients to determine when objects + have changed. May be used for optimistic concurrency, change detection, + and the watch operation on a resource or set of resources. Clients + must treat these values as opaque and passed unmodified back to the + server. They may only be valid for a particular resource or set of + resources. \n Populated by the system. Read-only. Value must be treated + as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency" + type: string + selfLink: + description: SelfLink is a URL representing this object. Populated by + the system. Read-only. + type: string + uid: + description: "UID is the unique in time and space value for this object. + It is typically generated by the server on successful creation of + a resource and is not allowed to change on PUT operations. \n Populated + by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids" + type: string type: object spec: description: ResourceGroupSpec defines the desired state of ResourceGroup diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 1e4332931bf..06e1e115558 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -21,7 +21,7 @@ bases: # [CERTMANAGER] To enable cert-manager, uncomment next line. 'WEBHOOK' components are required. - ../certmanager -patches: +patchesStrategicMerge: - manager_image_patch.yaml # Protect the /metrics endpoint by putting it behind auth. # Only one of manager_auth_proxy_patch.yaml and diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index 6e060cb4438..096ebb55bf1 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -8,7 +8,7 @@ spec: spec: containers: # Change the value of image field below to your controller image URL - - image: IMAGE_URL + - image: docker.io/eventhuboperator:JVtest name: manager env: - name: AZURE_CLIENT_ID diff --git a/config/default/manager_image_patch.yaml-e b/config/default/manager_image_patch.yaml-e new file mode 100644 index 00000000000..6e060cb4438 --- /dev/null +++ b/config/default/manager_image_patch.yaml-e @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + # Change the value of image field below to your controller image URL + - image: IMAGE_URL + name: manager + env: + - name: AZURE_CLIENT_ID + valueFrom: + secretKeyRef: + name: azureoperatorsettings + key: AZURE_CLIENT_ID + - name: AZURE_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: azureoperatorsettings + key: AZURE_CLIENT_SECRET + - name: AZURE_TENANT_ID + valueFrom: + secretKeyRef: + name: azureoperatorsettings + key: AZURE_TENANT_ID + - name: AZURE_SUBSCRIPTION_ID + valueFrom: + secretKeyRef: + name: azureoperatorsettings + key: AZURE_SUBSCRIPTION_ID + #requeue after time in seconds" + - name: REQUEUE_AFTER + value: "30" diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 76adc48e2e5..e7282543886 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -6,6 +6,13 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - watch - apiGroups: - apps resources: @@ -19,17 +26,21 @@ rules: - update - watch - apiGroups: - - apps + - azure.microsoft.com resources: - - deployments/status + - keyvaults verbs: + - create + - delete - get + - list - patch - update + - watch - apiGroups: - azure.microsoft.com resources: - - eventhubnamespaces + - resourcegroups verbs: - create - delete @@ -41,27 +52,27 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubnamespaces/status + - eventhubnamespaces verbs: + - create + - delete - get + - list - patch - update + - watch - apiGroups: - azure.microsoft.com resources: - - eventhubs + - eventhubnamespaces/status verbs: - - create - - delete - get - - list - patch - update - - watch - apiGroups: - azure.microsoft.com resources: - - eventhubs/status + - keyvaults/status verbs: - get - patch @@ -69,14 +80,15 @@ rules: - apiGroups: - azure.microsoft.com resources: - - events + - resourcegroups/status verbs: - - create + - get - patch + - update - apiGroups: - - azure.microsoft.com + - "" resources: - - keyvaults + - secrets verbs: - create - delete @@ -88,15 +100,14 @@ rules: - apiGroups: - azure.microsoft.com resources: - - keyvaults/status + - events verbs: - - get + - create - patch - - update - apiGroups: - azure.microsoft.com resources: - - resourcegroups + - eventhubs verbs: - create - delete @@ -108,27 +119,16 @@ rules: - apiGroups: - azure.microsoft.com resources: - - resourcegroups/status + - eventhubs/status verbs: - get - patch - update - apiGroups: - - "" - resources: - - events - verbs: - - create - - watch -- apiGroups: - - "" + - apps resources: - - secrets + - deployments/status verbs: - - create - - delete - get - - list - patch - update - - watch diff --git a/config/samples/azure_v1_keyvault.yaml b/config/samples/azure_v1_keyvault.yaml index 506cd180d4b..a835078ba42 100644 --- a/config/samples/azure_v1_keyvault.yaml +++ b/config/samples/azure_v1_keyvault.yaml @@ -1,7 +1,7 @@ apiVersion: azure.microsoft.com/v1 kind: KeyVault metadata: - name: ace-kv-sample + name: jan-kv-sample2 spec: - resourceGroup: ace-crd - location: westus2 + resourceGroup: Janani-testRG + location: westus diff --git a/config/samples/creator_v1_eventhub.yaml b/config/samples/creator_v1_eventhub.yaml index 299c60cfb1c..0c635ae9067 100644 --- a/config/samples/creator_v1_eventhub.yaml +++ b/config/samples/creator_v1_eventhub.yaml @@ -1,11 +1,11 @@ apiVersion: azure.microsoft.com/v1 kind: Eventhub metadata: - name: eventhub-sample-2307-09 + name: eventhub-janani-123 spec: location: "westus" - resourcegroup: "resourcegroup-sample-1907" - namespace: "eventhubnamespace-sample-1907" + resourcegroup: "jananitest1234" + namespace: "eventhubnamespace-janani-123" properties: messageretentionindays: 7 partitioncount: 1 diff --git a/config/samples/creator_v1_eventhubnamespace.yaml b/config/samples/creator_v1_eventhubnamespace.yaml index 5ee294bd385..a979cf06bfa 100644 --- a/config/samples/creator_v1_eventhubnamespace.yaml +++ b/config/samples/creator_v1_eventhubnamespace.yaml @@ -1,10 +1,10 @@ apiVersion: azure.microsoft.com/v1 kind: EventhubNamespace metadata: - name: eventhubnamespace-sample-6 + name: eventhubnamespace-janani-123 spec: location: "westus" - resourcegroup: "resourcegroup-sample-6" + resourcegroup: "jananitest1234" sku: name: "Standard" tier: "Standard" diff --git a/config/samples/creator_v1_resourcegroup copy.yaml b/config/samples/creator_v1_resourcegroup copy.yaml new file mode 100644 index 00000000000..90c1b9ca3d8 --- /dev/null +++ b/config/samples/creator_v1_resourcegroup copy.yaml @@ -0,0 +1,6 @@ +apiVersion: azure.microsoft.com/v1 +kind: ResourceGroup +metadata: + name: jananitest123 +spec: + location: "westus" diff --git a/config/samples/creator_v1_resourcegroup.yaml b/config/samples/creator_v1_resourcegroup.yaml index b9d42210d14..6af32c0feb7 100644 --- a/config/samples/creator_v1_resourcegroup.yaml +++ b/config/samples/creator_v1_resourcegroup.yaml @@ -1,6 +1,6 @@ apiVersion: azure.microsoft.com/v1 kind: ResourceGroup metadata: - name: resourcegroup-sample-6 + name: jananitest1234 spec: location: "westus" diff --git a/controllers/eventhub_controller.go b/controllers/eventhub_controller.go index 4ed5b266102..aa934949d3d 100644 --- a/controllers/eventhub_controller.go +++ b/controllers/eventhub_controller.go @@ -56,6 +56,7 @@ func ignoreNotFound(err error) error { // +kubebuilder:rbac:groups=azure.microsoft.com,resources=eventhubs,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=azure.microsoft.com,resources=eventhubs/status,verbs=get;update;patch +// Reconcile function does the main reconciliation loop of the operator func (r *EventhubReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { ctx := context.Background() log := r.Log.WithValues("eventhub", req.NamespacedName) diff --git a/controllers/eventhubnamespace_controller.go b/controllers/eventhubnamespace_controller.go index ccf5308273e..6a153d6dfe0 100644 --- a/controllers/eventhubnamespace_controller.go +++ b/controllers/eventhubnamespace_controller.go @@ -92,6 +92,7 @@ func (r *EventhubNamespaceReconciler) Reconcile(req ctrl.Request) (ctrl.Result, }, nil } +//SetupWithManager sets up the functions for the controller func (r *EventhubNamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&azurev1.EventhubNamespace{}). diff --git a/controllers/keyvault_controller.go b/controllers/keyvault_controller.go index aeb28187004..b24528ca3ab 100644 --- a/controllers/keyvault_controller.go +++ b/controllers/keyvault_controller.go @@ -22,9 +22,10 @@ import ( "strconv" "time" - "github.com/Azure/azure-service-operator/resourcemanager/keyvaults" azurev1 "github.com/Azure/azure-service-operator/api/v1" helpers "github.com/Azure/azure-service-operator/helpers" + "github.com/Azure/azure-service-operator/pkg/errhelp" + "github.com/Azure/azure-service-operator/resourcemanager/keyvaults" "github.com/go-logr/logr" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" @@ -36,26 +37,36 @@ const keyVaultFinalizerName = "keyvault.finalizers.azure.com" // KeyVaultReconciler reconciles a KeyVault object type KeyVaultReconciler struct { client.Client - Log logr.Logger - Recorder record.EventRecorder + Log logr.Logger + Recorder record.EventRecorder + RequeueTime time.Duration } // +kubebuilder:rbac:groups=azure.microsoft.com,resources=keyvaults,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=azure.microsoft.com,resources=keyvaults/status,verbs=get;update;patch +// Reconcile function runs the actual reconcilation loop of the controller func (r *KeyVaultReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { ctx := context.Background() log := r.Log.WithValues("keyvault", req.NamespacedName) var instance azurev1.KeyVault + + requeueAfter, err := strconv.Atoi(os.Getenv("REQUEUE_AFTER")) + if err != nil { + requeueAfter = 30 + } + if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { - log.Error(err, "unable to fetch KeyVault") + //log.Error(err, "unable to fetch KeyVault") + log.Info("Unable to fetch KeyVault", "err", err.Error()) return ctrl.Result{}, client.IgnoreNotFound(err) } if helpers.IsBeingDeleted(&instance) { if helpers.HasFinalizer(&instance, keyVaultFinalizerName) { if err := r.deleteExternal(&instance); err != nil { + log.Info("Delete KeyVault failed with ", err.Error()) return ctrl.Result{}, err } @@ -69,25 +80,26 @@ func (r *KeyVaultReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { if !helpers.HasFinalizer(&instance, keyVaultFinalizerName) { if err := r.addFinalizer(&instance); err != nil { + log.Info("Adding keyvault finalizer failed with ", err.Error()) return ctrl.Result{}, err } } if !instance.IsSubmitted() { if err := r.reconcileExternal(&instance); err != nil { + if errhelp.IsAsynchronousOperationNotComplete(err) || errhelp.IsGroupNotFound(err) { + log.Info("Requeuing as the async operation is not complete") + return ctrl.Result{ + Requeue: true, + RequeueAfter: time.Second * time.Duration(requeueAfter), + }, nil + } return ctrl.Result{}, fmt.Errorf("error reconciling keyvault in azure: %v", err) } return ctrl.Result{}, nil } - requeueAfter, err := strconv.Atoi(os.Getenv("REQUEUE_AFTER")) - if err != nil { - requeueAfter = 30 - } - - return ctrl.Result{ - RequeueAfter: time.Second * time.Duration(requeueAfter), - }, nil + return ctrl.Result{}, nil } func (r *KeyVaultReconciler) addFinalizer(instance *azurev1.KeyVault) error { @@ -108,13 +120,17 @@ func (r *KeyVaultReconciler) reconcileExternal(instance *azurev1.KeyVault) error // write information back to instance instance.Status.Provisioning = true - + if err := r.Update(ctx, instance); err != nil { r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") } - _, err := keyvaults.CreateVaultAndWait(ctx, groupName, name, location) - if err != nil { + _, err := keyvaults.CreateVault(ctx, groupName, name, location) + if err != nil { + if errhelp.IsAsynchronousOperationNotComplete(err) || errhelp.IsGroupNotFound(err) { + r.Recorder.Event(instance, "Normal", "Updated", name+" provisioning") + return err + } r.Recorder.Event(instance, "Warning", "Failed", "Couldn't create resource in azure") instance.Status.Provisioning = false errUpdate := r.Update(ctx, instance) @@ -127,7 +143,7 @@ func (r *KeyVaultReconciler) reconcileExternal(instance *azurev1.KeyVault) error instance.Status.Provisioning = false instance.Status.Provisioned = true - if err = r.Update(ctx, instance); err != nil { + if err = r.Update(ctx, instance); err != nil { r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") } @@ -147,6 +163,7 @@ func (r *KeyVaultReconciler) deleteExternal(instance *azurev1.KeyVault) error { return nil } +// SetupWithManager sets up the controller functions func (r *KeyVaultReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&azurev1.KeyVault{}). diff --git a/controllers/resourcegroup_controller.go b/controllers/resourcegroup_controller.go index 5d2b2a4b0cd..c0696744b75 100644 --- a/controllers/resourcegroup_controller.go +++ b/controllers/resourcegroup_controller.go @@ -43,6 +43,7 @@ type ResourceGroupReconciler struct { // +kubebuilder:rbac:groups=azure.microsoft.com,resources=resourcegroups,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=azure.microsoft.com,resources=resourcegroups/status,verbs=get;update;patch +// Reconcile function does the main reconciliation loop of the operator func (r *ResourceGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { ctx := context.Background() log := r.Log.WithValues("resourcegroup", req.NamespacedName) @@ -92,6 +93,7 @@ func (r *ResourceGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro } +// SetupWithManager function sets up the functions with the controller func (r *ResourceGroupReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&azurev1.ResourceGroup{}). diff --git a/go.mod b/go.mod index eff3883d5ec..814fd9f2897 100644 --- a/go.mod +++ b/go.mod @@ -11,24 +11,19 @@ require ( github.com/Azure/go-autorest/autorest/validation v0.1.0 // indirect github.com/go-logr/logr v0.1.0 github.com/gobuffalo/envy v1.7.0 - github.com/gogo/protobuf v1.2.1 // indirect - github.com/google/go-cmp v0.3.0 // indirect github.com/google/gofuzz v1.0.0 // indirect github.com/googleapis/gnostic v0.3.0 // indirect - github.com/json-iterator/go v1.1.6 // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega v1.5.0 github.com/satori/go.uuid v1.2.0 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 // indirect golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/sys v0.0.0-20190621203818-d432491b9138 // indirect - golang.org/x/text v0.3.2 // indirect - gopkg.in/yaml.v2 v2.2.2 // indirect k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible k8s.io/klog v0.3.3 // indirect k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208 // indirect sigs.k8s.io/controller-runtime v0.2.0-beta.4 + sigs.k8s.io/controller-tools v0.2.0-beta.3 // indirect ) diff --git a/go.sum b/go.sum index c2febe3287c..d2daa5ccf5a 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,8 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.5 h1:xpKq9ap8MbYfhuPCF0dBH854Gp9CxZjr/IocxELFflo= +github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -115,6 +117,7 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -194,6 +197,8 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -239,6 +244,7 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= @@ -262,6 +268,7 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190621203818-d432491b9138 h1:t8BZD9RDjkm9/h7yYN6kE8oaeov5r9aztkB7zKA5Tkg= golang.org/x/sys v0.0.0-20190621203818-d432491b9138/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -278,6 +285,8 @@ golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190501045030-23463209683d h1:D7DVZUZEUgsSIDTivnUtVeGfN5AvhDIKtdIZAqx0ieE= +golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= gomodules.xyz/jsonpatch/v2 v2.0.0 h1:OyHbl+7IOECpPKfVK42oFr6N7+Y2dR+Jsb/IiDV3hOo= gomodules.xyz/jsonpatch/v2 v2.0.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8= @@ -322,6 +331,7 @@ k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible h1:U5Bt+dab9 k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c= @@ -334,6 +344,8 @@ k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= sigs.k8s.io/controller-runtime v0.2.0-beta.4 h1:S1XVfRWR1MuIXZdkYx3jN8JDw+bbQxmWZroy0i87z/A= sigs.k8s.io/controller-runtime v0.2.0-beta.4/go.mod h1:HweyYKQ8fBuzdu2bdaeBJvsFgAi/OqBBnrVGXcqKhME= +sigs.k8s.io/controller-tools v0.2.0-beta.3 h1:7h1Hx+vpg79dktBILuQq/aDed4GZcdxeUwuEb59G1bw= +sigs.k8s.io/controller-tools v0.2.0-beta.3/go.mod h1:bPfk8OXC5AhzPGuPZjcwcltUhQUXYFmUtrJCCF2XaaE= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkWhVrs= sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U= diff --git a/main.go b/main.go index f54895b7034..8a7cfe77634 100644 --- a/main.go +++ b/main.go @@ -104,8 +104,8 @@ func main() { } if err = (&controllers.KeyVaultReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("KeyVault"), + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("KeyVault"), Recorder: mgr.GetEventRecorderFor("KeyVault-controller"), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "KeyVault") @@ -121,9 +121,9 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "Eventhub") os.Exit(1) } - + // +kubebuilder:scaffold:builder - + setupLog.Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { setupLog.Error(err, "problem running manager") diff --git a/pkg/errhelp/errhelp.go b/pkg/errhelp/errhelp.go new file mode 100644 index 00000000000..1bc9a18d37f --- /dev/null +++ b/pkg/errhelp/errhelp.go @@ -0,0 +1,25 @@ +package errhelp + +import ( + "strings" +) + +// IsParentNotFound checks if the error is about a parent resrouce not existing +func IsParentNotFound(err error) bool { + return strings.Contains(err.Error(), "ParentResourceNotFound") +} + +// IsGroupNotFound checks if error is about resource group not existing +func IsGroupNotFound(err error) bool { + return strings.Contains(err.Error(), "ResourceGroupNotFound") +} + +// IsNotActive checks if error is mentioning a non active resource +func IsNotActive(err error) bool { + return strings.Contains(err.Error(), "not active") +} + +// IsAsynchronousOperationNotComplete checks if error reports an asynchronous operation not completed +func IsAsynchronousOperationNotComplete(err error) bool { + return strings.Contains(err.Error(), "asynchronous operation has not completed") +} diff --git a/resourcemanager/keyvaults/keyvaults.go b/resourcemanager/keyvaults/keyvaults.go index 08d0949a9bd..1cd50c10017 100644 --- a/resourcemanager/keyvaults/keyvaults.go +++ b/resourcemanager/keyvaults/keyvaults.go @@ -39,8 +39,8 @@ func getVaultsClient() keyvault.VaultsClient { return vaultsClient } -// CreateVaultAndWait creates a new resource group named by env var -func CreateVaultAndWait(ctx context.Context, groupName string, vaultName string, location string) (keyvault.Vault, error) { +// CreateVault creates a new key vault +func CreateVault(ctx context.Context, groupName string, vaultName string, location string) (keyvault.Vault, error) { vaultsClient := getVaultsClient() id, err := uuid.FromString(config.TenantID()) if err != nil { @@ -61,17 +61,8 @@ func CreateVaultAndWait(ctx context.Context, groupName string, vaultName string, log.Println(fmt.Sprintf("creating keyvault '%s' in resource group '%s' and location: %v", vaultName, groupName, location)) future, err := vaultsClient.CreateOrUpdate(ctx, groupName, vaultName, params) - if err != nil { - return keyvault.Vault{}, err - } - - err = future.WaitForCompletionRef(ctx, vaultsClient.Client) - if err != nil { - return keyvault.Vault{}, err - } - result, err := future.Result(vaultsClient) - return result, err + return future.Result(vaultsClient) } // DeleteVault removes the resource group named by env var From f4eadbbe722c43e17c05023c75724107210111af Mon Sep 17 00:00:00 2001 From: jananivMS Date: Wed, 14 Aug 2019 12:00:56 -0600 Subject: [PATCH 03/19] Added an event for the final successful provisioning --- controllers/keyvault_controller.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/controllers/keyvault_controller.go b/controllers/keyvault_controller.go index b24528ca3ab..2814bdaa363 100644 --- a/controllers/keyvault_controller.go +++ b/controllers/keyvault_controller.go @@ -58,7 +58,6 @@ func (r *KeyVaultReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { } if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { - //log.Error(err, "unable to fetch KeyVault") log.Info("Unable to fetch KeyVault", "err", err.Error()) return ctrl.Result{}, client.IgnoreNotFound(err) } @@ -99,6 +98,7 @@ func (r *KeyVaultReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { return ctrl.Result{}, nil } + r.Recorder.Event(&instance, "Normal", "Provisioned", "Keyvault "+instance.ObjectMeta.Name+" provisioned ") return ctrl.Result{}, nil } @@ -128,7 +128,7 @@ func (r *KeyVaultReconciler) reconcileExternal(instance *azurev1.KeyVault) error _, err := keyvaults.CreateVault(ctx, groupName, name, location) if err != nil { if errhelp.IsAsynchronousOperationNotComplete(err) || errhelp.IsGroupNotFound(err) { - r.Recorder.Event(instance, "Normal", "Updated", name+" provisioning") + r.Recorder.Event(instance, "Normal", "Provisioning", name+" provisioning") return err } r.Recorder.Event(instance, "Warning", "Failed", "Couldn't create resource in azure") @@ -147,7 +147,6 @@ func (r *KeyVaultReconciler) reconcileExternal(instance *azurev1.KeyVault) error r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") } - r.Recorder.Event(instance, "Normal", "Updated", name+" provisioned") return nil } @@ -160,6 +159,8 @@ func (r *KeyVaultReconciler) deleteExternal(instance *azurev1.KeyVault) error { r.Recorder.Event(instance, "Warning", "Failed", "Couldn't delete resouce in azure") return err } + + r.Recorder.Event(instance, "Normal", "Deleted", name+"deleted") return nil } From 4893ac3268f0de2e7f395b77e59fe12efbba4e10 Mon Sep 17 00:00:00 2001 From: jananivMS Date: Mon, 19 Aug 2019 13:11:53 -0600 Subject: [PATCH 04/19] Updated changes based on the PR comments --- Dockerfile | 6 +- api/v1/keyvault_types.go | 2 + .../bases/azure.microsoft.com_keyvaults.yaml | 2 + config/default/manager_image_patch.yaml | 2 +- config/rbac/role.yaml | 56 +++++++++---------- config/samples/azure_v1_keyvault.yaml | 4 +- config/samples/creator_v1_eventhub.yaml | 6 +- .../samples/creator_v1_eventhubnamespace.yaml | 4 +- config/samples/creator_v1_resourcegroup.yaml | 4 +- controllers/keyvault_controller.go | 13 +++-- pkg/errhelp/errhelp.go | 5 ++ 11 files changed, 57 insertions(+), 47 deletions(-) diff --git a/Dockerfile b/Dockerfile index f03c1a8f528..ccbde4946b9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,11 +10,7 @@ COPY go.sum go.sum RUN go mod download # Copy the go source -COPY main.go main.go -COPY api/ api/ -COPY resourcemanager/ resourcemanager/ -COPY controllers/ controllers/ -COPY helpers/ helpers/ +COPY . . # Build RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go diff --git a/api/v1/keyvault_types.go b/api/v1/keyvault_types.go index 385ff12c4fc..c671ab60ee1 100644 --- a/api/v1/keyvault_types.go +++ b/api/v1/keyvault_types.go @@ -32,6 +32,7 @@ type KeyVaultStatus struct { } // +kubebuilder:object:root=true +// +kubebuilder:subresource:status // KeyVault is the Schema for the keyvaults API type KeyVault struct { @@ -43,6 +44,7 @@ type KeyVault struct { } // +kubebuilder:object:root=true +// +kubebuilder:subresource:status // KeyVaultList contains a list of KeyVault type KeyVaultList struct { diff --git a/config/crd/bases/azure.microsoft.com_keyvaults.yaml b/config/crd/bases/azure.microsoft.com_keyvaults.yaml index 2391eb8aff7..15646f3d192 100644 --- a/config/crd/bases/azure.microsoft.com_keyvaults.yaml +++ b/config/crd/bases/azure.microsoft.com_keyvaults.yaml @@ -11,6 +11,8 @@ spec: kind: KeyVault plural: keyvaults scope: "" + subresources: + status: {} validation: openAPIV3Schema: description: KeyVault is the Schema for the keyvaults API diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index 096ebb55bf1..9e09ecc83ec 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -8,7 +8,7 @@ spec: spec: containers: # Change the value of image field below to your controller image URL - - image: docker.io/eventhuboperator:JVtest + - image: controller:latest name: manager env: - name: AZURE_CLIENT_ID diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e7282543886..9ec64fabb9c 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -6,6 +6,14 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - azure.microsoft.com + resources: + - eventhubnamespaces/status + verbs: + - get + - patch + - update - apiGroups: - "" resources: @@ -13,6 +21,13 @@ rules: verbs: - create - watch +- apiGroups: + - azure.microsoft.com + resources: + - events + verbs: + - create + - patch - apiGroups: - apps resources: @@ -26,21 +41,17 @@ rules: - update - watch - apiGroups: - - azure.microsoft.com + - apps resources: - - keyvaults + - deployments/status verbs: - - create - - delete - get - - list - patch - update - - watch - apiGroups: - azure.microsoft.com resources: - - resourcegroups + - eventhubs verbs: - create - delete @@ -64,7 +75,7 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubnamespaces/status + - keyvaults/status verbs: - get - patch @@ -72,11 +83,15 @@ rules: - apiGroups: - azure.microsoft.com resources: - - keyvaults/status + - resourcegroups verbs: + - create + - delete - get + - list - patch - update + - watch - apiGroups: - azure.microsoft.com resources: @@ -100,14 +115,15 @@ rules: - apiGroups: - azure.microsoft.com resources: - - events + - eventhubs/status verbs: - - create + - get - patch + - update - apiGroups: - azure.microsoft.com resources: - - eventhubs + - keyvaults verbs: - create - delete @@ -116,19 +132,3 @@ rules: - patch - update - watch -- apiGroups: - - azure.microsoft.com - resources: - - eventhubs/status - verbs: - - get - - patch - - update -- apiGroups: - - apps - resources: - - deployments/status - verbs: - - get - - patch - - update diff --git a/config/samples/azure_v1_keyvault.yaml b/config/samples/azure_v1_keyvault.yaml index a835078ba42..d83bb205d1b 100644 --- a/config/samples/azure_v1_keyvault.yaml +++ b/config/samples/azure_v1_keyvault.yaml @@ -1,7 +1,7 @@ apiVersion: azure.microsoft.com/v1 kind: KeyVault metadata: - name: jan-kv-sample2 + name: keyvaultsample123 spec: - resourceGroup: Janani-testRG + resourceGroup: TestRG location: westus diff --git a/config/samples/creator_v1_eventhub.yaml b/config/samples/creator_v1_eventhub.yaml index 0c635ae9067..299c60cfb1c 100644 --- a/config/samples/creator_v1_eventhub.yaml +++ b/config/samples/creator_v1_eventhub.yaml @@ -1,11 +1,11 @@ apiVersion: azure.microsoft.com/v1 kind: Eventhub metadata: - name: eventhub-janani-123 + name: eventhub-sample-2307-09 spec: location: "westus" - resourcegroup: "jananitest1234" - namespace: "eventhubnamespace-janani-123" + resourcegroup: "resourcegroup-sample-1907" + namespace: "eventhubnamespace-sample-1907" properties: messageretentionindays: 7 partitioncount: 1 diff --git a/config/samples/creator_v1_eventhubnamespace.yaml b/config/samples/creator_v1_eventhubnamespace.yaml index a979cf06bfa..5ee294bd385 100644 --- a/config/samples/creator_v1_eventhubnamespace.yaml +++ b/config/samples/creator_v1_eventhubnamespace.yaml @@ -1,10 +1,10 @@ apiVersion: azure.microsoft.com/v1 kind: EventhubNamespace metadata: - name: eventhubnamespace-janani-123 + name: eventhubnamespace-sample-6 spec: location: "westus" - resourcegroup: "jananitest1234" + resourcegroup: "resourcegroup-sample-6" sku: name: "Standard" tier: "Standard" diff --git a/config/samples/creator_v1_resourcegroup.yaml b/config/samples/creator_v1_resourcegroup.yaml index 6af32c0feb7..5d51bbc00b2 100644 --- a/config/samples/creator_v1_resourcegroup.yaml +++ b/config/samples/creator_v1_resourcegroup.yaml @@ -1,6 +1,6 @@ apiVersion: azure.microsoft.com/v1 kind: ResourceGroup metadata: - name: jananitest1234 + name: resourcegroup-sample-6 spec: - location: "westus" + location: "westus" diff --git a/controllers/keyvault_controller.go b/controllers/keyvault_controller.go index 2814bdaa363..8b4a17b34e2 100644 --- a/controllers/keyvault_controller.go +++ b/controllers/keyvault_controller.go @@ -121,7 +121,7 @@ func (r *KeyVaultReconciler) reconcileExternal(instance *azurev1.KeyVault) error // write information back to instance instance.Status.Provisioning = true - if err := r.Update(ctx, instance); err != nil { + if err := r.Status().Update(ctx, instance); err != nil { r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") } @@ -133,7 +133,7 @@ func (r *KeyVaultReconciler) reconcileExternal(instance *azurev1.KeyVault) error } r.Recorder.Event(instance, "Warning", "Failed", "Couldn't create resource in azure") instance.Status.Provisioning = false - errUpdate := r.Update(ctx, instance) + errUpdate := r.Status().Update(ctx, instance) if errUpdate != nil { r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") } @@ -143,7 +143,7 @@ func (r *KeyVaultReconciler) reconcileExternal(instance *azurev1.KeyVault) error instance.Status.Provisioning = false instance.Status.Provisioned = true - if err = r.Update(ctx, instance); err != nil { + if err = r.Status().Update(ctx, instance); err != nil { r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") } @@ -156,11 +156,16 @@ func (r *KeyVaultReconciler) deleteExternal(instance *azurev1.KeyVault) error { groupName := instance.Spec.ResourceGroupName _, err := keyvaults.DeleteVault(ctx, groupName, name) if err != nil { + if errhelp.IsStatusCode204(err) { + r.Recorder.Event(instance, "Warning", "DoesNotExist", "Resource to delete does not exist") + return nil + } + r.Recorder.Event(instance, "Warning", "Failed", "Couldn't delete resouce in azure") return err } - r.Recorder.Event(instance, "Normal", "Deleted", name+"deleted") + r.Recorder.Event(instance, "Normal", "Deleted", name+" deleted") return nil } diff --git a/pkg/errhelp/errhelp.go b/pkg/errhelp/errhelp.go index 1bc9a18d37f..5952a219f21 100644 --- a/pkg/errhelp/errhelp.go +++ b/pkg/errhelp/errhelp.go @@ -23,3 +23,8 @@ func IsNotActive(err error) bool { func IsAsynchronousOperationNotComplete(err error) bool { return strings.Contains(err.Error(), "asynchronous operation has not completed") } + +// IsStatusCode204 checks if the error reports a status code 204 failure to respond to request +func IsStatusCode204(err error) bool { + return strings.Contains(err.Error(), "StatusCode=204") +} From add1b864b040c4f6b3825faa148a7730c242a9c3 Mon Sep 17 00:00:00 2001 From: jananivMS Date: Mon, 19 Aug 2019 14:37:55 -0600 Subject: [PATCH 05/19] removing unwanted file --- config/samples/creator_v1_resourcegroup copy.yaml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 config/samples/creator_v1_resourcegroup copy.yaml diff --git a/config/samples/creator_v1_resourcegroup copy.yaml b/config/samples/creator_v1_resourcegroup copy.yaml deleted file mode 100644 index 90c1b9ca3d8..00000000000 --- a/config/samples/creator_v1_resourcegroup copy.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: azure.microsoft.com/v1 -kind: ResourceGroup -metadata: - name: jananitest123 -spec: - location: "westus" From a3f49854e2d7a86895a404627fff7e0220cd77c2 Mon Sep 17 00:00:00 2001 From: jananivMS Date: Mon, 19 Aug 2019 14:39:59 -0600 Subject: [PATCH 06/19] making resource group name the one in the keyvault yaml --- config/samples/azure_v1_keyvault.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/samples/azure_v1_keyvault.yaml b/config/samples/azure_v1_keyvault.yaml index d83bb205d1b..d8c31d4d289 100644 --- a/config/samples/azure_v1_keyvault.yaml +++ b/config/samples/azure_v1_keyvault.yaml @@ -3,5 +3,5 @@ kind: KeyVault metadata: name: keyvaultsample123 spec: - resourceGroup: TestRG + resourceGroup: resourcegroup-sample-6 location: westus From 1e26f3a7633ab4ad494cbeedce9d4ef64e6de047 Mon Sep 17 00:00:00 2001 From: Erin Corson Date: Tue, 20 Aug 2019 17:56:39 -0600 Subject: [PATCH 07/19] need to handled unexpected error types...like validation.error (#111) --- pkg/errhelp/errors.go | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/pkg/errhelp/errors.go b/pkg/errhelp/errors.go index cbc76d02bb2..61e3efb579f 100644 --- a/pkg/errhelp/errors.go +++ b/pkg/errhelp/errors.go @@ -13,21 +13,29 @@ const ( ) func NewAzureError(err error) error { - det := err.(autorest.DetailedError) - code := det.StatusCode.(int) - var kind, reason string - if e, ok := det.Original.(*azure.RequestError); ok { - kind = e.ServiceError.Code - reason = e.ServiceError.Message - } else if e, ok := det.Original.(*azure.ServiceError); ok { - kind = e.Code - reason = e.Message - } else if _, ok := det.Original.(*errors.StatusError); ok { - kind = "StatusError" - reason = "StatusError" + ae := AzureError{ + Original: err, } + //det := err.(autorest.DetailedError) - return &AzureError{Type: kind, Reason: reason, Code: code, Original: err} + if det, ok := err.(autorest.DetailedError); ok { + var kind, reason string + ae.Code = det.StatusCode.(int) + + if e, ok := det.Original.(*azure.RequestError); ok { + kind = e.ServiceError.Code + reason = e.ServiceError.Message + } else if e, ok := det.Original.(*azure.ServiceError); ok { + kind = e.Code + reason = e.Message + } else if _, ok := det.Original.(*errors.StatusError); ok { + kind = "StatusError" + reason = "StatusError" + } + ae.Reason = reason + ae.Type = kind + } + return &ae } type AzureError struct { @@ -38,5 +46,5 @@ type AzureError struct { } func (e AzureError) Error() string { - return e.Type + return e.Original.Error() } From f5fd2cbbf6b19df933aeaea002811303dfd2b857 Mon Sep 17 00:00:00 2001 From: priyakumarank <51063856+priyakumarank@users.noreply.github.com> Date: Wed, 21 Aug 2019 10:23:34 +1000 Subject: [PATCH 08/19] refactor tests (#90) * improve tests with parallel execution and rm sleep * fix the tests to run on kindcluster --- Makefile | 4 +- api/v1/suite_test.go | 1 + controllers/eventhub_controller_test.go | 118 ++++------------ .../eventhubnamespace_controller_test.go | 131 +++++++----------- controllers/resourcegroup_controller_test.go | 8 +- controllers/suite_test.go | 34 ++++- pkg/resourcemanager/eventhubs/hub_test.go | 32 ++--- .../eventhubs/namespace_test.go | 16 +-- pkg/resourcemanager/eventhubs/suite_test.go | 26 +++- .../resourcegroups/resourcegroup_test.go | 5 - .../resourcegroups/suite_test.go | 4 + 11 files changed, 159 insertions(+), 220 deletions(-) diff --git a/Makefile b/Makefile index 85ef2b161ec..a7425b076e5 100644 --- a/Makefile +++ b/Makefile @@ -8,12 +8,12 @@ all: manager # Run tests test: generate fmt vet manifests - TEST_USE_EXISTING_CLUSTER=false go test -v -coverprofile=coverage.txt -covermode count ./api/... ./controllers/... ./resourcemanager/eventhubs/... ./resourcemanager/resourcegroups/... 2>&1 | tee testlogs.txt + TEST_USE_EXISTING_CLUSTER=false go test -v -coverprofile=coverage.txt -covermode count ./api/... ./controllers/... ./pkg/resourcemanager/eventhubs/... ./pkg/resourcemanager/resourcegroups/... 2>&1 | tee testlogs.txt go-junit-report < testlogs.txt > report.xml go tool cover -html=coverage.txt -o cover.html # Run tests with existing cluster test-existing: generate fmt vet manifests - TEST_USE_EXISTING_CLUSTER=true go test -v -coverprofile=coverage-existing.txt -covermode count ./api/... ./controllers/... ./resourcemanager/eventhubs/... ./resourcemanager/resourcegroups/... 2>&1 | tee testlogs-existing.txt + TEST_USE_EXISTING_CLUSTER=true go test -v -coverprofile=coverage-existing.txt -covermode count ./api/... ./controllers/... ./pkg/resourcemanager/eventhubs/... ./pkg/resourcemanager/resourcegroups/... 2>&1 | tee testlogs-existing.txt go-junit-report < testlogs-existing.txt > report-existing.xml go tool cover -html=coverage-existing.txt -o cover-existing.html diff --git a/api/v1/suite_test.go b/api/v1/suite_test.go index b3d69f9de67..e4d5357f792 100644 --- a/api/v1/suite_test.go +++ b/api/v1/suite_test.go @@ -38,6 +38,7 @@ var k8sClient client.Client var testEnv *envtest.Environment func TestAPIs(t *testing.T) { + t.Parallel() RegisterFailHandler(Fail) RunSpecsWithDefaultAndCustomReporters(t, diff --git a/controllers/eventhub_controller_test.go b/controllers/eventhub_controller_test.go index d4ac4b6149b..216f03c4df4 100644 --- a/controllers/eventhub_controller_test.go +++ b/controllers/eventhub_controller_test.go @@ -17,14 +17,15 @@ package controllers import ( "context" + azurev1 "github.com/Azure/azure-service-operator/api/v1" helpers "github.com/Azure/azure-service-operator/pkg/helpers" - resoucegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" + + "time" + . "github.com/onsi/ginkgo" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "os" - "time" . "github.com/onsi/gomega" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -48,87 +49,45 @@ var _ = Describe("EventHub Controller", func() { // Avoid adding tests for vanilla CRUD operations because they would // test Kubernetes API server, which isn't the goal here. Context("Create and Delete", func() { - if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" { - It("should validate eventhubnamespaces exist before creating eventhubs", func() { - - resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) - eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) - eventhubName := "t-eh-" + helpers.RandomString(10) - - // Create the EventHub object and expect the Reconcile to be created - eventhubInstance := &azurev1.Eventhub{ - ObjectMeta: metav1.ObjectMeta{ - Name: eventhubName, - Namespace: "default", - }, - Spec: azurev1.EventhubSpec{ - Location: "westus", - Namespace: eventhubNamespaceName, - ResourceGroup: resourceGroupName, - Properties: azurev1.EventhubProperties{ - MessageRetentionInDays: 7, - PartitionCount: 1, - }, - }, - } - - k8sClient.Create(context.Background(), eventhubInstance) - - time.Sleep(60 * time.Second) - - eventhubNamespacedName := types.NamespacedName{Name: eventhubName, Namespace: "default"} - - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubInstance) - return eventhubInstance.IsSubmitted() - }, timeout, - ).Should(BeFalse()) - - }) - } - - It("should create and delete eventhubs", func() { + It("should validate eventhubnamespaces exist before creating eventhubs", func() { - resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) - eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) eventhubName := "t-eh-" + helpers.RandomString(10) - var err error - - // Create the Resourcegroup object and expect the Reconcile to be created - resourceGroupInstance := &azurev1.ResourceGroup{ + // Create the EventHub object and expect the Reconcile to be created + eventhubInstance := &azurev1.Eventhub{ ObjectMeta: metav1.ObjectMeta{ - Name: resourceGroupName, + Name: eventhubName, Namespace: "default", }, - Spec: azurev1.ResourceGroupSpec{ - Location: "westus", + Spec: azurev1.EventhubSpec{ + Location: "westus", + Namespace: "t-ns-dev-eh-" + helpers.RandomString(10), + ResourceGroup: "t-rg-dev-eh-" + helpers.RandomString(10), + Properties: azurev1.EventhubProperties{ + MessageRetentionInDays: 7, + PartitionCount: 1, + }, }, } - err = k8sClient.Create(context.Background(), resourceGroupInstance) - Expect(apierrors.IsInvalid(err)).To(Equal(false)) - Expect(err).NotTo(HaveOccurred()) + k8sClient.Create(context.Background(), eventhubInstance) - time.Sleep(30 * time.Second) + eventhubNamespacedName := types.NamespacedName{Name: eventhubName, Namespace: "default"} - // Create the Eventhub namespace object and expect the Reconcile to be created - eventhubNamespaceInstance := &azurev1.EventhubNamespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: eventhubNamespaceName, - Namespace: "default", - }, - Spec: azurev1.EventhubNamespaceSpec{ - Location: "westus", - ResourceGroup: resourceGroupName, - }, - } + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubInstance) + return eventhubInstance.IsSubmitted() + }, timeout, + ).Should(BeFalse()) + }) - err = k8sClient.Create(context.Background(), eventhubNamespaceInstance) - Expect(apierrors.IsInvalid(err)).To(Equal(false)) - Expect(err).NotTo(HaveOccurred()) + It("should create and delete eventhubs", func() { + + resourceGroupName = "t-rg-dev-controller" + eventhubNamespaceName = "t-ns-dev-eh-ns" + eventhubName := "t-eh-" + helpers.RandomString(10) - time.Sleep(30 * time.Second) + var err error // Create the EventHub object and expect the Reconcile to be created eventhubInstance := &azurev1.Eventhub{ @@ -151,12 +110,6 @@ var _ = Describe("EventHub Controller", func() { Expect(apierrors.IsInvalid(err)).To(Equal(false)) Expect(err).NotTo(HaveOccurred()) - time.Sleep(30 * time.Second) - // The instance object may not be a valid object because it might be missing some required fields. - // Please modify the instance object by adding required fields and then remove the following if statement. - Expect(apierrors.IsInvalid(err)).To(Equal(false)) - Expect(err).NotTo(HaveOccurred()) - eventhubNamespacedName := types.NamespacedName{Name: eventhubName, Namespace: "default"} Eventually(func() bool { @@ -171,8 +124,6 @@ var _ = Describe("EventHub Controller", func() { }, timeout, ).Should(BeTrue()) - time.Sleep(2 * time.Second) - //create secret in k8s csecret := &v1.Secret{ TypeMeta: metav1.TypeMeta{ @@ -197,8 +148,6 @@ var _ = Describe("EventHub Controller", func() { err = k8sClient.Create(context.Background(), csecret) Expect(err).NotTo(HaveOccurred()) - time.Sleep(2 * time.Second) - //get secret from k8s secret := &v1.Secret{} err = k8sClient.Get(context.Background(), types.NamespacedName{Name: eventhubName, Namespace: eventhubInstance.Namespace}, secret) @@ -206,8 +155,6 @@ var _ = Describe("EventHub Controller", func() { Expect(secret.Data).To(Equal(csecret.Data)) Expect(secret.ObjectMeta).To(Equal(csecret.ObjectMeta)) - time.Sleep(2 * time.Second) - k8sClient.Delete(context.Background(), eventhubInstance) Eventually(func() bool { _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubInstance) @@ -215,11 +162,6 @@ var _ = Describe("EventHub Controller", func() { }, timeout, ).Should(BeTrue()) - time.Sleep(2 * time.Second) - - _, err = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) - Expect(err).NotTo(HaveOccurred()) - }) }) }) diff --git a/controllers/eventhubnamespace_controller_test.go b/controllers/eventhubnamespace_controller_test.go index 4ffb1c690c8..4735a5bbb96 100644 --- a/controllers/eventhubnamespace_controller_test.go +++ b/controllers/eventhubnamespace_controller_test.go @@ -17,13 +17,14 @@ package controllers import ( "context" + azurev1 "github.com/Azure/azure-service-operator/api/v1" helpers "github.com/Azure/azure-service-operator/pkg/helpers" - resoucegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" - . "github.com/onsi/ginkgo" - "os" + "time" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -36,6 +37,7 @@ var _ = Describe("EventHubNamespace Controller", func() { BeforeEach(func() { // Add any setup steps that needs to be executed before each test + }) AfterEach(func() { @@ -47,94 +49,72 @@ var _ = Describe("EventHubNamespace Controller", func() { // Avoid adding tests for vanilla CRUD operations because they would // test Kubernetes API server, which isn't the goal here. Context("Create and Delete", func() { - if os.Getenv("TEST_USE_EXISTING_CLUSTER") == "true" { - It("should validate eventhubnamespace name is valid", func() { - resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) - eventhubNamespaceName := "t-ns" + It("should validate eventhubnamespace name is valid", func() { - // Create the EventHubNamespace object and expect the Reconcile to be created - eventhubNamespaceInstance := &azurev1.EventhubNamespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: eventhubNamespaceName, - Namespace: "default", - }, - Spec: azurev1.EventhubNamespaceSpec{ - Location: "westus", - ResourceGroup: resourceGroupName, - }, - } - - k8sClient.Create(context.Background(), eventhubNamespaceInstance) + resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) + eventhubNamespaceName := "t-ns" - time.Sleep(60 * time.Second) + // Create the EventHubNamespace object and expect the Reconcile to be created + eventhubNamespaceInstance := &azurev1.EventhubNamespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: eventhubNamespaceName, + Namespace: "default", + }, + Spec: azurev1.EventhubNamespaceSpec{ + Location: "westus", + ResourceGroup: resourceGroupName, + }, + } - eventhubNamespacedName := types.NamespacedName{Name: eventhubNamespaceName, Namespace: "default"} + k8sClient.Create(context.Background(), eventhubNamespaceInstance) - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubNamespaceInstance) - return eventhubNamespaceInstance.IsSubmitted() - }, timeout, - ).Should(BeFalse()) + eventhubNamespacedName := types.NamespacedName{Name: eventhubNamespaceName, Namespace: "default"} - }) + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubNamespaceInstance) + return eventhubNamespaceInstance.IsSubmitted() + }, timeout, + ).Should(BeFalse()) - It("should validate resourcegroup exist before creating eventhubnamespaces", func() { + }) - resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) - eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) + It("should validate resourcegroup exist before creating eventhubnamespaces", func() { - // Create the EventHubNamespace object and expect the Reconcile to be created - eventhubNamespaceInstance := &azurev1.EventhubNamespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: eventhubNamespaceName, - Namespace: "default", - }, - Spec: azurev1.EventhubNamespaceSpec{ - Location: "westus", - ResourceGroup: resourceGroupName, - }, - } + resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) + eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) - k8sClient.Create(context.Background(), eventhubNamespaceInstance) + // Create the EventHubNamespace object and expect the Reconcile to be created + eventhubNamespaceInstance := &azurev1.EventhubNamespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: eventhubNamespaceName, + Namespace: "default", + }, + Spec: azurev1.EventhubNamespaceSpec{ + Location: "westus", + ResourceGroup: resourceGroupName, + }, + } - time.Sleep(60 * time.Second) + k8sClient.Create(context.Background(), eventhubNamespaceInstance) - eventhubNamespacedName := types.NamespacedName{Name: eventhubNamespaceName, Namespace: "default"} + eventhubNamespacedName := types.NamespacedName{Name: eventhubNamespaceName, Namespace: "default"} - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubNamespaceInstance) - return eventhubNamespaceInstance.IsSubmitted() - }, timeout, - ).Should(BeFalse()) + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubNamespaceInstance) + return eventhubNamespaceInstance.IsSubmitted() + }, timeout, + ).Should(BeFalse()) - }) - } + }) It("should create and delete namespace in k8s", func() { - resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) + resourceGroupName := "t-rg-dev-controller" eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) var err error - // Create the Resourcegroup object and expect the Reconcile to be created - resourceGroupInstance := &azurev1.ResourceGroup{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourceGroupName, - Namespace: "default", - }, - Spec: azurev1.ResourceGroupSpec{ - Location: "westus", - }, - } - - err = k8sClient.Create(context.Background(), resourceGroupInstance) - Expect(apierrors.IsInvalid(err)).To(Equal(false)) - Expect(err).NotTo(HaveOccurred()) - - time.Sleep(60 * time.Second) - // Create the Eventhub namespace object and expect the Reconcile to be created eventhubNamespaceInstance := &azurev1.EventhubNamespace{ ObjectMeta: metav1.ObjectMeta{ @@ -151,8 +131,6 @@ var _ = Describe("EventHubNamespace Controller", func() { Expect(apierrors.IsInvalid(err)).To(Equal(false)) Expect(err).NotTo(HaveOccurred()) - time.Sleep(30 * time.Second) - eventhubNamespacedName := types.NamespacedName{Name: eventhubNamespaceName, Namespace: "default"} Eventually(func() bool { @@ -161,16 +139,12 @@ var _ = Describe("EventHubNamespace Controller", func() { }, timeout, ).Should(BeTrue()) - time.Sleep(30 * time.Second) - Eventually(func() bool { _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubNamespaceInstance) return eventhubNamespaceInstance.IsSubmitted() }, timeout, ).Should(BeTrue()) - time.Sleep(30 * time.Second) - k8sClient.Delete(context.Background(), eventhubNamespaceInstance) Eventually(func() bool { _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubNamespaceInstance) @@ -178,11 +152,6 @@ var _ = Describe("EventHubNamespace Controller", func() { }, timeout, ).Should(BeTrue()) - time.Sleep(2 * time.Second) - - _, err = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) - Expect(err).NotTo(HaveOccurred()) - }) }) }) diff --git a/controllers/resourcegroup_controller_test.go b/controllers/resourcegroup_controller_test.go index a9ec25a2eb0..9a7f519c484 100644 --- a/controllers/resourcegroup_controller_test.go +++ b/controllers/resourcegroup_controller_test.go @@ -23,7 +23,6 @@ import ( azurev1 "github.com/Azure/azure-service-operator/api/v1" helpers "github.com/Azure/azure-service-operator/pkg/helpers" - //resoucegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -70,8 +69,11 @@ var _ = Describe("ResourceGroup Controller", func() { Expect(err).NotTo(HaveOccurred()) resourceGroupNamespacedName := types.NamespacedName{Name: resourceGroupName, Namespace: "default"} - - time.Sleep(2 * time.Second) + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), resourceGroupNamespacedName, resourceGroupInstance) + return resourceGroupInstance.IsSubmitted() + }, timeout, + ).Should(BeTrue()) k8sClient.Delete(context.Background(), resourceGroupInstance) Eventually(func() bool { diff --git a/controllers/suite_test.go b/controllers/suite_test.go index c79e25b2b4d..64a49c50a89 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -16,14 +16,19 @@ limitations under the License. package controllers import ( + "context" "os" "path/filepath" "testing" azurev1 "github.com/Azure/azure-service-operator/api/v1" resourcemanagerconfig "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" + + eventhubs "github.com/Azure/azure-service-operator/pkg/resourcemanager/eventhubs" + resoucegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -41,10 +46,19 @@ var cfg *rest.Config var k8sClient client.Client var k8sManager ctrl.Manager var testEnv *envtest.Environment +var resourceGroupName string +var resourcegroupLocation string +var eventhubNamespaceName string +var namespaceLocation string func TestAPIs(t *testing.T) { + t.Parallel() RegisterFailHandler(Fail) + resourceGroupName = "t-rg-dev-controller" + resourcegroupLocation = "westus" + eventhubNamespaceName = "t-ns-dev-eh-ns" + namespaceLocation = "westus" RunSpecsWithDefaultAndCustomReporters(t, "Controller Suite", []Reporter{envtest.NewlineReporter{}}) @@ -119,13 +133,27 @@ var _ = BeforeSuite(func(done Done) { k8sClient = k8sManager.GetClient() Expect(k8sClient).ToNot(BeNil()) + // Create the Resourcegroup resource + result, _ := resoucegroupsresourcemanager.CheckExistence(context.Background(), resourceGroupName) + if result.Response.StatusCode != 204 { + _, _ = resoucegroupsresourcemanager.CreateGroup(context.Background(), resourceGroupName, resourcegroupLocation) + } + + // Create the Eventhub namespace resource + _, err = eventhubs.CreateNamespaceAndWait(context.Background(), resourceGroupName, eventhubNamespaceName, namespaceLocation) + close(done) -}, 60) +}, 120) -var _ = AfterSuite(func() { +var _ = AfterSuite(func(done Done) { //clean up the resources created for test By("tearing down the test environment") + + _, _ = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) + err := testEnv.Stop() Expect(err).ToNot(HaveOccurred()) -}) + close(done) + +}, 60) diff --git a/pkg/resourcemanager/eventhubs/hub_test.go b/pkg/resourcemanager/eventhubs/hub_test.go index d76c3497826..55be9925906 100644 --- a/pkg/resourcemanager/eventhubs/hub_test.go +++ b/pkg/resourcemanager/eventhubs/hub_test.go @@ -22,17 +22,25 @@ import ( model "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" helpers "github.com/Azure/azure-service-operator/pkg/helpers" - resoucegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) -var _ = Describe("Namespace", func() { +var _ = Describe("Eventhub", func() { const timeout = time.Second * 240 + var resourceGroupName string + var eventhubNamespaceName string + var namespaceLocation string BeforeEach(func() { // Add any setup steps that needs to be executed before each test + resourceGroupName = "t-rg-dev-rm-eh" + eventhubNamespaceName = "t-ns-dev-eh-" + helpers.RandomString(10) + namespaceLocation = "westus" + + _, _ = CreateNamespaceAndWait(context.Background(), resourceGroupName, eventhubNamespaceName, namespaceLocation) + }) AfterEach(func() { @@ -47,26 +55,12 @@ var _ = Describe("Namespace", func() { Context("Create and Delete", func() { It("should create and delete hubs in azure", func() { - resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) - resourcegroupLocation := "westus" - eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) - namespaceLocation := "westus" eventhubName := "t-eh-" + helpers.RandomString(10) messageRetentionInDays := int32(7) partitionCount := int32(1) var err error - _, err = resoucegroupsresourcemanager.CreateGroup(context.Background(), resourceGroupName, resourcegroupLocation) - Expect(err).NotTo(HaveOccurred()) - - time.Sleep(30 * time.Second) - - _, err = CreateNamespaceAndWait(context.Background(), resourceGroupName, eventhubNamespaceName, namespaceLocation) - Expect(err).NotTo(HaveOccurred()) - - time.Sleep(30 * time.Second) - _, err = CreateHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, messageRetentionInDays, partitionCount) Expect(err).NotTo(HaveOccurred()) @@ -83,6 +77,7 @@ var _ = Describe("Namespace", func() { Rights: &accessRights, }, } + _, err = CreateOrUpdateAuthorizationRule(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, authorizationRuleName, parameters) Expect(err).NotTo(HaveOccurred()) @@ -101,11 +96,6 @@ var _ = Describe("Namespace", func() { }, timeout, ).Should(BeTrue()) - time.Sleep(30 * time.Second) - - _, err = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) - Expect(err).NotTo(HaveOccurred()) - }) }) diff --git a/pkg/resourcemanager/eventhubs/namespace_test.go b/pkg/resourcemanager/eventhubs/namespace_test.go index 1c24274135f..35449a3d4ed 100644 --- a/pkg/resourcemanager/eventhubs/namespace_test.go +++ b/pkg/resourcemanager/eventhubs/namespace_test.go @@ -20,8 +20,6 @@ import ( "context" "time" - resoucegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" - helpers "github.com/Azure/azure-service-operator/pkg/helpers" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -47,16 +45,11 @@ var _ = Describe("Namespace", func() { Context("Create and Delete", func() { It("should create and delete namespace in azure", func() { - resourceGroupName := "t-rg-dev-eh-" + helpers.RandomString(10) - resourcegroupLocation := "westus" + resourceGroupName := "t-rg-dev-rm-eh" eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) namespaceLocation := "westus" - var err error - - _, err = resoucegroupsresourcemanager.CreateGroup(context.Background(), resourceGroupName, resourcegroupLocation) - Expect(err).NotTo(HaveOccurred()) - time.Sleep(30 * time.Second) + var err error _, err = CreateNamespaceAndWait(context.Background(), resourceGroupName, eventhubNamespaceName, namespaceLocation) Expect(err).NotTo(HaveOccurred()) @@ -76,11 +69,6 @@ var _ = Describe("Namespace", func() { }, timeout, ).Should(BeTrue()) - time.Sleep(30 * time.Second) - - _, err = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) - Expect(err).NotTo(HaveOccurred()) - }) }) diff --git a/pkg/resourcemanager/eventhubs/suite_test.go b/pkg/resourcemanager/eventhubs/suite_test.go index bd812b6d291..c30e6878f1a 100644 --- a/pkg/resourcemanager/eventhubs/suite_test.go +++ b/pkg/resourcemanager/eventhubs/suite_test.go @@ -19,9 +19,13 @@ import ( "testing" resourcemanagerconfig "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" + + resoucegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "context" + "k8s.io/client-go/rest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -32,9 +36,17 @@ import ( // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. var cfg *rest.Config +var resourceGroupName string +var resourcegroupLocation string func TestAPIs(t *testing.T) { + t.Parallel() + if testing.Short() { + t.Skip("skipping Resource Manager Eventhubs Suite") + } RegisterFailHandler(Fail) + resourceGroupName = "t-rg-dev-rm-eh" + resourcegroupLocation = "westus" RunSpecs(t, "Eventhubs Suite") } @@ -46,12 +58,20 @@ var _ = BeforeSuite(func(done Done) { resourcemanagerconfig.LoadSettings() + //create resourcegroup for this suite + result, _ := resoucegroupsresourcemanager.CheckExistence(context.Background(), resourceGroupName) + if result.Response.StatusCode != 204 { + _, _ = resoucegroupsresourcemanager.CreateGroup(context.Background(), resourceGroupName, resourcegroupLocation) + } + close(done) }, 60) -var _ = AfterSuite(func() { +var _ = AfterSuite(func(done Done) { //clean up the resources created for test - By("tearing down the test environment") -}) + _, _ = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) + + close(done) +}, 60) diff --git a/pkg/resourcemanager/resourcegroups/resourcegroup_test.go b/pkg/resourcemanager/resourcegroups/resourcegroup_test.go index 1a87ef8f621..099ecde64c2 100644 --- a/pkg/resourcemanager/resourcegroups/resourcegroup_test.go +++ b/pkg/resourcemanager/resourcegroups/resourcegroup_test.go @@ -54,8 +54,6 @@ var _ = Describe("ResourceGroups", func() { _, err = CreateGroup(context.Background(), resourcegroupName, resourcegroupLocation) Expect(err).NotTo(HaveOccurred()) - time.Sleep(40 * time.Second) - Eventually(func() bool { result, _ := CheckExistence(context.Background(), resourcegroupName) @@ -66,8 +64,6 @@ var _ = Describe("ResourceGroups", func() { _, err = DeleteGroup(context.Background(), resourcegroupName) Expect(err).NotTo(HaveOccurred()) - time.Sleep(30 * time.Second) - Eventually(func() bool { result, _ := CheckExistence(context.Background(), resourcegroupName) @@ -75,7 +71,6 @@ var _ = Describe("ResourceGroups", func() { }, timeout, ).Should(BeTrue()) - //DeleteAllGroupsWithPrefix(context.Background(), "t-rg-") }) }) diff --git a/pkg/resourcemanager/resourcegroups/suite_test.go b/pkg/resourcemanager/resourcegroups/suite_test.go index c5d2ed330ce..5a7a91bbfe2 100644 --- a/pkg/resourcemanager/resourcegroups/suite_test.go +++ b/pkg/resourcemanager/resourcegroups/suite_test.go @@ -32,6 +32,10 @@ import ( var cfg *rest.Config func TestAPIs(t *testing.T) { + t.Parallel() + if testing.Short() { + t.Skip("skipping Resource Manager Resource Group Suite") + } RegisterFailHandler(Fail) RunSpecs(t, "Resource Manager Suite") From ea86ff6064db3955bf46f6d50bd34832f260b94b Mon Sep 17 00:00:00 2001 From: Erin Corson Date: Wed, 21 Aug 2019 11:35:51 -0600 Subject: [PATCH 09/19] Updates to KV controller from Ace (#80) (#112) * feat: implement keyvault controller * Ace's KV changes with updates * Added an event for the final successful provisioning * Updated changes based on the PR comments * removing unwanted file * making resource group name the one in the keyvault yaml Co-authored-by: Ace Eldeib From 93d3fb4ac08efee5e7a77a6e3502e200ef771afa Mon Sep 17 00:00:00 2001 From: Erin Corson Date: Wed, 21 Aug 2019 15:08:21 -0600 Subject: [PATCH 10/19] Test update (#115) * this needs to exist in the reconciler in order to use controllerutil createorupdate --- controllers/suite_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 64a49c50a89..9c622a75a0a 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -108,6 +108,7 @@ var _ = BeforeSuite(func(done Done) { Client: k8sManager.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("EventHub"), Recorder: k8sManager.GetEventRecorderFor("Eventhub-controller"), + Scheme: scheme.Scheme, }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) From 0dd02d0751ed9855f64e1e630c91e5d994df2409 Mon Sep 17 00:00:00 2001 From: priyakumarank <51063856+priyakumarank@users.noreply.github.com> Date: Thu, 22 Aug 2019 10:55:02 +1000 Subject: [PATCH 11/19] Feat/add consumer group kind (#117) * add consumer group kind * update tests for consumer group * fix isbeingdeleted --- PROJECT | 3 + api/v1/consumergroup_types.go | 90 ++++ api/v1/consumergroup_types_test.go | 76 +++ api/v1/zz_generated.deepcopy.go | 89 ++++ .../azure.microsoft.com_consumergroups.yaml | 432 ++++++++++++++++++ config/crd/kustomization.yaml | 3 + .../cainjection_in_consumergroups.yaml | 8 + .../patches/webhook_in_consumergroups.yaml | 17 + config/rbac/role.yaml | 64 ++- config/samples/azure_v1_consumergroup.yaml | 9 + controllers/consumergroup_controller.go | 184 ++++++++ .../consumergroup_controller_finalizer.go | 57 +++ controllers/consumergroup_controller_test.go | 100 ++++ controllers/eventhub_controller.go | 10 +- controllers/eventhub_controller_test.go | 4 + controllers/suite_test.go | 12 + main.go | 10 + .../eventhubs/consumergroup.go | 65 +++ .../eventhubs/consumergroup_test.go | 90 ++++ 19 files changed, 1293 insertions(+), 30 deletions(-) create mode 100644 api/v1/consumergroup_types.go create mode 100644 api/v1/consumergroup_types_test.go create mode 100644 config/crd/bases/azure.microsoft.com_consumergroups.yaml create mode 100644 config/crd/patches/cainjection_in_consumergroups.yaml create mode 100644 config/crd/patches/webhook_in_consumergroups.yaml create mode 100644 config/samples/azure_v1_consumergroup.yaml create mode 100644 controllers/consumergroup_controller.go create mode 100644 controllers/consumergroup_controller_finalizer.go create mode 100644 controllers/consumergroup_controller_test.go create mode 100644 pkg/resourcemanager/eventhubs/consumergroup.go create mode 100644 pkg/resourcemanager/eventhubs/consumergroup_test.go diff --git a/PROJECT b/PROJECT index be5d1060e35..40562d2d55c 100644 --- a/PROJECT +++ b/PROJECT @@ -14,3 +14,6 @@ resources: - group: azure version: v1 kind: KeyVault +- group: azure + version: v1 + kind: ConsumerGroup diff --git a/api/v1/consumergroup_types.go b/api/v1/consumergroup_types.go new file mode 100644 index 00000000000..d7dd1c5de99 --- /dev/null +++ b/api/v1/consumergroup_types.go @@ -0,0 +1,90 @@ +/* + +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 v1 + +import ( + helpers "github.com/Azure/azure-service-operator/pkg/helpers" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// ConsumerGroupSpec defines the desired state of ConsumerGroup +type ConsumerGroupSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + ResourceGroupName string `json:"resourcegroup,omitempty"` + NamespaceName string `json:"namespace,omitempty"` + EventhubName string `json:"eventhub,omitempty"` +} + +// ConsumerGroupStatus defines the observed state of ConsumerGroup +type ConsumerGroupStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + Provisioning bool `json:"provisioning,omitempty"` + Provisioned bool `json:"provisioned,omitempty"` +} + +// +kubebuilder:object:root=true + +// ConsumerGroup is the Schema for the consumergroups API +type ConsumerGroup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ConsumerGroupSpec `json:"spec,omitempty"` + Status ConsumerGroupStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ConsumerGroupList contains a list of ConsumerGroup +type ConsumerGroupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ConsumerGroup `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ConsumerGroup{}, &ConsumerGroupList{}) +} + +func init() { + SchemeBuilder.Register(&ConsumerGroup{}, &ConsumerGroupList{}) +} + +func (consumerGroup *ConsumerGroup) IsBeingDeleted() bool { + return !consumerGroup.ObjectMeta.DeletionTimestamp.IsZero() +} + +func (consumerGroup *ConsumerGroup) IsSubmitted() bool { + return consumerGroup.Status.Provisioning || consumerGroup.Status.Provisioned + +} + +func (consumerGroup *ConsumerGroup) HasFinalizer(finalizerName string) bool { + return helpers.ContainsString(consumerGroup.ObjectMeta.Finalizers, finalizerName) +} + +func (consumerGroup *ConsumerGroup) AddFinalizer(finalizerName string) { + consumerGroup.ObjectMeta.Finalizers = append(consumerGroup.ObjectMeta.Finalizers, finalizerName) +} + +func (consumerGroup *ConsumerGroup) RemoveFinalizer(finalizerName string) { + consumerGroup.ObjectMeta.Finalizers = helpers.RemoveString(consumerGroup.ObjectMeta.Finalizers, finalizerName) +} diff --git a/api/v1/consumergroup_types_test.go b/api/v1/consumergroup_types_test.go new file mode 100644 index 00000000000..6d326e40322 --- /dev/null +++ b/api/v1/consumergroup_types_test.go @@ -0,0 +1,76 @@ +/* + +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 v1 + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// These tests are written in BDD-style using Ginkgo framework. Refer to +// http://onsi.github.io/ginkgo to learn more. + +var _ = Describe("ConsumerGroup", func() { + var ( + key types.NamespacedName + created, fetched *ConsumerGroup + ) + + BeforeEach(func() { + // Add any setup steps that needs to be executed before each test + }) + + AfterEach(func() { + // Add any teardown steps that needs to be executed after each test + }) + + // Add Tests for OpenAPI validation (or additonal CRD features) specified in + // your API definition. + // Avoid adding tests for vanilla CRUD operations because they would + // test Kubernetes API server, which isn't the goal here. + Context("Create API", func() { + + It("should create an object successfully", func() { + + key = types.NamespacedName{ + Name: "foo", + Namespace: "default", + } + created = &ConsumerGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }} + + By("creating an API obj") + Expect(k8sClient.Create(context.TODO(), created)).To(Succeed()) + + fetched = &ConsumerGroup{} + Expect(k8sClient.Get(context.TODO(), key, fetched)).To(Succeed()) + Expect(fetched).To(Equal(created)) + + By("deleting the created object") + Expect(k8sClient.Delete(context.TODO(), created)).To(Succeed()) + Expect(k8sClient.Get(context.TODO(), key, created)).ToNot(Succeed()) + }) + + }) + +}) diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index cc8243b3678..760242cc0f6 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -23,6 +23,95 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConsumerGroup) DeepCopyInto(out *ConsumerGroup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsumerGroup. +func (in *ConsumerGroup) DeepCopy() *ConsumerGroup { + if in == nil { + return nil + } + out := new(ConsumerGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ConsumerGroup) 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 *ConsumerGroupList) DeepCopyInto(out *ConsumerGroupList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ConsumerGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsumerGroupList. +func (in *ConsumerGroupList) DeepCopy() *ConsumerGroupList { + if in == nil { + return nil + } + out := new(ConsumerGroupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ConsumerGroupList) 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 *ConsumerGroupSpec) DeepCopyInto(out *ConsumerGroupSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsumerGroupSpec. +func (in *ConsumerGroupSpec) DeepCopy() *ConsumerGroupSpec { + if in == nil { + return nil + } + out := new(ConsumerGroupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConsumerGroupStatus) DeepCopyInto(out *ConsumerGroupStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConsumerGroupStatus. +func (in *ConsumerGroupStatus) DeepCopy() *ConsumerGroupStatus { + if in == nil { + return nil + } + out := new(ConsumerGroupStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Eventhub) DeepCopyInto(out *Eventhub) { *out = *in diff --git a/config/crd/bases/azure.microsoft.com_consumergroups.yaml b/config/crd/bases/azure.microsoft.com_consumergroups.yaml new file mode 100644 index 00000000000..8d00e1f0cd1 --- /dev/null +++ b/config/crd/bases/azure.microsoft.com_consumergroups.yaml @@ -0,0 +1,432 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: consumergroups.azure.microsoft.com +spec: + group: azure.microsoft.com + names: + kind: ConsumerGroup + plural: consumergroups + scope: "" + validation: + openAPIV3Schema: + description: ConsumerGroup is the Schema for the consumergroups 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/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/api-conventions.md#types-kinds' + type: string + metadata: + description: ObjectMeta is metadata that all persisted resources must have, + which includes all objects users must create. + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value map stored with + a resource that may be set by external tools to store and retrieve + arbitrary metadata. They are not queryable and should be preserved + when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + clusterName: + description: The name of the cluster which the object belongs to. This + is used to distinguish resources with same name and namespace in different + clusters. This field is not set anywhere right now and apiserver is + going to ignore it if set in create or update request. + type: string + creationTimestamp: + description: "CreationTimestamp is a timestamp representing the server + time when this object was created. It is not guaranteed to be set + in happens-before order across separate operations. Clients may not + set this value. It is represented in RFC3339 form and is in UTC. \n + Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + deletionGracePeriodSeconds: + description: Number of seconds allowed for this object to gracefully + terminate before it will be removed from the system. Only set when + deletionTimestamp is also set. May only be shortened. Read-only. + format: int64 + type: integer + deletionTimestamp: + description: "DeletionTimestamp is RFC 3339 date and time at which this + resource will be deleted. This field is set by the server when a graceful + deletion is requested by the user, and is not directly settable by + a client. The resource is expected to be deleted (no longer visible + from resource lists, and not reachable by name) after the time in + this field, once the finalizers list is empty. As long as the finalizers + list contains items, deletion is blocked. Once the deletionTimestamp + is set, this value may not be unset or be set further into the future, + although it may be shortened or the resource may be deleted prior + to this time. For example, a user may request that a pod is deleted + in 30 seconds. The Kubelet will react by sending a graceful termination + signal to the containers in the pod. After that 30 seconds, the Kubelet + will send a hard termination signal (SIGKILL) to the container and + after cleanup, remove the pod from the API. In the presence of network + partitions, this object may still exist after this timestamp, until + an administrator or automated process can determine the resource is + fully terminated. If not set, graceful deletion of the object has + not been requested. \n Populated by the system when a graceful deletion + is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata" + format: date-time + type: string + finalizers: + description: Must be empty before the object is deleted from the registry. + Each entry is an identifier for the responsible component that will + remove the entry from the list. If the deletionTimestamp of the object + is non-nil, entries in this list can only be removed. + items: + type: string + type: array + generateName: + description: "GenerateName is an optional prefix, used by the server, + to generate a unique name ONLY IF the Name field has not been provided. + If this field is used, the name returned to the client will be different + than the name passed. This value will also be combined with a unique + suffix. The provided value has the same validation rules as the Name + field, and may be truncated by the length of the suffix required to + make the value unique on the server. \n If this field is specified + and the generated name exists, the server will NOT return a 409 - + instead, it will either return 201 Created or 500 with Reason ServerTimeout + indicating a unique name could not be found in the time allotted, + and the client should retry (optionally after the time indicated in + the Retry-After header). \n Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency" + type: string + generation: + description: A sequence number representing a specific generation of + the desired state. Populated by the system. Read-only. + format: int64 + type: integer + initializers: + description: "An initializer is a controller which enforces some system + invariant at object creation time. This field is a list of initializers + that have not yet acted on this object. If nil or empty, this object + has been completely initialized. Otherwise, the object is considered + uninitialized and is hidden (in list/watch and get calls) from clients + that haven't explicitly asked to observe uninitialized objects. \n + When an object is created, the system will populate this list with + the current set of initializers. Only privileged users may set or + modify this list. Once it is empty, it may not be modified further + by any user. \n DEPRECATED - initializers are an alpha field and will + be removed in v1.15." + properties: + pending: + description: Pending is a list of initializers that must execute + in order before this object is visible. When the last pending + initializer is removed, and no failing result is set, the initializers + struct will be set to nil and the object is considered as initialized + and visible to all clients. + items: + description: Initializer is information about an initializer that + has not yet completed. + properties: + name: + description: name of the process that is responsible for initializing + this object. + type: string + required: + - name + type: object + type: array + result: + description: If result is set with the Failure field, the object + will be persisted to storage and then deleted, ensuring that other + clients can observe the deletion. + 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/api-conventions.md#resources' + type: string + code: + description: Suggested HTTP return code for this status, 0 if + not set. + format: int32 + type: integer + details: + description: Extended data associated with the reason. Each + reason may define its own extended details. This field is + optional and the data returned is not guaranteed to conform + to any schema except that defined by the reason type. + properties: + causes: + description: The Causes array includes more details associated + with the StatusReason failure. Not all StatusReasons may + provide detailed causes. + items: + description: StatusCause provides more information about + an api.Status failure, including cases when multiple + errors are encountered. + properties: + field: + description: "The field of the resource that has caused + this error, as named by its JSON serialization. + May include dot and postfix notation for nested + attributes. Arrays are zero-indexed. Fields may + appear more than once in an array of causes due + to fields having multiple errors. Optional. \n Examples: + \ \"name\" - the field \"name\" on the current + resource \"items[0].name\" - the field \"name\" + on the first array entry in \"items\"" + type: string + message: + description: A human-readable description of the cause + of the error. This field may be presented as-is + to a reader. + type: string + reason: + description: A machine-readable description of the + cause of the error. If this value is empty there + is no information available. + type: string + type: object + type: array + group: + description: The group attribute of the resource associated + with the status StatusReason. + type: string + kind: + description: 'The kind attribute of the resource associated + with the status StatusReason. On some operations may differ + from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: The name attribute of the resource associated + with the status StatusReason (when there is a single name + which can be described). + type: string + retryAfterSeconds: + description: If specified, the time in seconds before the + operation should be retried. Some errors may indicate + the client must take an alternate action - for those errors + this field may indicate how long to wait before taking + the alternate action. + format: int32 + type: integer + uid: + description: 'UID of the resource. (when there is a single + resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + type: object + 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/api-conventions.md#types-kinds' + type: string + message: + description: A human-readable description of the status of this + operation. + type: string + metadata: + description: 'Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + properties: + continue: + description: continue may be set if the user set a limit + on the number of items returned, and indicates that the + server has more data available. The value is opaque and + may be used to issue another request to the endpoint that + served this list to retrieve the next set of available + objects. Continuing a consistent list may not be possible + if the server configuration has changed or more than a + few minutes have passed. The resourceVersion field returned + when using this continue value will be identical to the + value in the first response, unless you have received + this token from an error message. + type: string + resourceVersion: + description: 'String that identifies the server''s internal + version of this object that can be used by clients to + determine when objects have changed. Value must be treated + as opaque by clients and passed unmodified back to the + server. Populated by the system. Read-only. More info: + https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency' + type: string + selfLink: + description: selfLink is a URL representing this object. + Populated by the system. Read-only. + type: string + type: object + reason: + description: A machine-readable description of why this operation + is in the "Failure" status. If this value is empty there is + no information available. A Reason clarifies an HTTP status + code but does not override it. + type: string + status: + description: 'Status of the operation. One of: "Success" or + "Failure". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status' + type: string + type: object + required: + - pending + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can be used to organize + and categorize (scope and select) objects. May match selectors of + replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + managedFields: + description: "ManagedFields maps workflow-id and version to the set + of fields that are managed by that workflow. This is mostly for internal + housekeeping, and users typically shouldn't need to set or understand + this field. A workflow can be the user's name, a controller's name, + or the name of a specific apply path like \"ci-cd\". The set of fields + is always in the version that the workflow used when modifying the + object. \n This field is alpha and can be changed or removed without + notice." + items: + description: ManagedFieldsEntry is a workflow-id, a FieldSet and the + group version of the resource that the fieldset applies to. + properties: + apiVersion: + description: APIVersion defines the version of this resource that + this field set applies to. The format is "group/version" just + like the top-level APIVersion field. It is necessary to track + the version of a field set because it cannot be automatically + converted. + type: string + fields: + additionalProperties: true + description: Fields identifies a set of fields. + type: object + manager: + description: Manager is an identifier of the workflow managing + these fields. + type: string + operation: + description: Operation is the type of operation which lead to + this ManagedFieldsEntry being created. The only valid values + for this field are 'Apply' and 'Update'. + type: string + time: + description: Time is timestamp of when these fields were set. + It should always be empty if Operation is 'Apply' + format: date-time + type: string + type: object + type: array + name: + description: 'Name must be unique within a namespace. Is required when + creating resources, although some resources may allow a client to + request the generation of an appropriate name automatically. Name + is primarily intended for creation idempotence and configuration definition. + Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: "Namespace defines the space within each name must be unique. + An empty namespace is equivalent to the \"default\" namespace, but + \"default\" is the canonical representation. Not all objects are required + to be scoped to a namespace - the value of this field for those objects + will be empty. \n Must be a DNS_LABEL. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/namespaces" + type: string + ownerReferences: + description: List of objects depended by this object. If ALL objects + in the list have been deleted, this object will be garbage collected. + If this object is managed by a controller, then an entry in this list + will point to this controller, with the controller field set to true. + There cannot be more than one managing controller. + items: + description: OwnerReference contains enough information to let you + identify an owning object. An owning object must be in the same + namespace as the dependent, or be cluster-scoped, so there is no + namespace field. + properties: + apiVersion: + description: API version of the referent. + type: string + blockOwnerDeletion: + description: If true, AND if the owner has the "foregroundDeletion" + finalizer, then the owner cannot be deleted from the key-value + store until this reference is removed. Defaults to false. To + set this field, a user needs "delete" permission of the owner, + otherwise 422 (Unprocessable Entity) will be returned. + type: boolean + controller: + description: If true, this reference points to the managing controller. + type: boolean + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + uid: + description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + required: + - apiVersion + - kind + - name + - uid + type: object + type: array + resourceVersion: + description: "An opaque value that represents the internal version of + this object that can be used by clients to determine when objects + have changed. May be used for optimistic concurrency, change detection, + and the watch operation on a resource or set of resources. Clients + must treat these values as opaque and passed unmodified back to the + server. They may only be valid for a particular resource or set of + resources. \n Populated by the system. Read-only. Value must be treated + as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency" + type: string + selfLink: + description: SelfLink is a URL representing this object. Populated by + the system. Read-only. + type: string + uid: + description: "UID is the unique in time and space value for this object. + It is typically generated by the server on successful creation of + a resource and is not allowed to change on PUT operations. \n Populated + by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids" + type: string + type: object + spec: + description: ConsumerGroupSpec defines the desired state of ConsumerGroup + properties: + eventhub: + type: string + namespace: + type: string + resourcegroup: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + type: string + type: object + status: + description: ConsumerGroupStatus defines the observed state of ConsumerGroup + properties: + provisioned: + type: boolean + provisioning: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: boolean + type: object + type: object + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 11ac6f5386c..24e4d6bcd14 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -5,6 +5,7 @@ resources: - bases/azure.microsoft.com_eventhubs.yaml - bases/azure.microsoft.com_resourcegroups.yaml - bases/azure.microsoft.com_eventhubnamespaces.yaml +- bases/azure.microsoft.com_consumergroups.yaml - bases/azure.microsoft.com_keyvaults.yaml # +kubebuilder:scaffold:crdkustomizeresource @@ -13,6 +14,7 @@ patches: #- patches/webhook_in_eventhubs.yaml #- patches/webhook_in_resourcegroups.yaml #- patches/webhook_in_eventhubnamespaces.yaml +#- patches/webhook_in_consumergroups.yaml #- patches/webhook_in_keyvaults.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch @@ -20,6 +22,7 @@ patches: #- patches/cainjection_in_eventhubs.yaml #- patches/cainjection_in_resourcegroups.yaml #- patches/cainjection_in_eventhubnamespaces.yaml +#- patches/cainjection_in_consumergroups.yaml #- patches/cainjection_in_keyvaults.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch diff --git a/config/crd/patches/cainjection_in_consumergroups.yaml b/config/crd/patches/cainjection_in_consumergroups.yaml new file mode 100644 index 00000000000..4c029c56574 --- /dev/null +++ b/config/crd/patches/cainjection_in_consumergroups.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + certmanager.k8s.io/inject-ca-from: $(NAMESPACE)/$(CERTIFICATENAME) + name: consumergroups.azure.microsoft.com diff --git a/config/crd/patches/webhook_in_consumergroups.yaml b/config/crd/patches/webhook_in_consumergroups.yaml new file mode 100644 index 00000000000..2e82d0f6726 --- /dev/null +++ b/config/crd/patches/webhook_in_consumergroups.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: consumergroups.azure.microsoft.com +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 9b8859a9350..11d72ca7ada 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -9,15 +9,7 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubs/status - verbs: - - get - - patch - - update -- apiGroups: - - azure.microsoft.com - resources: - - eventhubnamespaces/status + - consumergroups/status verbs: - get - patch @@ -37,7 +29,7 @@ rules: - apiGroups: - azure.microsoft.com resources: - - keyvaults/status + - resourcegroups/status verbs: - get - patch @@ -45,21 +37,26 @@ rules: - apiGroups: - "" resources: - - events + - secrets verbs: - create + - delete + - get + - list + - patch + - update - watch - apiGroups: - - azure.microsoft.com + - "" resources: - events verbs: - create - - patch + - watch - apiGroups: - azure.microsoft.com resources: - - eventhubs + - resourcegroups verbs: - create - delete @@ -71,7 +68,7 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubnamespaces + - consumergroups verbs: - create - delete @@ -83,7 +80,7 @@ rules: - apiGroups: - azure.microsoft.com resources: - - resourcegroups + - eventhubs verbs: - create - delete @@ -95,15 +92,23 @@ rules: - apiGroups: - azure.microsoft.com resources: - - resourcegroups/status + - eventhubs/status verbs: - get - patch - update - apiGroups: - - "" + - azure.microsoft.com resources: - - secrets + - eventhubnamespaces/status + verbs: + - get + - patch + - update +- apiGroups: + - apps + resources: + - deployments verbs: - create - delete @@ -115,7 +120,15 @@ rules: - apiGroups: - apps resources: - - deployments + - deployments/status + verbs: + - get + - patch + - update +- apiGroups: + - azure.microsoft.com + resources: + - eventhubnamespaces verbs: - create - delete @@ -125,10 +138,17 @@ rules: - update - watch - apiGroups: - - apps + - azure.microsoft.com resources: - - deployments/status + - keyvaults/status verbs: - get - patch - update +- apiGroups: + - azure.microsoft.com + resources: + - events + verbs: + - create + - patch diff --git a/config/samples/azure_v1_consumergroup.yaml b/config/samples/azure_v1_consumergroup.yaml new file mode 100644 index 00000000000..076bf32c357 --- /dev/null +++ b/config/samples/azure_v1_consumergroup.yaml @@ -0,0 +1,9 @@ +apiVersion: azure.microsoft.com/v1 +kind: ConsumerGroup +metadata: + name: consumergroup-sample-1 +spec: + # Add fields here + resourcegroup: "resourcegroup-sample-1" + namespace: "eventhubnamespace-sample-1" + eventhub: "eventhub-sample-2" diff --git a/controllers/consumergroup_controller.go b/controllers/consumergroup_controller.go new file mode 100644 index 00000000000..f6988cee06e --- /dev/null +++ b/controllers/consumergroup_controller.go @@ -0,0 +1,184 @@ +/* + +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 controllers + +import ( + "context" + "fmt" + "os" + "strconv" + "time" + + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + azurev1 "github.com/Azure/azure-service-operator/api/v1" + eventhubsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/eventhubs" +) + +// ConsumerGroupReconciler reconciles a ConsumerGroup object +type ConsumerGroupReconciler struct { + client.Client + Log logr.Logger + Recorder record.EventRecorder +} + +// +kubebuilder:rbac:groups=azure.microsoft.com,resources=consumergroups,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=azure.microsoft.com,resources=consumergroups/status,verbs=get;update;patch + +//Reconcile reconciler for consumergroup +func (r *ConsumerGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + ctx := context.Background() + log := r.Log.WithValues("consumergroup", req.NamespacedName) + + var instance azurev1.ConsumerGroup + if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { + log.Error(err, "unable to fetch consumergroup") + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + if instance.IsBeingDeleted() { + err := r.handleFinalizer(&instance) + if err != nil { + return reconcile.Result{}, fmt.Errorf("error when handling finalizer: %v", err) + } + return ctrl.Result{}, nil + } + + if !instance.HasFinalizer(consumerGroupFinalizerName) { + err := r.addFinalizer(&instance) + if err != nil { + return ctrl.Result{}, fmt.Errorf("error when removing finalizer: %v", err) + } + return ctrl.Result{}, nil + } + + if !instance.IsSubmitted() { + err := r.createConsumerGroup(&instance) + if err != nil { + + return ctrl.Result{}, fmt.Errorf("error when creating consumer group in azure: %v", err) + } + return ctrl.Result{}, nil + } + + requeueAfter, err := strconv.Atoi(os.Getenv("REQUEUE_AFTER")) + if err != nil { + requeueAfter = 30 + } + + return ctrl.Result{ + RequeueAfter: time.Second * time.Duration(requeueAfter), + }, nil +} + +func (r *ConsumerGroupReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&azurev1.ConsumerGroup{}). + Complete(r) +} + +func (r *ConsumerGroupReconciler) createConsumerGroup(instance *azurev1.ConsumerGroup) error { + + ctx := context.Background() + var err error + consumergroupName := instance.ObjectMeta.Name + namespaceName := instance.Spec.NamespaceName + resourcegroup := instance.Spec.ResourceGroupName + eventhubName := instance.Spec.EventhubName + + // write information back to instance + instance.Status.Provisioning = true + + //get owner instance + var ownerInstance azurev1.Eventhub + eventhubNamespacedName := types.NamespacedName{Name: eventhubName, Namespace: instance.Namespace} + err = r.Get(ctx, eventhubNamespacedName, &ownerInstance) + + if err != nil { + //log error and kill it, as the parent might not exist in the cluster. It could have been created elsewhere or through the portal directly + r.Recorder.Event(instance, "Warning", "Failed", "Unable to get owner instance of eventhub") + } else { + //set owner reference for consumer group if it exists + references := []metav1.OwnerReference{ + metav1.OwnerReference{ + APIVersion: "v1", + Kind: "Eventhub", + Name: ownerInstance.GetName(), + UID: ownerInstance.GetUID(), + }, + } + instance.ObjectMeta.SetOwnerReferences(references) + } + + err = r.Update(ctx, instance) + if err != nil { + //log error and kill it + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } + + _, err = eventhubsresourcemanager.CreateConsumerGroup(ctx, resourcegroup, namespaceName, eventhubName, consumergroupName) + if err != nil { + + r.Recorder.Event(instance, "Warning", "Failed", "Couldn't create consumer group in azure") + instance.Status.Provisioning = false + errUpdate := r.Update(ctx, instance) + if errUpdate != nil { + //log error and kill it + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } + return err + } + // write information back to instance + instance.Status.Provisioning = false + instance.Status.Provisioned = true + + err = r.Update(ctx, instance) + if err != nil { + //log error and kill it + r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance") + } + + r.Recorder.Event(instance, "Normal", "Updated", consumergroupName+" provisioned") + + return nil + +} + +func (r *ConsumerGroupReconciler) deleteConsumerGroup(instance *azurev1.ConsumerGroup) error { + ctx := context.Background() + + consumergroupName := instance.ObjectMeta.Name + namespaceName := instance.Spec.NamespaceName + resourcegroup := instance.Spec.ResourceGroupName + eventhubName := instance.Spec.EventhubName + + var err error + _, err = eventhubsresourcemanager.DeleteConsumerGroup(ctx, resourcegroup, namespaceName, eventhubName, consumergroupName) + if err != nil { + r.Recorder.Event(instance, "Warning", "Failed", "Couldn't delete consumer group in azure") + return err + } + return nil +} diff --git a/controllers/consumergroup_controller_finalizer.go b/controllers/consumergroup_controller_finalizer.go new file mode 100644 index 00000000000..99cc3883fd8 --- /dev/null +++ b/controllers/consumergroup_controller_finalizer.go @@ -0,0 +1,57 @@ +/* +Copyright 2019 microsoft. + +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 controllers + +import ( + "context" + "fmt" + + azurev1 "github.com/Azure/azure-service-operator/api/v1" +) + +const consumerGroupFinalizerName = "consumergroup.finalizers.com" + +func (r *ConsumerGroupReconciler) addFinalizer(instance *azurev1.ConsumerGroup) error { + instance.AddFinalizer(consumerGroupFinalizerName) + err := r.Update(context.Background(), instance) + if err != nil { + return fmt.Errorf("failed to update finalizer: %v", err) + } + r.Recorder.Event(instance, "Normal", "Updated", fmt.Sprintf("finalizer %s added", consumerGroupFinalizerName)) + return nil +} + +func (r *ConsumerGroupReconciler) handleFinalizer(instance *azurev1.ConsumerGroup) error { + if instance.HasFinalizer(consumerGroupFinalizerName) { + // our finalizer is present, so lets handle our external dependency + if err := r.deleteExternalDependency(instance); err != nil { + return err + } + + instance.RemoveFinalizer(consumerGroupFinalizerName) + if err := r.Update(context.Background(), instance); err != nil { + return err + } + } + // Our finalizer has finished, so the reconciler can do nothing. + return nil +} + +func (r *ConsumerGroupReconciler) deleteExternalDependency(instance *azurev1.ConsumerGroup) error { + + return r.deleteConsumerGroup(instance) +} diff --git a/controllers/consumergroup_controller_test.go b/controllers/consumergroup_controller_test.go new file mode 100644 index 00000000000..0bd10170073 --- /dev/null +++ b/controllers/consumergroup_controller_test.go @@ -0,0 +1,100 @@ +/* +Copyright 2019 microsoft. + +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 controllers + +import ( + "context" + + azurev1 "github.com/Azure/azure-service-operator/api/v1" + helpers "github.com/Azure/azure-service-operator/pkg/helpers" + + "time" + + . "github.com/onsi/ginkgo" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/onsi/gomega" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" +) + +var _ = Describe("ConsumerGroup Controller", func() { + + const timeout = time.Second * 240 + + BeforeEach(func() { + // Add any setup steps that needs to be executed before each test + }) + + AfterEach(func() { + // Add any teardown steps that needs to be executed after each test + }) + + // Add Tests for OpenAPI validation (or additonal CRD features) specified in + // your API definition. + // Avoid adding tests for vanilla CRUD operations because they would + // test Kubernetes API server, which isn't the goal here. + Context("Create and Delete", func() { + It("should create and delete consumer groups", func() { + + resourceGroupName = "t-rg-dev-controller" + eventhubNamespaceName = "t-ns-dev-eh-ns" + eventhubName = "t-eh-dev-sample" + consumerGroupName := "t-cg-" + helpers.RandomString(10) + + var err error + + // Create the consumer group object and expect the Reconcile to be created + consumerGroupInstance := &azurev1.ConsumerGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: consumerGroupName, + Namespace: "default", + }, + Spec: azurev1.ConsumerGroupSpec{ + NamespaceName: eventhubNamespaceName, + ResourceGroupName: resourceGroupName, + EventhubName: eventhubName, + }, + } + + err = k8sClient.Create(context.Background(), consumerGroupInstance) + Expect(apierrors.IsInvalid(err)).To(Equal(false)) + Expect(err).NotTo(HaveOccurred()) + + consumerGroupNamespacedName := types.NamespacedName{Name: consumerGroupName, Namespace: "default"} + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), consumerGroupNamespacedName, consumerGroupInstance) + return consumerGroupInstance.HasFinalizer(consumerGroupFinalizerName) + }, timeout, + ).Should(BeTrue()) + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), consumerGroupNamespacedName, consumerGroupInstance) + return consumerGroupInstance.IsSubmitted() + }, timeout, + ).Should(BeTrue()) + + k8sClient.Delete(context.Background(), consumerGroupInstance) + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), consumerGroupNamespacedName, consumerGroupInstance) + return consumerGroupInstance.IsBeingDeleted() + }, timeout, + ).Should(BeTrue()) + + }) + }) +}) diff --git a/controllers/eventhub_controller.go b/controllers/eventhub_controller.go index c781eab6864..57ff6558797 100644 --- a/controllers/eventhub_controller.go +++ b/controllers/eventhub_controller.go @@ -38,6 +38,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ) // EventhubReconciler reconciles a Eventhub object @@ -70,14 +71,7 @@ func (r *EventhubReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { if instance.IsBeingDeleted() { err := r.handleFinalizer(&instance) if err != nil { - catch := []string{ - errhelp.ResourceGroupNotFoundErrorCode, - } - if helpers.ContainsString(catch, err.(*errhelp.AzureError).Type) { - log.Info("Got ignorable error", "type", err.(*errhelp.AzureError).Type) - return ctrl.Result{RequeueAfter: time.Second * 30}, nil - } - return ctrl.Result{}, fmt.Errorf("error when handling finalizer: %v", err) + return reconcile.Result{}, fmt.Errorf("error when handling finalizer: %v", err) } return ctrl.Result{}, nil } diff --git a/controllers/eventhub_controller_test.go b/controllers/eventhub_controller_test.go index 216f03c4df4..0a07f57290e 100644 --- a/controllers/eventhub_controller_test.go +++ b/controllers/eventhub_controller_test.go @@ -103,6 +103,10 @@ var _ = Describe("EventHub Controller", func() { MessageRetentionInDays: 7, PartitionCount: 1, }, + AuthorizationRule: azurev1.EventhubAuthorizationRule{ + Name: "RootManageSharedAccessKey", + Rights: []string{"Listen"}, + }, }, } diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 9c622a75a0a..e8fba4c4c45 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -49,6 +49,7 @@ var testEnv *envtest.Environment var resourceGroupName string var resourcegroupLocation string var eventhubNamespaceName string +var eventhubName string var namespaceLocation string func TestAPIs(t *testing.T) { @@ -58,6 +59,7 @@ func TestAPIs(t *testing.T) { resourcegroupLocation = "westus" eventhubNamespaceName = "t-ns-dev-eh-ns" + eventhubName = "t-eh-dev-sample" namespaceLocation = "westus" RunSpecsWithDefaultAndCustomReporters(t, "Controller Suite", @@ -126,6 +128,13 @@ var _ = BeforeSuite(func(done Done) { }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) + err = (&ConsumerGroupReconciler{ + Client: k8sManager.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("ConsumerGroup"), + Recorder: k8sManager.GetEventRecorderFor("ConsumerGroup-controller"), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + go func() { err = k8sManager.Start(ctrl.SetupSignalHandler()) Expect(err).ToNot(HaveOccurred()) @@ -143,6 +152,9 @@ var _ = BeforeSuite(func(done Done) { // Create the Eventhub namespace resource _, err = eventhubs.CreateNamespaceAndWait(context.Background(), resourceGroupName, eventhubNamespaceName, namespaceLocation) + // Create the Eventhub resource + _, err = eventhubs.CreateHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, int32(7), int32(1)) + close(done) }, 120) diff --git a/main.go b/main.go index c86448a3e1a..d899844d772 100644 --- a/main.go +++ b/main.go @@ -118,6 +118,16 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "EventhubNamespace") os.Exit(1) } + + err = (&controllers.ConsumerGroupReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("ConsumerGroup"), + Recorder: mgr.GetEventRecorderFor("ConsumerGroup-controller"), + }).SetupWithManager(mgr) + if err != nil { + setupLog.Error(err, "unable to create controller", "controller", "ConsumerGroup") + os.Exit(1) + } if !resourcemanagerconfig.Declarative() { if err = (&azurev1.EventhubNamespace{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "EventhubNamespace") diff --git a/pkg/resourcemanager/eventhubs/consumergroup.go b/pkg/resourcemanager/eventhubs/consumergroup.go new file mode 100644 index 00000000000..0e1223345e7 --- /dev/null +++ b/pkg/resourcemanager/eventhubs/consumergroup.go @@ -0,0 +1,65 @@ +package eventhubs + +import ( + "context" + + "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" + "github.com/Azure/azure-service-operator/pkg/resourcemanager/iam" + "github.com/Azure/go-autorest/autorest" + + "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" +) + +func getConsumerGroupsClient() eventhub.ConsumerGroupsClient { + consumerGroupClient := eventhub.NewConsumerGroupsClient(config.SubscriptionID()) + auth, _ := iam.GetResourceManagementAuthorizer() + consumerGroupClient.Authorizer = auth + consumerGroupClient.AddToUserAgent(config.UserAgent()) + return consumerGroupClient +} + +// CreateConsumerGroup creates an Event Hub Consumer Group +// Parameters: +// resourceGroupName - name of the resource group within the azure subscription. +// namespaceName - the Namespace name +// eventHubName - the Event Hub name +// consumerGroupName - the consumer group name +// parameters - parameters supplied to create or update a consumer group resource. +func CreateConsumerGroup(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, consumerGroupName string) (eventhub.ConsumerGroup, error) { + consumerGroupClient := getConsumerGroupsClient() + + parameters := eventhub.ConsumerGroup{} + return consumerGroupClient.CreateOrUpdate( + ctx, + resourceGroupName, + namespaceName, + eventHubName, + consumerGroupName, + parameters, + ) + +} + +// DeleteConsumerGroup deletes an Event Hub Consumer Group +// Parameters: +// resourceGroupName - name of the resource group within the azure subscription. +// namespaceName - the Namespace name +// eventHubName - the Event Hub name +// consumerGroupName - the consumer group name +func DeleteConsumerGroup(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, consumerGroupName string) (result autorest.Response, err error) { + consumerGroupClient := getConsumerGroupsClient() + return consumerGroupClient.Delete( + ctx, + resourceGroupName, + namespaceName, + eventHubName, + consumerGroupName, + ) + +} + +//GetConsumerGroup gets consumer group description for the specified Consumer Group. +func GetConsumerGroup(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, consumerGroupName string) (eventhub.ConsumerGroup, error) { + consumerGroupClient := getConsumerGroupsClient() + return consumerGroupClient.Get(ctx, resourceGroupName, namespaceName, eventHubName, consumerGroupName) +} diff --git a/pkg/resourcemanager/eventhubs/consumergroup_test.go b/pkg/resourcemanager/eventhubs/consumergroup_test.go new file mode 100644 index 00000000000..b20b5da106b --- /dev/null +++ b/pkg/resourcemanager/eventhubs/consumergroup_test.go @@ -0,0 +1,90 @@ +/* +Copyright 2019 microsoft. + +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 eventhubs + +import ( + "context" + "time" + + helpers "github.com/Azure/azure-service-operator/pkg/helpers" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("ConsumerGroup", func() { + + const timeout = time.Second * 240 + var resourceGroupName string + var eventhubNamespaceName string + var eventhubName string + var namespaceLocation string + var messageRetentionInDays int32 + var partitionCount int32 + + BeforeEach(func() { + // Add any setup steps that needs to be executed before each test + resourceGroupName = "t-rg-dev-rm-eh" + eventhubNamespaceName = "t-ns-dev-eh-" + helpers.RandomString(10) + namespaceLocation = "westus" + eventhubName = "t-eh-dev-ehs" + messageRetentionInDays = int32(7) + partitionCount = int32(1) + + _, _ = CreateNamespaceAndWait(context.Background(), resourceGroupName, eventhubNamespaceName, namespaceLocation) + + _, _ = CreateHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, messageRetentionInDays, partitionCount) + + }) + + AfterEach(func() { + // Add any teardown steps that needs to be executed after each test + }) + + // Add Tests for OpenAPI validation (or additonal CRD features) specified in + // your API definition. + // Avoid adding tests for vanilla CRUD operations because they would + // test Kubernetes API server, which isn't the goal here. + + Context("Create and Delete", func() { + It("should create and delete consumer groups in azure", func() { + + consumerGroupName := "t-cg-" + helpers.RandomString(10) + + var err error + + _, err = CreateConsumerGroup(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, consumerGroupName) + Expect(err).NotTo(HaveOccurred()) + + Eventually(func() bool { + result, _ := GetConsumerGroup(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, consumerGroupName) + return result.Response.StatusCode == 200 + }, timeout, + ).Should(BeTrue()) + + _, err = DeleteConsumerGroup(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, consumerGroupName) + Expect(err).NotTo(HaveOccurred()) + + Eventually(func() bool { + result, _ := GetConsumerGroup(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, consumerGroupName) + return result.Response.StatusCode == 404 + }, timeout, + ).Should(BeTrue()) + + }) + + }) +}) From d02a81a8fc05887b57221f6fb5e9f910628ac9b7 Mon Sep 17 00:00:00 2001 From: Stephen Zoio Date: Thu, 22 Aug 2019 17:15:18 +1000 Subject: [PATCH 12/19] Updates to README - steps for onboarding (#114) * cluster additions * updated docs --- README.md | 119 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index c46c7036142..ec3a37e4673 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,17 @@ The project was built using 1. You have GoLang installed. 2. You have the kubectl command line (kubectl CLI) installed. -3. You have acess to a Kubernetes cluster. It can be a local hosted Cluster like [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/), [Kind](https://github.com/kubernetes-sigs/kind) or, Docker for desktop installed localy with RBAC enabled. if you opt for Azure Kubernetes Service ([AKS](https://azure.microsoft.com/en-au/services/kubernetes-service/)), you can use: `az aks get-credentials --resource-group $RG_NAME --name $Cluster_NAME` -Kubectl: Client version 1.14 Server Version 1.12 -4. [kustomize](https://github.com/kubernetes-sigs/kustomize) is also needed +3. You have acess to a Kubernetes cluster. + It can be a local hosted Cluster like + [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/), + [Kind](https://github.com/kubernetes-sigs/kind) or, Docker for desktop installed locally with RBAC enabled. + If you opt for Azure Kubernetes Service ([AKS](https://azure.microsoft.com/en-au/services/kubernetes-service/)), you can use: + `az aks get-credentials --resource-group $RG_NAME --name $Cluster_NAME` + Kubectl: Client version 1.14 Server Version 1.12 + + It is recommended to use [Kind](https://github.com/kubernetes-sigs/kind) as it is needed for testing Webhooks. +4. Install [Kubebuilder](https://book.kubebuilder.io/), following the linked installation instructions. +5. [kustomize](https://github.com/kubernetes-sigs/kustomize) is also needed. This must be installed via `make install-kustomize` (see section below). Basic commands to check your cluster @@ -32,49 +40,78 @@ Basic commands to check your cluster kubectl get pods -n kube-system ``` -5. [Cert Manager](https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html) -```shell -kubectl get secret webhook-server-cert -n azureoperator-system -o yaml >certs.txt -``` -you can use `https://inbrowser.tools/` and exctarct ca.crt,tls.crt and tls.key - -### Run Souce Code +### Building and Running from Source -1. Clone the repo +1. Clone the repo from the following folder `/src/github.com/Azure`. -2. Install the azure_v1_eventhub CRD in the configured Kubernetes cluster folder ~/.kube/config, -run `kubectl apply -f config/crd/bases` or `make install` +2. Make sure the envronment variable `GO111MODULE=on` is set. 3. Update the values in `azure_v1_eventhub.yaml` to reflect the resource group and event hub you want to provision -4. you need kind to test webhooks - -```shell - GO111MODULE="on" go get sigs.k8s.io/kind@v0.4.0 && kind create cluster - kind create cluster - export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" - kubectl cluster-info - IMG="docker.io/yourimage:tag" make docker-build - kind load docker-image docker.io/yourimage:tag --loglevel "trace" - make deploy -``` - -4. Set the environment variables AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, REQUEUE_AFTER -If you are running it on Windows the environment variables should not have quotes. It should be set in this way: -SET AZURE_TENANT_ID=11xxxx-xxx-xxx-xxx-xxxxx -and the VSCode should be run from the same session/command window - -5. Set the azureoperatorsettings secrete - -```shell -Create the azureoperator-system namespace -kubectl --namespace azureoperator-system \ - create secret generic azureoperatorsettings \ - --from-literal=AZURE_CLIENT_ID="xxxx" \ - --from-literal=AZURE_CLIENT_SECRET="xxxxx" \ - --from-literal=AZURE_SUBSCRIPTION_ID="xxxx" \ - --from-literal=AZURE_TENANT_ID="xxxxx" -``` +4. Install [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/) + + ```shell + GO111MODULE="on" go get sigs.k8s.io/kind@v0.4.0 && kind create cluster + kind create cluster + export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" + kubectl cluster-info + IMG="docker.io/yourimage:tag" make docker-build + kind load docker-image docker.io/yourimage:tag --loglevel "trace" + make deploy + ``` + +5. Create a Service Principal + If you don't have a Service Principal create one from the Azure CLI: + ```bash + az ad sp create-for-rbac --skip-assignment + ``` + Then make sure this service principal has rights assigned to provision resources on your Azure account. + +6. Set the environment variables `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `REQUEUE_AFTER` + If you are running it on Windows the environment variables should not have quotes. + It should be set in this way: + SET AZURE_TENANT_ID=11xxxx-xxx-xxx-xxx-xxxxx + and the VSCode should be run from the same session/command window + +7. Set up the Cluster + + If you are using Kind: + ```shell + make set-kindcluster + ``` + + If you are not using Kind, it's a manual process, as follows: + + a. Create the namespace + + ```shell + kubectl create namespace azureoperator-system + ``` + + b. Set the azureoperatorsettings secret + + ```shell + kubectl --namespace azureoperator-system \ + create secret generic azureoperatorsettings \ + --from-literal=AZURE_CLIENT_ID="$AZURE_CLIENT_ID" \ + --from-literal=AZURE_CLIENT_SECRET="$AZURE_CLIENT_SECRET" \ + --from-literal=AZURE_SUBSCRIPTION_ID="$AZURE_SUBSCRIPTION_ID" \ + --from-literal=AZURE_TENANT_ID="$AZURE_TENANT_ID" + ``` + + c. [Cert Manager](https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html) + + ```shell + kubectl get secret webhook-server-cert -n azureoperator-system -o yaml > certs.txt + ``` + you can use `https://inbrowser.tools/` and extract `ca.crt`, `tls.crt` and `tls.key` + +8. Install [kustomize](https://github.com/kubernetes-sigs/kustomize) using `make install-kustomize`. + +9. Install the azure_v1_eventhub CRD in the configured Kubernetes cluster folder ~/.kube/config, + + run `kubectl apply -f config/crd/bases` or `make install` + ### How to extend the operator and build your own images #### Updating the Azure operator: From 39b0d049542da12961c33f7d71c32e7eb7a4bbc0 Mon Sep 17 00:00:00 2001 From: priyakumarank <51063856+priyakumarank@users.noreply.github.com> Date: Fri, 23 Aug 2019 09:56:34 +1000 Subject: [PATCH 13/19] Update azure-pipelines.yaml (#119) * Update azure-pipelines.yaml --- devops/azure-pipelines.yaml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/devops/azure-pipelines.yaml b/devops/azure-pipelines.yaml index 09a244a1f14..a77a0e27d0d 100644 --- a/devops/azure-pipelines.yaml +++ b/devops/azure-pipelines.yaml @@ -86,7 +86,7 @@ steps: gocov convert coverage.txt > coverage.json gocov-xml < coverage.json > coverage.xml mkdir coverage - continueOnError: 'true' + continueOnError: 'false' displayName: 'Run unit tests' env: GO111MODULE: on @@ -125,7 +125,7 @@ steps: echo "all the pods should be running" make deploy make test-existing - continueOnError: 'true' + continueOnError: 'false' displayName: 'Set kind cluster and Run unit tests' env: GO111MODULE: on @@ -139,23 +139,27 @@ steps: - task: PublishTestResults@2 displayName: 'test cluster' - inputs: - failedTaskOnFailedTest: true + inputs: testRunner: JUnit testResultsFiles: $(System.DefaultWorkingDirectory)/**/report.xml + failTaskOnFailedTests: 'true' + failOnStandardError: 'true' - task: PublishTestResults@2 displayName: 'existing cluster' - inputs: - failedTaskOnFailedTest: true + inputs: testRunner: JUnit testResultsFiles: $(System.DefaultWorkingDirectory)/**/report-existing.xml + failTaskOnFailedTests: 'true' + failOnStandardError: 'true' - task: PublishCodeCoverageResults@1 inputs: codeCoverageTool: Cobertura summaryFileLocation: $(System.DefaultWorkingDirectory)/**/coverage.xml reportDirectory: $(System.DefaultWorkingDirectory)/**/coverage + failIfCoverageEmpty: 'true' + failOnStandardError: 'true' - script: docker build -t $(IMAGE_NAME) . # add options to this command to meet your needs condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/master')) From 594c5cda76b5e06d2db8df9642dd71164b65ff84 Mon Sep 17 00:00:00 2001 From: priyakumarank <51063856+priyakumarank@users.noreply.github.com> Date: Mon, 26 Aug 2019 15:45:31 +1000 Subject: [PATCH 14/19] fix tests (#140) * revert back // +kubebuilder:subresource:status changes - fix broken tests --- api/v1/eventhub_types.go | 1 - config/crd/bases/azure.microsoft.com_eventhubs.yaml | 2 -- main.go | 1 - 3 files changed, 4 deletions(-) diff --git a/api/v1/eventhub_types.go b/api/v1/eventhub_types.go index b3bdfb850ad..9b6f071a43c 100644 --- a/api/v1/eventhub_types.go +++ b/api/v1/eventhub_types.go @@ -72,7 +72,6 @@ type EventhubProperties struct { } // +kubebuilder:object:root=true -// +kubebuilder:subresource:status // Eventhub is the Schema for the eventhubs API type Eventhub struct { metav1.TypeMeta `json:",inline"` diff --git a/config/crd/bases/azure.microsoft.com_eventhubs.yaml b/config/crd/bases/azure.microsoft.com_eventhubs.yaml index f881df6e68a..76c5fd857ec 100644 --- a/config/crd/bases/azure.microsoft.com_eventhubs.yaml +++ b/config/crd/bases/azure.microsoft.com_eventhubs.yaml @@ -11,8 +11,6 @@ spec: kind: Eventhub plural: eventhubs scope: "" - subresources: - status: {} validation: openAPIV3Schema: description: Eventhub is the Schema for the eventhubs API diff --git a/main.go b/main.go index d899844d772..1ed35c2930f 100644 --- a/main.go +++ b/main.go @@ -118,7 +118,6 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "EventhubNamespace") os.Exit(1) } - err = (&controllers.ConsumerGroupReconciler{ Client: mgr.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("ConsumerGroup"), From 5223da915ab6943664d5846668e4f1797f48bd20 Mon Sep 17 00:00:00 2001 From: JasonTheDeveloper Date: Mon, 26 Aug 2019 16:53:18 +1000 Subject: [PATCH 15/19] Devcontainer to Help Onboard New People (#142) * add dev conatiner - wip * DevContainer up and running. * Removed `sleep 80` and replaced with `kubectl wait`. * Run `make set-kindcluster` from docker-compose. * Set timeout on wait. * Added `install-test-dependency` to makefile and dockerfile. * Update README - Create SP with contribution rights. * Updated README with details on using devcontainer. * Stuff that wanted me to commit. * Reverted changes made to `docker-build` in Makefile. --- .devcontainer/Dockerfile | 91 +++++++++++++++++++++++ .devcontainer/devcontainer.json | 24 ++++++ .devcontainer/docker-compose.yml | 24 ++++++ .gitignore | 1 - Makefile | 82 ++++++++++++-------- README.md | 99 +++++++++++++++++-------- config/default/manager_image_patch.yaml | 2 +- config/rbac/role.yaml | 68 ++++++++--------- go.mod | 3 + go.sum | 34 +++++++++ 10 files changed, 330 insertions(+), 98 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000000..925ea3756c3 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,91 @@ +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +FROM golang:1.12.5 + +# Avoid warnings by switching to noninteractive +ENV DEBIAN_FRONTEND=noninteractive + +# Configure apt, install packages and tools +RUN apt-get update \ + && apt-get -y install --no-install-recommends apt-utils 2>&1 \ + # + # Verify git, process tools, lsb-release (common in install instructions for CLIs) installed + && apt-get -y install git procps lsb-release \ + # + # Install gocode-gomod + && go get -x -d github.com/stamblerre/gocode 2>&1 \ + && go build -o gocode-gomod github.com/stamblerre/gocode \ + && mv gocode-gomod $GOPATH/bin/ \ + # + # Install Go tools + && go get -u -v \ + github.com/mdempsky/gocode \ + github.com/uudashr/gopkgs/cmd/gopkgs \ + github.com/ramya-rao-a/go-outline \ + github.com/acroca/go-symbols \ + github.com/godoctor/godoctor \ + golang.org/x/tools/cmd/guru \ + golang.org/x/tools/cmd/gorename \ + github.com/rogpeppe/godef \ + github.com/zmb3/gogetdoc \ + github.com/haya14busa/goplay/cmd/goplay \ + github.com/sqs/goreturns \ + github.com/josharian/impl \ + github.com/davidrjenni/reftools/cmd/fillstruct \ + github.com/fatih/gomodifytags \ + github.com/cweill/gotests/... \ + golang.org/x/tools/cmd/goimports \ + golang.org/x/lint/golint \ + golang.org/x/tools/cmd/gopls \ + github.com/alecthomas/gometalinter \ + honnef.co/go/tools/... \ + github.com/golangci/golangci-lint/cmd/golangci-lint \ + github.com/mgechev/revive \ + github.com/derekparker/delve/cmd/dlv 2>&1 \ + # + # Clean up + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + +RUN apt-get update \ + # + # Install Docker CE CLI + && apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common lsb-release \ + && curl -fsSL https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/gpg | apt-key add - 2>/dev/null \ + && add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]') $(lsb_release -cs) stable" \ + && apt-get update \ + && apt-get install -y docker-ce-cli \ + # + # Install kubectl + && curl -sSL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl \ + && chmod +x /usr/local/bin/kubectl \ + # + # Install Helm + && curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash - + +# Verify git, process tools installed +RUN apt-get -y install git procps wget nano zsh inotify-tools jq +RUN wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | zsh || true + +ENV PATH="/usr/local/kubebuilder/bin:${PATH}" + +ENV GO111MODULE=on + +# Set the default shell to bash instead of sh +ENV AZURE_CLIENT_ID="" +ENV AZURE_CLIENT_SECRET="" +ENV AZURE_SUBSCRIPTION_ID="" +ENV AZURE_TENANT_ID="" +ENV KUBECONFIG="/root/.kube/kind-config-kind" + +COPY ./Makefile ./ +RUN make install-kind +RUN make install-kubebuilder +RUN make install-kustomize +RUN make install-test-dependency + +ENV SHELL /bin/bash diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..2ff8884aa04 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml. +{ + "name": "Go", + "dockerComposeFile": "docker-compose.yml", + "service": "docker-in-docker", + "workspaceFolder": "/workspace", + "shutdownAction": "stopCompose", + "extensions": [ + "ms-azuretools.vscode-docker", + "ms-vscode.go" + ], + "settings": { + "terminal.integrated.shell.linux": "zsh", + "go.gopath": "/go", + "go.inferGopath": true, + "go.useLanguageServer": true, + "go.toolsEnvVars": { + "GO111MODULE": "on" + }, + "remote.extensionKind": { + "ms-azuretools.vscode-docker": "workspace" + } + } +} \ No newline at end of file diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 00000000000..d9c884e4c7d --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3' +services: + docker-in-docker: + build: + context: ../ + dockerfile: .devcontainer/Dockerfile + network_mode: "host" + ports: + - 5002:5001 + volumes: + # Update this to wherever you want VS Code to mount the folder of your project + - ..:/workspace + + # This lets you avoid setting up Git again in the container + - ~/.gitconfig:/root/.gitconfig + - ~/.ssh:/root/.ssh:ro # does not work on Windows! Will need to generate in container :( + # Forwarding the socket is optional, but lets docker work inside the container if you install the Docker CLI. + # See the docker-in-docker-compose definition for details on how to install it. + - /var/run/docker.sock:/var/run/docker.sock + + # Overrides default command so things don't shut down after the process ends - useful for debugging + command: bash -c "cd /workspace/ && make set-kindcluster && sleep infinity" + env_file: + - .env \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4fa06d60840..12c5e121e94 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ testlogs.txt .env __debug_bin .vscode -.devcontainer .DS_Store cover-existing.html coverage-existing.txt diff --git a/Makefile b/Makefile index a7425b076e5..eb5dbb1af75 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ - # Image URL to use all building/pushing image targets IMG ?= controller:latest # Produce CRDs that work back to Kubernetes 1.11 (no version conversion) @@ -74,22 +73,7 @@ else CONTROLLER_GEN=$(shell which controller-gen) endif -set-kindcluster: -ifeq (,$(shell which kind)) - @echo "installing kind" - GO111MODULE="on" go get sigs.k8s.io/kind@v0.4.0 -else - @echo "kind has been installed" -endif - #KUBECONFIG=$(shell kind get kubeconfig-path --name="kind") - #$(shell export KUBECONFIG="$(kind get kubeconfig-path --name="kind")") -ifeq ($(shell kind get kubeconfig-path --name="kind"),$(KUBECONFIG)) - @echo "kubeconfig-path points to kind path" -else - @echo "please run below command in your shell and then re-run make set-kindcluster" - @echo "\e[31mexport KUBECONFIG=$(shell kind get kubeconfig-path --name="kind")\e[0m" - @exit 111 -endif +create-kindcluster: ifeq (,$(shell kind get clusters)) @echo "no kind cluster" else @@ -98,10 +82,21 @@ else endif @echo "creating kind cluster" kind create cluster - export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" + +set-kindcluster: install-kind +ifeq (${shell kind get kubeconfig-path --name="kind"},${KUBECONFIG}) + @echo "kubeconfig-path points to kind path" +else + @echo "please run below command in your shell and then re-run make set-kindcluster" + @echo "\e[31mexport KUBECONFIG=$(shell kind get kubeconfig-path --name="kind")\e[0m" + @exit 111 +endif + make create-kindcluster + @echo "getting value of KUBECONFIG" - kind get kubeconfig-path --name="kind" @echo ${KUBECONFIG} + @echo "getting value of kind kubeconfig-path" + kubectl cluster-info kubectl create namespace azureoperator-system kubectl --namespace azureoperator-system \ @@ -111,33 +106,60 @@ endif --from-literal=AZURE_SUBSCRIPTION_ID=${AZURE_SUBSCRIPTION_ID} \ --from-literal=AZURE_TENANT_ID=${AZURE_TENANT_ID} - kubectl create namespace cert-manager - kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true - kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.9.0/cert-manager.yaml + make install-cert-manager #create image and load it into cluster IMG="docker.io/controllertest:1" make docker-build kind load docker-image docker.io/controllertest:1 --loglevel "trace" make install kubectl get namespaces - @echo "sleep 80 seconds to get the cert pods running" - sleep 80 - @echo "end of sleep" kubectl get pods --namespace cert-manager + @echo "Waiting for cert-manager to be ready" + kubectl wait pod -n cert-manager --for condition=ready --timeout=60s --all @echo "all the pods should be running" make deploy + sed -i'' -e 's@image: .*@image: '"IMAGE_URL"'@' ./config/default/manager_image_patch.yaml + +install-kind: +ifeq (,$(shell which kind)) + @echo "installing kind" + GO111MODULE="on" go get sigs.k8s.io/kind@v0.4.0 +else + @echo "kind has been installed" +endif + +install-kubebuilder: +ifeq (,$(shell which kubebuilder)) + @echo "installing kubebuilder" + # download kubebuilder and extract it to tmp + curl -sL https://go.kubebuilder.io/dl/2.0.0-rc.0/$(shell go env GOOS)/$(shell go env GOARCH) | tar -xz -C /tmp/ + # move to a long-term location and put it on your path + # (you'll need to set the KUBEBUILDER_ASSETS env var if you put it somewhere else) + mv /tmp/kubebuilder_2.0.0-rc.0_$(shell go env GOOS)_$(shell go env GOARCH) /usr/local/kubebuilder + export PATH=$PATH:/usr/local/kubebuilder/bin +else + @echo "kubebuilder has been installed" +endif install-kustomize: ifeq (,$(shell which kustomize)) @echo "installing kustomize" # download kustomize - sudo mkdir -p /usr/local/kustomize/ - sudo curl -o /usr/local/kubebuilder/bin/kustomize -sL "https://go.kubebuilder.io/kustomize/$(shell go env GOOS)/$(shell go env GOARCH)" + curl -o /usr/local/kubebuilder/bin/kustomize -sL "https://go.kubebuilder.io/kustomize/$(shell go env GOOS)/$(shell go env GOARCH)" # set permission - sudo chmod a+x /usr/local/kubebuilder/bin/kustomize - # export path + chmod a+x /usr/local/kubebuilder/bin/kustomize $(shell which kustomize) - else @echo "kustomize has been installed" endif + +install-cert-manager: + kubectl create namespace cert-manager + kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true + kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.9.0/cert-manager.yaml + +install-test-dependency: + go get -u github.com/jstemmer/go-junit-report \ + && go get github.com/axw/gocov/gocov \ + && go get github.com/AlekSi/gocov-xml \ + && go get golang.org/x/tools/cmd/cover \ No newline at end of file diff --git a/README.md b/README.md index ec3a37e4673..316878e9c00 100644 --- a/README.md +++ b/README.md @@ -9,27 +9,31 @@ Kubernetes offers the facility of extending it's API through the concept of 'Operators' ([Introducing Operators: Putting Operational Knowledge into Software](https://coreos.com/blog/introducing-operators.html)). This repository contains the resources and code to provision a Resource group and Azure Event Hub using Kubernetes operator. The Azure Operator comprises of: -- The golang application is a Kubernetes controller that watches Customer Resource Definitions (CRDs) that define a Resource Group and Event Hub + +- The golang application is a Kubernetes controller that watches Customer Resource Definitions (CRDs) that define a Resource Group and Event Hub The project was built using 1. [Kubebuilder](https://book.kubebuilder.io/) +## Building and Running from Source + ### Prerequisites And Assumptions 1. You have GoLang installed. -2. You have the kubectl command line (kubectl CLI) installed. -3. You have acess to a Kubernetes cluster. - It can be a local hosted Cluster like - [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/), - [Kind](https://github.com/kubernetes-sigs/kind) or, Docker for desktop installed locally with RBAC enabled. - If you opt for Azure Kubernetes Service ([AKS](https://azure.microsoft.com/en-au/services/kubernetes-service/)), you can use: +2. [Docker](https://docs.docker.com/install/) is installed and running. +3. You have the kubectl command line (kubectl CLI) installed. +4. You have access to a Kubernetes cluster. + - It can be a local hosted Cluster like + [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/), + [Kind](https://github.com/kubernetes-sigs/kind) or Docker for desktop installed locally with RBAC enabled. + - If you opt for Azure Kubernetes Service ([AKS](https://azure.microsoft.com/en-au/services/kubernetes-service/)), you can use: `az aks get-credentials --resource-group $RG_NAME --name $Cluster_NAME` - Kubectl: Client version 1.14 Server Version 1.12 - - It is recommended to use [Kind](https://github.com/kubernetes-sigs/kind) as it is needed for testing Webhooks. -4. Install [Kubebuilder](https://book.kubebuilder.io/), following the linked installation instructions. -5. [kustomize](https://github.com/kubernetes-sigs/kustomize) is also needed. This must be installed via `make install-kustomize` (see section below). + - Kubectl: Client version 1.14 Server Version 1.12 + + **Note:** it is recommended to use [Kind](https://github.com/kubernetes-sigs/kind) as it is needed for testing Webhooks. +5. Install [Kubebuilder](https://book.kubebuilder.io/), following the linked installation instructions. +6. [Kustomize](https://github.com/kubernetes-sigs/kustomize) is also needed. This must be installed via `make install-kustomize` (see section below). Basic commands to check your cluster @@ -40,11 +44,36 @@ Basic commands to check your cluster kubectl get pods -n kube-system ``` -### Building and Running from Source +### Quick Start + +If you're using VSCode with [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extensions installed, you quickly have you're environment set up and ready to go with everything you need to get started. + +1. Open this project in VSCode. +2. Inside `.devcontainer`, create a file called `.env` and using the following template, copy your Service Principal's details. + + ```txt + AZURE_CLIENT_ID= + + AZURE_CLIENT_SECRET= + + AZURE_SUBSCRIPTION_ID= + + AZURE_TENANT_ID= + ``` + +3. Open the Command Pallet (`Command+Shift+P` on MacOS or `CTRL+Shift+P` on Windows), type `Remote-Containers: Open Folder in Container...` and hit enter. +4. VSCode will relaunch and start building our development container. This will install all the necessary dependencies required for you to begin developing. +5. Once the container has finished building, you can now start testing your Azure Service Operator within your own local kubernetes environment. + +**Note**: after the DevContainer has finished building, the kind cluster will initialising and installing the Azure Service Operator in the background. This will take some time before it is available. + +To see when the kind cluster is ready, use `docker ps -a` to list your running containers, look for `IMAGE` with the name `azure-service-operator_devcontainer_docker-in-docker...`. Using that image's `CONTAINER ID`, use `docker logs -f CONTAINER ID` to view the logs from the container setting up your cluster. + +### Getting started -1. Clone the repo from the following folder `/src/github.com/Azure`. +1. Clone the repository from the following folder `/src/github.com/Azure`. -2. Make sure the envronment variable `GO111MODULE=on` is set. +2. Make sure the environment variable `GO111MODULE=on` is set. 3. Update the values in `azure_v1_eventhub.yaml` to reflect the resource group and event hub you want to provision @@ -62,34 +91,39 @@ Basic commands to check your cluster 5. Create a Service Principal If you don't have a Service Principal create one from the Azure CLI: + ```bash - az ad sp create-for-rbac --skip-assignment + az ad sp create-for-rbac --role Contributor ``` + Then make sure this service principal has rights assigned to provision resources on your Azure account. -6. Set the environment variables `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `REQUEUE_AFTER` - If you are running it on Windows the environment variables should not have quotes. +6. Set the environment variables `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `REQUEUE_AFTER`. + + If you are running it on Windows the environment variables should not have quotes. + It should be set in this way: - SET AZURE_TENANT_ID=11xxxx-xxx-xxx-xxx-xxxxx + `SET AZURE_TENANT_ID=11xxxx-xxx-xxx-xxx-xxxxx` and the VSCode should be run from the same session/command window 7. Set up the Cluster If you are using Kind: + ```shell make set-kindcluster ``` - + If you are not using Kind, it's a manual process, as follows: - + a. Create the namespace - + ```shell kubectl create namespace azureoperator-system ``` b. Set the azureoperatorsettings secret - + ```shell kubectl --namespace azureoperator-system \ create secret generic azureoperatorsettings \ @@ -98,25 +132,26 @@ Basic commands to check your cluster --from-literal=AZURE_SUBSCRIPTION_ID="$AZURE_SUBSCRIPTION_ID" \ --from-literal=AZURE_TENANT_ID="$AZURE_TENANT_ID" ``` - + c. [Cert Manager](https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html) - + ```shell kubectl get secret webhook-server-cert -n azureoperator-system -o yaml > certs.txt ``` + you can use `https://inbrowser.tools/` and extract `ca.crt`, `tls.crt` and `tls.key` 8. Install [kustomize](https://github.com/kubernetes-sigs/kustomize) using `make install-kustomize`. -9. Install the azure_v1_eventhub CRD in the configured Kubernetes cluster folder ~/.kube/config, - +9. Install the azure_v1_eventhub CRD in the configured Kubernetes cluster folder ~/.kube/config, + run `kubectl apply -f config/crd/bases` or `make install` - -### How to extend the operator and build your own images -#### Updating the Azure operator: +## How to extend the operator and build your own images + +### Updating the Azure operator -This Repo is generated by [Kubebuilder](https://book.kubebuilder.io/). +This repository is generated by [Kubebuilder](https://book.kubebuilder.io/). To Extend the operator `github.com/Azure/azure-service-operator`: @@ -130,7 +165,7 @@ To Extend the operator `github.com/Azure/azure-service-operator`: 8. Build `make build` 9. Deploy `make deploy` -# Contributing +## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index 9e09ecc83ec..6e060cb4438 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -8,7 +8,7 @@ spec: spec: containers: # Change the value of image field below to your controller image URL - - image: controller:latest + - image: IMAGE_URL name: manager env: - name: AZURE_CLIENT_ID diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 11d72ca7ada..f4216c8c96d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -9,15 +9,7 @@ rules: - apiGroups: - azure.microsoft.com resources: - - consumergroups/status - verbs: - - get - - patch - - update -- apiGroups: - - azure.microsoft.com - resources: - - keyvaults + - eventhubs verbs: - create - delete @@ -29,15 +21,15 @@ rules: - apiGroups: - azure.microsoft.com resources: - - resourcegroups/status + - eventhubs/status verbs: - get - patch - update - apiGroups: - - "" + - azure.microsoft.com resources: - - secrets + - keyvaults verbs: - create - delete @@ -47,16 +39,17 @@ rules: - update - watch - apiGroups: - - "" + - azure.microsoft.com resources: - - events + - keyvaults/status verbs: - - create - - watch + - get + - patch + - update - apiGroups: - - azure.microsoft.com + - "" resources: - - resourcegroups + - secrets verbs: - create - delete @@ -80,19 +73,15 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubs + - consumergroups/status verbs: - - create - - delete - get - - list - patch - update - - watch - apiGroups: - azure.microsoft.com resources: - - eventhubs/status + - eventhubnamespaces/status verbs: - get - patch @@ -100,31 +89,30 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubnamespaces/status + - resourcegroups/status verbs: - get - patch - update - apiGroups: - - apps + - "" resources: - - deployments + - events verbs: - create - - delete - - get - - list - - patch - - update - watch - apiGroups: - apps resources: - - deployments/status + - deployments verbs: + - create + - delete - get + - list - patch - update + - watch - apiGroups: - azure.microsoft.com resources: @@ -140,11 +128,15 @@ rules: - apiGroups: - azure.microsoft.com resources: - - keyvaults/status + - resourcegroups verbs: + - create + - delete - get + - list - patch - update + - watch - apiGroups: - azure.microsoft.com resources: @@ -152,3 +144,11 @@ rules: verbs: - create - patch +- apiGroups: + - apps + resources: + - deployments/status + verbs: + - get + - patch + - update diff --git a/go.mod b/go.mod index e7b0fa90be7..ad261c7cb3b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Azure/azure-service-operator go 1.12 require ( + github.com/AlekSi/gocov-xml v0.0.0-20190121064608-3a14fb1c4737 // indirect github.com/Azure-Samples/azure-sdk-for-go-samples v0.0.0-20190805235326-79e3f3af791c github.com/Azure/azure-sdk-for-go v32.0.0+incompatible github.com/Azure/go-autorest/autorest v0.5.0 @@ -10,6 +11,7 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.1.0 github.com/Azure/go-autorest/autorest/to v0.2.0 github.com/Azure/go-autorest/autorest/validation v0.1.0 // indirect + github.com/axw/gocov v1.0.0 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20190724012636-11b2859924c1 github.com/go-logr/logr v0.1.0 github.com/gobuffalo/envy v1.7.0 @@ -22,6 +24,7 @@ require ( golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 // indirect golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/sys v0.0.0-20190621203818-d432491b9138 // indirect + golang.org/x/tools v0.0.0-20190822191935-b1e2c8edcefd // indirect k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible diff --git a/go.sum b/go.sum index a4a1b902d7c..24e6276b5ab 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc= contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= +github.com/AlekSi/gocov-xml v0.0.0-20190121064608-3a14fb1c4737 h1:JZHBkt0GhM+ARQykshqpI49yaWCHQbJonH3XpDTwMZQ= +github.com/AlekSi/gocov-xml v0.0.0-20190121064608-3a14fb1c4737/go.mod h1:w1KSuh2JgIL3nyRiZijboSUwbbxOrTzWwyWVFUHtXBQ= github.com/Azure-Samples/azure-sdk-for-go-samples v0.0.0-20190717201944-ad38292fb10f/go.mod h1:fHSkenNUlKPxCdWPfkfEubZHu7B3er09X30oJ6db8Fg= github.com/Azure-Samples/azure-sdk-for-go-samples v0.0.0-20190805235326-79e3f3af791c h1:ND9JhvcUO30ENxUh4zfMHom+pszWyFqM5TCbyE/dXnM= github.com/Azure-Samples/azure-sdk-for-go-samples v0.0.0-20190805235326-79e3f3af791c/go.mod h1:fHSkenNUlKPxCdWPfkfEubZHu7B3er09X30oJ6db8Fg= @@ -43,12 +45,18 @@ github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvd github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/axw/gocov v1.0.0 h1:YsqYR66hUmilVr23tu8USgnJIJvnwh3n7j5zRn7x4LU= +github.com/axw/gocov v1.0.0/go.mod h1:LvQpEYiwwIb2nYkXY2fDWhg9/AsYqkhmrCshjlUJECE= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4= @@ -67,7 +75,9 @@ github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -82,9 +92,17 @@ github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7 github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0 h1:yJW3HCkTHg7NOA+gZ83IPHzUSnUzGXhGmsdiCcMexbA= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4= +github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= @@ -119,6 +137,8 @@ github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= @@ -145,6 +165,7 @@ github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswD github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -157,6 +178,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marstr/collection v1.0.1 h1:j61osRfyny7zxBlLRtoCvOZ2VX7HEyybkZcsLNLJ0z0= github.com/marstr/collection v1.0.1/go.mod h1:HHDXVxjLO3UYCBXJWY+J/ZrxCUOYqrO66ob1AzIsmYA= github.com/marstr/randname v0.0.0-20181206212954-d5b0f288ab8c h1:JE+MDz5rhFN5EC9Dj/N8dLYKboTWm6FXeWhnyKVj0vA= @@ -221,6 +244,8 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -263,6 +288,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -316,6 +342,13 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190501045030-23463209683d h1:D7DVZUZEUgsSIDTivnUtVeGfN5AvhDIKtdIZAqx0ieE= golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774 h1:CQVOmarCBFzTx0kbOU0ru54Cvot8SdSrNYjZPhQl+gk= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190822000311-fc82fb2afd64 h1:4EN1tY9aQxwLGYHWT5WdQN56Xzbwlg2UTINDbZ04l10= +golang.org/x/tools v0.0.0-20190822000311-fc82fb2afd64/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190822191935-b1e2c8edcefd h1:sl3cZ9UhakOcf0k3nWTLpJFHPGbvWf5Cao9HxvzkDos= +golang.org/x/tools v0.0.0-20190822191935-b1e2c8edcefd/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.0.0 h1:OyHbl+7IOECpPKfVK42oFr6N7+Y2dR+Jsb/IiDV3hOo= gomodules.xyz/jsonpatch/v2 v2.0.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8= @@ -371,6 +404,7 @@ k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c= k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c h1:3KSCztE7gPitlZmWbNwue/2U0YruD65DqX3INopDAQM= k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/kube-openapi v0.0.0-20190510232812-a01b7d5d6c22/go.mod h1:iU+ZGYsNlvU9XKUSso6SQfKTCCw7lFduMZy26Mgr2Fw= k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208 h1:5sW+fEHvlJI3Ngolx30CmubFulwH28DhKjGf70Xmtco= k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g90ETOiA6rfLV1Y= From 5b6278da280e55b7c5721c7e2968a7b51d733fc6 Mon Sep 17 00:00:00 2001 From: William Mortl <32373900+WilliamMortlMicrosoft@users.noreply.github.com> Date: Mon, 26 Aug 2019 16:25:53 -0600 Subject: [PATCH 16/19] pass future where possible instead of bool (#121) * first commit on Amanda's branch * first * before properties * test not tested * test works * unit tests work, needs firewall rules * addresses feedback * erin's feedback * janani's change, pass future * async works much better now * janani feedback * screwed up interface prototype --- go.mod | 2 +- go.sum | 1 + .../sqlclient/endtoend_test.go | 131 +++++++++++++ pkg/resourcemanager/sqlclient/gosdkclient.go | 16 ++ pkg/resourcemanager/sqlclient/main_test.go | 72 +++++++ .../sqlclient/resourceclient.go | 20 ++ .../sqlclient/sqlclient_godsk.go | 181 ++++++++++++++++++ .../sqlclient/sqlproperties.go | 141 ++++++++++++++ 8 files changed, 563 insertions(+), 1 deletion(-) create mode 100644 pkg/resourcemanager/sqlclient/endtoend_test.go create mode 100644 pkg/resourcemanager/sqlclient/gosdkclient.go create mode 100644 pkg/resourcemanager/sqlclient/main_test.go create mode 100644 pkg/resourcemanager/sqlclient/resourceclient.go create mode 100644 pkg/resourcemanager/sqlclient/sqlclient_godsk.go create mode 100644 pkg/resourcemanager/sqlclient/sqlproperties.go diff --git a/go.mod b/go.mod index ad261c7cb3b..24079a36543 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/marstr/randname v0.0.0-20181206212954-d5b0f288ab8c github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega v1.5.0 - github.com/satori/go.uuid v1.2.0 + github.com/satori/go.uuid v1.2.0 // indirect golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 // indirect golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/sys v0.0.0-20190621203818-d432491b9138 // indirect diff --git a/go.sum b/go.sum index 24e6276b5ab..fac5a314131 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ github.com/Azure/azure-sdk-for-go v30.1.0+incompatible h1:HyYPft8wXpxMd0kfLtXo6e github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v32.0.0+incompatible h1:lkD8YqWFj4DVw65CHwtjOtTnlMD2LLy9z/f8I+BBsVM= github.com/Azure/azure-sdk-for-go v32.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v32.5.0+incompatible h1:Hn/DsObfmw0M7dMGS/c0MlVrJuGFzHzOpBWL89acR68= github.com/Azure/go-autorest v12.4.3+incompatible h1:tCkdkgLZqAk+43nZu3wda9n413Q2g+z7xp1wmjiJTPY= github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= github.com/Azure/go-autorest/autorest v0.2.0 h1:zBtSTOQTtjzHVRe+mhkiHvHwRTKHhjBEyo1m6DfI3So= diff --git a/pkg/resourcemanager/sqlclient/endtoend_test.go b/pkg/resourcemanager/sqlclient/endtoend_test.go new file mode 100644 index 00000000000..119de4a2812 --- /dev/null +++ b/pkg/resourcemanager/sqlclient/endtoend_test.go @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +package sqlclient + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" + "github.com/Azure/azure-service-operator/pkg/resourcemanager/resources" + "github.com/Azure/azure-service-operator/pkg/util" + "github.com/Azure/go-autorest/autorest/to" +) + +// TestCreateOrUpdateSQLServer tests creating and delete a SQL server +func TestCreateOrUpdateSQLServer(t *testing.T) { + + var groupName = config.GenerateGroupName("SQLCreateDeleteTest") + config.SetGroupName(groupName) + + ctx := context.Background() + defer resources.Cleanup(ctx) + + // create the resource group + _, err := resources.CreateGroup(ctx, config.GroupName()) + if err != nil { + util.PrintAndLog(err.Error()) + t.FailNow() + } + + // create the Go SDK client with relevant info + sdk := GoSDKClient{ + Ctx: ctx, + ResourceGroupName: groupName, + ServerName: generateName("sqlsrvtest"), + Location: "eastus2", + } + + // create the server + sqlServerProperties := SQLServerProperties{ + AdministratorLogin: to.StringPtr("Moss"), + AdministratorLoginPassword: to.StringPtr("TheITCrowd_{01}!"), + AllowAzureServicesAccess: true, + } + + // wait for server to be created, then only proceed once activated + for { + time.Sleep(time.Second) + server, err := sdk.CreateOrUpdateSQLServer(sqlServerProperties) + if err == nil { + if *server.State == "Ready" { + util.PrintAndLog("sql server ready") + break + } + } else { + if sdk.IsAsyncNotCompleted(err) { + util.PrintAndLog("waiting for sql server to be ready...") + continue + } else { + util.PrintAndLog(fmt.Sprintf("cannot create sql server: %v", err)) + t.FailNow() + break + } + } + } + + // create a DB + sqlDBProperties := SQLDatabaseProperties{ + DatabaseName: "testDB", + Edition: Basic, + } + + // wait for db to be created, then only proceed once activated + for { + time.Sleep(time.Second) + db, err := sdk.CreateOrUpdateDB(sqlDBProperties) + if err == nil { + if *db.Status == "Online" { + util.PrintAndLog("db ready") + break + } + } else { + if sdk.IsAsyncNotCompleted(err) { + util.PrintAndLog("waiting for db to be ready...") + continue + } else { + util.PrintAndLog(fmt.Sprintf("cannot create db: %v", err)) + t.FailNow() + break + } + } + } + + // delete the DB + time.Sleep(time.Second) + response, err := sdk.DeleteDB("testDB") + if err == nil { + if response.StatusCode == 200 { + util.PrintAndLog("db deleted") + } + } else { + util.PrintAndLog(fmt.Sprintf("cannot delete db: %v", err)) + t.FailNow() + } + + // delete the server + for { + time.Sleep(time.Second) + response, err := sdk.DeleteSQLServer() + if err == nil { + if response.StatusCode == 200 { + util.PrintAndLog("sql server deleted") + break + } + } else { + if sdk.IsAsyncNotCompleted(err) { + util.PrintAndLog("waiting for sql server to be deleted...") + continue + } else { + util.PrintAndLog(fmt.Sprintf("cannot delete sql server: %v", err)) + t.FailNow() + break + } + } + } +} diff --git a/pkg/resourcemanager/sqlclient/gosdkclient.go b/pkg/resourcemanager/sqlclient/gosdkclient.go new file mode 100644 index 00000000000..f5ddbb84e62 --- /dev/null +++ b/pkg/resourcemanager/sqlclient/gosdkclient.go @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +package sqlclient + +import "context" + +// GoSDKClient is used to pass information to an implemetation of the ResourceClient interface that wraps calls to the Go SDK for Azure. +type GoSDKClient struct { + Ctx context.Context + ResourceGroupName string + ServerName string + Location string +} diff --git a/pkg/resourcemanager/sqlclient/main_test.go b/pkg/resourcemanager/sqlclient/main_test.go new file mode 100644 index 00000000000..61317031f07 --- /dev/null +++ b/pkg/resourcemanager/sqlclient/main_test.go @@ -0,0 +1,72 @@ +package sqlclient + +import ( + "context" + "fmt" + "os" + "strings" + "testing" + + "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" + "github.com/Azure/azure-service-operator/pkg/resourcemanager/resources" + "github.com/Azure/azure-service-operator/pkg/util" + "github.com/marstr/randname" +) + +func addLocalEnvAndParse() error { + // parse env at top-level (also controls dotenv load) + err := config.ParseEnvironment() + if err != nil { + return fmt.Errorf("failed to add top-level env: %v", err.Error()) + } + return nil +} + +func setup() error { + var err error + err = addLocalEnvAndParse() + if err != nil { + return err + } + + return nil +} + +func teardown() error { + if !config.KeepResources() { + // does not wait + _, err := resources.DeleteGroup(context.Background(), config.GroupName()) + if err != nil { + + // this indicates that the resource group wasn't created + // no worries! + util.PrintAndLog(fmt.Sprintf("could not teardown resource group, this may not be a problem: %v\n", err)) + } + } + return nil +} + +// test helpers +func generateName(prefix string) string { + return strings.ToLower(randname.GenerateWithPrefix(prefix, 5)) +} + +// TestMain is the main entry point for tests +func TestMain(m *testing.M) { + var err error + var code int + + err = setup() + if err != nil { + util.PrintAndLog(fmt.Sprintf("could not set up environment: %v\n", err)) + } + + code = m.Run() + + err = teardown() + if err != nil { + util.PrintAndLog(fmt.Sprintf("could not tear down environment: %v\n; original exit code: %v\n", err, code)) + } + + os.Exit(code) +} diff --git a/pkg/resourcemanager/sqlclient/resourceclient.go b/pkg/resourcemanager/sqlclient/resourceclient.go new file mode 100644 index 00000000000..61dd253710c --- /dev/null +++ b/pkg/resourcemanager/sqlclient/resourceclient.go @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +package sqlclient + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" + "github.com/Azure/go-autorest/autorest" +) + +// ResourceClient contains the helper functions for interacting with SQL servers / databases +type ResourceClient interface { + CreateOrUpdateSQLServer(properties SQLServerProperties) (result sql.Server, err error) + CreateOrUpdateDB(properties SQLDatabaseProperties) (result sql.Database, err error) + DeleteDB(databaseName string) (result autorest.Response, err error) + DeleteSQLServer() (result autorest.Response, err error) + IsAsyncNotCompleted(err error) (result bool) +} diff --git a/pkg/resourcemanager/sqlclient/sqlclient_godsk.go b/pkg/resourcemanager/sqlclient/sqlclient_godsk.go new file mode 100644 index 00000000000..2b0f0966439 --- /dev/null +++ b/pkg/resourcemanager/sqlclient/sqlclient_godsk.go @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +package sqlclient + +import ( + "net/http" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" + "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" + "github.com/Azure/azure-service-operator/pkg/resourcemanager/iam" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/to" +) + +// getGoServersClient retrieves a ServersClient +func getGoServersClient() sql.ServersClient { + serversClient := sql.NewServersClient(config.SubscriptionID()) + a, _ := iam.GetResourceManagementAuthorizer() + serversClient.Authorizer = a + serversClient.AddToUserAgent(config.UserAgent()) + return serversClient +} + +// getGoDbClient retrieves a DatabasesClient +func getGoDbClient() sql.DatabasesClient { + dbClient := sql.NewDatabasesClient(config.SubscriptionID()) + a, _ := iam.GetResourceManagementAuthorizer() + dbClient.Authorizer = a + dbClient.AddToUserAgent(config.UserAgent()) + return dbClient +} + +// CreateOrUpdateSQLServer creates a SQL server in Azure +func (sdk GoSDKClient) CreateOrUpdateSQLServer(properties SQLServerProperties) (result sql.Server, err error) { + serversClient := getGoServersClient() + serverProp := SQLServerPropertiesToServer(properties) + + // check to see if the server exists, if it does then short-circuit + server, err := serversClient.Get( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + ) + if err == nil && *server.State == "Ready" { + return server, nil + } + + // issue the creation + future, err := serversClient.CreateOrUpdate( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + sql.Server{ + Location: to.StringPtr(config.Location()), + ServerProperties: &serverProp, + }) + if err != nil { + return result, err + } + + return future.Result(serversClient) +} + +// CreateOrUpdateDB creates or updates a DB in Azure +func (sdk GoSDKClient) CreateOrUpdateDB(properties SQLDatabaseProperties) (result sql.Database, err error) { + dbClient := getGoDbClient() + dbProp := SQLDatabasePropertiesToDatabase(properties) + + // check to see if the db exists, if it does then short-circuit + db, err := dbClient.Get( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + properties.DatabaseName, + "serviceTierAdvisors, transparentDataEncryption", + ) + if err == nil && *db.Status == "Online" { + return db, nil + } + + future, err := dbClient.CreateOrUpdate( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + properties.DatabaseName, + sql.Database{ + Location: to.StringPtr(sdk.Location), + DatabaseProperties: &dbProp, + }) + if err != nil { + return result, err + } + + // TODO: Will needs to remove the sync + err = future.WaitForCompletionRef( + sdk.Ctx, + dbClient.Client, + ) + if err != nil { + return result, err + } + + return future.Result(dbClient) +} + +// DeleteDB deletes a DB +func (sdk GoSDKClient) DeleteDB(databaseName string) (result autorest.Response, err error) { + dbClient := getGoDbClient() + + // check to see if the db exists, if it does then short-circuit + _, err = dbClient.Get( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + databaseName, + "serviceTierAdvisors, transparentDataEncryption", + ) + if err != nil { + result = autorest.Response{ + Response: &http.Response{ + StatusCode: 200, + }, + } + return result, nil + } + + result, err = dbClient.Delete( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + databaseName, + ) + + return result, err +} + +// DeleteSQLServer deletes a SQL server +func (sdk GoSDKClient) DeleteSQLServer() (result autorest.Response, err error) { + serversClient := getGoServersClient() + + // check to see if the server exists, if it doesn't then short-circuit + _, err = serversClient.Get( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + ) + if err != nil { + result = autorest.Response{ + Response: &http.Response{ + StatusCode: 200, + }, + } + return result, nil + } + + future, err := serversClient.Delete( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + ) + if err != nil { + return result, err + } + + return future.Result(serversClient) +} + +// IsAsyncNotCompleted returns true if the error is due to async not completed +func (sdk GoSDKClient) IsAsyncNotCompleted(err error) (result bool) { + result = false + if err != nil && strings.Contains(err.Error(), "asynchronous operation has not completed") { + result = true + } else if strings.Contains(err.Error(), "is busy with another operation") { + result = true + } + return result +} diff --git a/pkg/resourcemanager/sqlclient/sqlproperties.go b/pkg/resourcemanager/sqlclient/sqlproperties.go new file mode 100644 index 00000000000..23f1116d3cc --- /dev/null +++ b/pkg/resourcemanager/sqlclient/sqlproperties.go @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft and contributors. All rights reserved. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +package sqlclient + +import ( + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" +) + +// DBEdition - wraps: https://godoc.org/github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql#DatabaseEdition +type DBEdition byte + +const ( + // Basic ... + Basic DBEdition = 0 + // Business ... + Business DBEdition = 1 + // BusinessCritical ... + BusinessCritical DBEdition = 2 + // DataWarehouse ... + DataWarehouse DBEdition = 3 + // Free ... + Free DBEdition = 4 + // GeneralPurpose ... + GeneralPurpose DBEdition = 5 + // Hyperscale ... + Hyperscale DBEdition = 6 + // Premium ... + Premium DBEdition = 7 + // PremiumRS ... + PremiumRS DBEdition = 8 + // Standard ... + Standard DBEdition = 9 + // Stretch ... + Stretch DBEdition = 10 + // System ... + System DBEdition = 11 + // System2 ... + System2 DBEdition = 12 + // Web ... + Web DBEdition = 13 +) + +// SQLServerProperties contains values needed for adding / updating SQL servers, +// wraps: https://godoc.org/github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql#Server +// also wraps: https://godoc.org/github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql#ServerProperties +type SQLServerProperties struct { + + // AdministratorLogin - Administrator username for the server. Once created it cannot be changed. + AdministratorLogin *string + + // AdministratorLoginPassword - The administrator login password (required for server creation). + AdministratorLoginPassword *string + + // AllowAzureServicesAccess - allow Azure services and resources to access this server + AllowAzureServicesAccess bool +} + +// SQLDatabaseProperties contains values needed for adding / updating SQL servers, +// wraps: https://godoc.org/github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql#Database +// also wraps: https://godoc.org/github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql#DatabaseProperties +type SQLDatabaseProperties struct { + + // DatabaseName is the name of the database + DatabaseName string + + // Edition - The edition of the database. The DatabaseEditions enumeration contains all the valid editions. If createMode is NonReadableSecondary or OnlineSecondary, this value is ignored. + // + // The list of SKUs may vary by region and support offer. To determine the SKUs (including the SKU name, tier/edition, family, and capacity) that are available to your subscription in an Azure region, use the `Capabilities_ListByLocation` REST API or one of the following commands: + // + // ```azurecli + // az sql db list-editions -l -o table + // ```` + // + // ```powershell + // Get-AzSqlServerServiceObjective -Location + // ```` + // . Possible values include: 'Web', 'Business', 'Basic', 'Standard', 'Premium', 'PremiumRS', 'Free', 'Stretch', 'DataWarehouse', 'System', 'System2', 'GeneralPurpose', 'BusinessCritical', 'Hyperscale' + Edition DBEdition +} + +// SQLServerPropertiesToServer translates SQLServerProperties to ServerProperties +func SQLServerPropertiesToServer(properties SQLServerProperties) (result sql.ServerProperties) { + + result = sql.ServerProperties{ + AdministratorLogin: properties.AdministratorLogin, + AdministratorLoginPassword: properties.AdministratorLoginPassword, + } + + return result +} + +// SQLDatabasePropertiesToDatabase translates SQLDatabaseProperties to DatabaseProperties +func SQLDatabasePropertiesToDatabase(properties SQLDatabaseProperties) (result sql.DatabaseProperties) { + + result = sql.DatabaseProperties{ + Edition: translateDBEdition(properties.Edition), + } + + return result +} + +// translateDBEdition translates enums +func translateDBEdition(in DBEdition) (result sql.DatabaseEdition) { + switch in { + case 0: + result = sql.Basic + case 1: + result = sql.Business + case 2: + result = sql.BusinessCritical + case 3: + result = sql.DataWarehouse + case 4: + result = sql.Free + case 5: + result = sql.GeneralPurpose + case 6: + result = sql.Hyperscale + case 7: + result = sql.Premium + case 8: + result = sql.PremiumRS + case 9: + result = sql.Standard + case 10: + result = sql.Stretch + case 11: + result = sql.System + case 12: + result = sql.System2 + case 13: + result = sql.Web + default: + result = sql.Free + } + + return result +} From 15d092d61783d0a61bcbfc2a18c3d018538ea347 Mon Sep 17 00:00:00 2001 From: priyakumarank <51063856+priyakumarank@users.noreply.github.com> Date: Wed, 28 Aug 2019 13:33:06 +1000 Subject: [PATCH 17/19] randomize the resources names used in tests (#152) --- controllers/consumergroup_controller_test.go | 16 ++++++++++------ controllers/eventhub_controller_test.go | 11 +++++++---- .../eventhubnamespace_controller_test.go | 5 +++-- controllers/suite_test.go | 7 ++++--- .../eventhubs/consumergroup_test.go | 18 +++++++++--------- pkg/resourcemanager/eventhubs/hub_test.go | 16 ++++++++-------- .../eventhubs/namespace_test.go | 11 ++++++----- pkg/resourcemanager/eventhubs/suite_test.go | 3 ++- 8 files changed, 49 insertions(+), 38 deletions(-) diff --git a/controllers/consumergroup_controller_test.go b/controllers/consumergroup_controller_test.go index 0bd10170073..21e82883515 100644 --- a/controllers/consumergroup_controller_test.go +++ b/controllers/consumergroup_controller_test.go @@ -34,9 +34,16 @@ import ( var _ = Describe("ConsumerGroup Controller", func() { const timeout = time.Second * 240 + var rgName string + var ehnName string + var ehName string BeforeEach(func() { // Add any setup steps that needs to be executed before each test + rgName = resourceGroupName + ehnName = eventhubNamespaceName + ehName = eventhubName + }) AfterEach(func() { @@ -50,9 +57,6 @@ var _ = Describe("ConsumerGroup Controller", func() { Context("Create and Delete", func() { It("should create and delete consumer groups", func() { - resourceGroupName = "t-rg-dev-controller" - eventhubNamespaceName = "t-ns-dev-eh-ns" - eventhubName = "t-eh-dev-sample" consumerGroupName := "t-cg-" + helpers.RandomString(10) var err error @@ -64,9 +68,9 @@ var _ = Describe("ConsumerGroup Controller", func() { Namespace: "default", }, Spec: azurev1.ConsumerGroupSpec{ - NamespaceName: eventhubNamespaceName, - ResourceGroupName: resourceGroupName, - EventhubName: eventhubName, + NamespaceName: ehnName, + ResourceGroupName: rgName, + EventhubName: ehName, }, } diff --git a/controllers/eventhub_controller_test.go b/controllers/eventhub_controller_test.go index 0a07f57290e..c63eebe0396 100644 --- a/controllers/eventhub_controller_test.go +++ b/controllers/eventhub_controller_test.go @@ -36,8 +36,13 @@ var _ = Describe("EventHub Controller", func() { const timeout = time.Second * 240 + var rgName string + var ehnName string + BeforeEach(func() { // Add any setup steps that needs to be executed before each test + rgName = resourceGroupName + ehnName = eventhubNamespaceName }) AfterEach(func() { @@ -83,8 +88,6 @@ var _ = Describe("EventHub Controller", func() { It("should create and delete eventhubs", func() { - resourceGroupName = "t-rg-dev-controller" - eventhubNamespaceName = "t-ns-dev-eh-ns" eventhubName := "t-eh-" + helpers.RandomString(10) var err error @@ -97,8 +100,8 @@ var _ = Describe("EventHub Controller", func() { }, Spec: azurev1.EventhubSpec{ Location: "westus", - Namespace: eventhubNamespaceName, - ResourceGroup: resourceGroupName, + Namespace: ehnName, + ResourceGroup: rgName, Properties: azurev1.EventhubProperties{ MessageRetentionInDays: 7, PartitionCount: 1, diff --git a/controllers/eventhubnamespace_controller_test.go b/controllers/eventhubnamespace_controller_test.go index 4735a5bbb96..15f6c0d061b 100644 --- a/controllers/eventhubnamespace_controller_test.go +++ b/controllers/eventhubnamespace_controller_test.go @@ -34,9 +34,11 @@ import ( var _ = Describe("EventHubNamespace Controller", func() { const timeout = time.Second * 240 + var rgName string BeforeEach(func() { // Add any setup steps that needs to be executed before each test + rgName = resourceGroupName }) @@ -110,7 +112,6 @@ var _ = Describe("EventHubNamespace Controller", func() { It("should create and delete namespace in k8s", func() { - resourceGroupName := "t-rg-dev-controller" eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) var err error @@ -123,7 +124,7 @@ var _ = Describe("EventHubNamespace Controller", func() { }, Spec: azurev1.EventhubNamespaceSpec{ Location: "westus", - ResourceGroup: resourceGroupName, + ResourceGroup: rgName, }, } diff --git a/controllers/suite_test.go b/controllers/suite_test.go index e8fba4c4c45..30be158aed9 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -17,6 +17,7 @@ package controllers import ( "context" + helpers "github.com/Azure/azure-service-operator/pkg/helpers" "os" "path/filepath" "testing" @@ -55,11 +56,11 @@ var namespaceLocation string func TestAPIs(t *testing.T) { t.Parallel() RegisterFailHandler(Fail) - resourceGroupName = "t-rg-dev-controller" + resourceGroupName = "t-rg-dev-controller-" + helpers.RandomString(10) resourcegroupLocation = "westus" - eventhubNamespaceName = "t-ns-dev-eh-ns" - eventhubName = "t-eh-dev-sample" + eventhubNamespaceName = "t-ns-dev-eh-ns-" + helpers.RandomString(10) + eventhubName = "t-eh-dev-sample-" + helpers.RandomString(10) namespaceLocation = "westus" RunSpecsWithDefaultAndCustomReporters(t, "Controller Suite", diff --git a/pkg/resourcemanager/eventhubs/consumergroup_test.go b/pkg/resourcemanager/eventhubs/consumergroup_test.go index b20b5da106b..b070ca1208b 100644 --- a/pkg/resourcemanager/eventhubs/consumergroup_test.go +++ b/pkg/resourcemanager/eventhubs/consumergroup_test.go @@ -28,7 +28,7 @@ import ( var _ = Describe("ConsumerGroup", func() { const timeout = time.Second * 240 - var resourceGroupName string + var rgName string var eventhubNamespaceName string var eventhubName string var namespaceLocation string @@ -37,16 +37,16 @@ var _ = Describe("ConsumerGroup", func() { BeforeEach(func() { // Add any setup steps that needs to be executed before each test - resourceGroupName = "t-rg-dev-rm-eh" + rgName = resourceGroupName eventhubNamespaceName = "t-ns-dev-eh-" + helpers.RandomString(10) namespaceLocation = "westus" - eventhubName = "t-eh-dev-ehs" + eventhubName = "t-eh-dev-ehs-" + helpers.RandomString(10) messageRetentionInDays = int32(7) partitionCount = int32(1) - _, _ = CreateNamespaceAndWait(context.Background(), resourceGroupName, eventhubNamespaceName, namespaceLocation) + _, _ = CreateNamespaceAndWait(context.Background(), rgName, eventhubNamespaceName, namespaceLocation) - _, _ = CreateHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, messageRetentionInDays, partitionCount) + _, _ = CreateHub(context.Background(), rgName, eventhubNamespaceName, eventhubName, messageRetentionInDays, partitionCount) }) @@ -66,20 +66,20 @@ var _ = Describe("ConsumerGroup", func() { var err error - _, err = CreateConsumerGroup(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, consumerGroupName) + _, err = CreateConsumerGroup(context.Background(), rgName, eventhubNamespaceName, eventhubName, consumerGroupName) Expect(err).NotTo(HaveOccurred()) Eventually(func() bool { - result, _ := GetConsumerGroup(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, consumerGroupName) + result, _ := GetConsumerGroup(context.Background(), rgName, eventhubNamespaceName, eventhubName, consumerGroupName) return result.Response.StatusCode == 200 }, timeout, ).Should(BeTrue()) - _, err = DeleteConsumerGroup(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, consumerGroupName) + _, err = DeleteConsumerGroup(context.Background(), rgName, eventhubNamespaceName, eventhubName, consumerGroupName) Expect(err).NotTo(HaveOccurred()) Eventually(func() bool { - result, _ := GetConsumerGroup(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, consumerGroupName) + result, _ := GetConsumerGroup(context.Background(), rgName, eventhubNamespaceName, eventhubName, consumerGroupName) return result.Response.StatusCode == 404 }, timeout, ).Should(BeTrue()) diff --git a/pkg/resourcemanager/eventhubs/hub_test.go b/pkg/resourcemanager/eventhubs/hub_test.go index 55be9925906..8a2d883b572 100644 --- a/pkg/resourcemanager/eventhubs/hub_test.go +++ b/pkg/resourcemanager/eventhubs/hub_test.go @@ -29,13 +29,13 @@ import ( var _ = Describe("Eventhub", func() { const timeout = time.Second * 240 - var resourceGroupName string + var rgName string var eventhubNamespaceName string var namespaceLocation string BeforeEach(func() { // Add any setup steps that needs to be executed before each test - resourceGroupName = "t-rg-dev-rm-eh" + rgName = resourceGroupName eventhubNamespaceName = "t-ns-dev-eh-" + helpers.RandomString(10) namespaceLocation = "westus" @@ -61,11 +61,11 @@ var _ = Describe("Eventhub", func() { var err error - _, err = CreateHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, messageRetentionInDays, partitionCount) + _, err = CreateHub(context.Background(), rgName, eventhubNamespaceName, eventhubName, messageRetentionInDays, partitionCount) Expect(err).NotTo(HaveOccurred()) Eventually(func() bool { - result, _ := GetHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName) + result, _ := GetHub(context.Background(), rgName, eventhubNamespaceName, eventhubName) return result.Response.StatusCode == 200 }, timeout, ).Should(BeTrue()) @@ -78,20 +78,20 @@ var _ = Describe("Eventhub", func() { }, } - _, err = CreateOrUpdateAuthorizationRule(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, authorizationRuleName, parameters) + _, err = CreateOrUpdateAuthorizationRule(context.Background(), rgName, eventhubNamespaceName, eventhubName, authorizationRuleName, parameters) Expect(err).NotTo(HaveOccurred()) Eventually(func() bool { - result, _ := ListKeys(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, authorizationRuleName) + result, _ := ListKeys(context.Background(), rgName, eventhubNamespaceName, eventhubName, authorizationRuleName) return result.Response.StatusCode == 200 }, timeout, ).Should(BeTrue()) - _, err = DeleteHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName) + _, err = DeleteHub(context.Background(), rgName, eventhubNamespaceName, eventhubName) Expect(err).NotTo(HaveOccurred()) Eventually(func() bool { - result, _ := GetHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName) + result, _ := GetHub(context.Background(), rgName, eventhubNamespaceName, eventhubName) return result.Response.StatusCode == 404 }, timeout, ).Should(BeTrue()) diff --git a/pkg/resourcemanager/eventhubs/namespace_test.go b/pkg/resourcemanager/eventhubs/namespace_test.go index 35449a3d4ed..471d3a93354 100644 --- a/pkg/resourcemanager/eventhubs/namespace_test.go +++ b/pkg/resourcemanager/eventhubs/namespace_test.go @@ -29,8 +29,10 @@ var _ = Describe("Namespace", func() { const timeout = time.Second * 240 + var rgName string BeforeEach(func() { // Add any setup steps that needs to be executed before each test + rgName = resourceGroupName }) AfterEach(func() { @@ -45,26 +47,25 @@ var _ = Describe("Namespace", func() { Context("Create and Delete", func() { It("should create and delete namespace in azure", func() { - resourceGroupName := "t-rg-dev-rm-eh" eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) namespaceLocation := "westus" var err error - _, err = CreateNamespaceAndWait(context.Background(), resourceGroupName, eventhubNamespaceName, namespaceLocation) + _, err = CreateNamespaceAndWait(context.Background(), rgName, eventhubNamespaceName, namespaceLocation) Expect(err).NotTo(HaveOccurred()) Eventually(func() bool { - result, _ := GetNamespace(context.Background(), resourceGroupName, eventhubNamespaceName) + result, _ := GetNamespace(context.Background(), rgName, eventhubNamespaceName) return result.Response.StatusCode == 200 }, timeout, ).Should(BeTrue()) - _, err = DeleteNamespace(context.Background(), resourceGroupName, eventhubNamespaceName) + _, err = DeleteNamespace(context.Background(), rgName, eventhubNamespaceName) Expect(err).NotTo(HaveOccurred()) Eventually(func() bool { - result, _ := GetNamespace(context.Background(), resourceGroupName, eventhubNamespaceName) + result, _ := GetNamespace(context.Background(), rgName, eventhubNamespaceName) return result.Response.StatusCode == 404 }, timeout, ).Should(BeTrue()) diff --git a/pkg/resourcemanager/eventhubs/suite_test.go b/pkg/resourcemanager/eventhubs/suite_test.go index c30e6878f1a..3ddb4f3f8e8 100644 --- a/pkg/resourcemanager/eventhubs/suite_test.go +++ b/pkg/resourcemanager/eventhubs/suite_test.go @@ -26,6 +26,7 @@ import ( "context" + helpers "github.com/Azure/azure-service-operator/pkg/helpers" "k8s.io/client-go/rest" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -45,7 +46,7 @@ func TestAPIs(t *testing.T) { t.Skip("skipping Resource Manager Eventhubs Suite") } RegisterFailHandler(Fail) - resourceGroupName = "t-rg-dev-rm-eh" + resourceGroupName = "t-rg-dev-rm-eh-" + helpers.RandomString(10) resourcegroupLocation = "westus" RunSpecs(t, "Eventhubs Suite") From c22f7c0329ad028c717865156b87373dd43a6d06 Mon Sep 17 00:00:00 2001 From: JasonTheDeveloper Date: Wed, 28 Aug 2019 15:00:02 +1000 Subject: [PATCH 18/19] Ability to Set SecretName When Creating Event Hub (#151) * Updated eventhub_types - Added `secretName`. * Added `secretName` to sample manifest. * Set secret name to `secretName` if set, otherwise use eventhub name. * Updated Makefile to update Azure Operator. Also added the ability to rebuild image without cache. * Updated README on how to update the Azure Operator. * Updated CRD with SecretName description. * Added tests to ensure `SecretName` was being used if present. * Fix test. --- Makefile | 9 +- README.md | 5 +- api/v1/eventhub_types.go | 2 + .../bases/azure.microsoft.com_eventhubs.yaml | 4 + config/rbac/role.yaml | 80 ++++++++--------- config/samples/azure_v1_eventhub.yaml | 1 + controllers/eventhub_controller.go | 14 ++- controllers/eventhub_controller_test.go | 89 +++++++++++++++++++ go.mod | 2 +- 9 files changed, 159 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index eb5dbb1af75..610cbfccf89 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,13 @@ deploy: manifests kubectl apply -f config/crd/bases kustomize build config/default | kubectl apply -f - +update: + IMG="docker.io/controllertest:1" make ARGS="${ARGS}" docker-build + kind load docker-image docker.io/controllertest:1 --loglevel "trace" + make install + make deploy + sed -i'' -e 's@image: .*@image: '"IMAGE_URL"'@' ./config/default/manager_image_patch.yaml + delete: kubectl delete -f config/crd/bases kustomize build config/default | kubectl delete -f - @@ -55,7 +62,7 @@ generate: controller-gen # Build the docker image docker-build: - docker build . -t ${IMG} + docker build . -t ${IMG} ${ARGS} @echo "updating kustomize image patch file for manager resource" sed -i'' -e 's@image: .*@image: '"${IMG}"'@' ./config/default/manager_image_patch.yaml diff --git a/README.md b/README.md index 316878e9c00..eb64e4897d4 100644 --- a/README.md +++ b/README.md @@ -162,8 +162,9 @@ To Extend the operator `github.com/Azure/azure-service-operator`: 5. Generate code `make generate` 6. Update operator `controller\eventhub_controller.go` 7. Update tests and run `make test` -8. Build `make build` -9. Deploy `make deploy` +8. Deploy `make deploy` + +If you make changes to the operator and want to update the deployment without recreating the cluster (when testing locally), you can use the `make update` to update your Azure Operator pod. If you need to rebuild the docker image without cache, use `make ARGS="--no-cache" update`. ## Contributing diff --git a/api/v1/eventhub_types.go b/api/v1/eventhub_types.go index 9b6f071a43c..89d0f5f6dda 100644 --- a/api/v1/eventhub_types.go +++ b/api/v1/eventhub_types.go @@ -41,6 +41,8 @@ type EventhubSpec struct { Properties EventhubProperties `json:"properties,omitempty"` ResourceGroup string `json:"resourcegroup,omitempty"` AuthorizationRule EventhubAuthorizationRule `json:"authorizationrule,omitempty"` + // SecretName - Used to specify the name of the secret. Defaults to Event Hub name if omitted. + SecretName string `json:"secretname,omitempty"` } // EventhubStatus defines the observed state of Eventhub diff --git a/config/crd/bases/azure.microsoft.com_eventhubs.yaml b/config/crd/bases/azure.microsoft.com_eventhubs.yaml index 76c5fd857ec..249dc37d014 100644 --- a/config/crd/bases/azure.microsoft.com_eventhubs.yaml +++ b/config/crd/bases/azure.microsoft.com_eventhubs.yaml @@ -438,6 +438,10 @@ spec: type: object resourcegroup: type: string + secretname: + description: SecretName - Used to specify the name of the secret. Defaults + to Event Hub name if omitted. + type: string required: - location type: object diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index f4216c8c96d..54f2aeb0bff 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -7,9 +7,9 @@ metadata: name: manager-role rules: - apiGroups: - - azure.microsoft.com + - "" resources: - - eventhubs + - secrets verbs: - create - delete @@ -19,17 +19,16 @@ rules: - update - watch - apiGroups: - - azure.microsoft.com + - "" resources: - - eventhubs/status + - events verbs: - - get - - patch - - update + - create + - watch - apiGroups: - azure.microsoft.com resources: - - keyvaults + - consumergroups verbs: - create - delete @@ -41,15 +40,15 @@ rules: - apiGroups: - azure.microsoft.com resources: - - keyvaults/status + - consumergroups/status verbs: - get - patch - update - apiGroups: - - "" + - azure.microsoft.com resources: - - secrets + - keyvaults verbs: - create - delete @@ -61,27 +60,22 @@ rules: - apiGroups: - azure.microsoft.com resources: - - consumergroups + - resourcegroups/status verbs: - - create - - delete - get - - list - patch - update - - watch - apiGroups: - azure.microsoft.com resources: - - consumergroups/status + - events verbs: - - get + - create - patch - - update - apiGroups: - - azure.microsoft.com + - apps resources: - - eventhubnamespaces/status + - deployments/status verbs: - get - patch @@ -89,22 +83,27 @@ rules: - apiGroups: - azure.microsoft.com resources: - - resourcegroups/status + - eventhubs verbs: + - create + - delete - get + - list - patch - update + - watch - apiGroups: - - "" + - azure.microsoft.com resources: - - events + - eventhubs/status verbs: - - create - - watch + - get + - patch + - update - apiGroups: - - apps + - azure.microsoft.com resources: - - deployments + - eventhubnamespaces verbs: - create - delete @@ -116,39 +115,40 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubnamespaces + - keyvaults/status verbs: - - create - - delete - get - - list - patch - update - - watch - apiGroups: - azure.microsoft.com resources: - - resourcegroups + - eventhubnamespaces/status verbs: - - create - - delete - get - - list - patch - update - - watch - apiGroups: - azure.microsoft.com resources: - - events + - resourcegroups verbs: - create + - delete + - get + - list - patch + - update + - watch - apiGroups: - apps resources: - - deployments/status + - deployments verbs: + - create + - delete - get + - list - patch - update + - watch diff --git a/config/samples/azure_v1_eventhub.yaml b/config/samples/azure_v1_eventhub.yaml index fa0fd7989a9..89f98afc3b1 100644 --- a/config/samples/azure_v1_eventhub.yaml +++ b/config/samples/azure_v1_eventhub.yaml @@ -15,3 +15,4 @@ spec: - "Listen" - "Manage" - "Send" + secretname: "secret-stream-eventhub-sample" diff --git a/controllers/eventhub_controller.go b/controllers/eventhub_controller.go index 57ff6558797..6712fa8b2ee 100644 --- a/controllers/eventhub_controller.go +++ b/controllers/eventhub_controller.go @@ -124,6 +124,11 @@ func (r *EventhubReconciler) reconcileExternal(instance *azurev1.Eventhub) error resourcegroup := instance.Spec.ResourceGroup partitionCount := instance.Spec.Properties.PartitionCount messageRetentionInDays := instance.Spec.Properties.MessageRetentionInDays + secretName := instance.Spec.SecretName + + if secretName == "" { + secretName = eventhubName + } // write information back to instance instance.Status.Provisioning = true @@ -173,7 +178,7 @@ func (r *EventhubReconciler) reconcileExternal(instance *azurev1.Eventhub) error return err } - err = r.listAccessKeysAndCreateSecrets(resourcegroup, eventhubNamespace, eventhubName, instance.Spec.AuthorizationRule.Name, instance) + err = r.listAccessKeysAndCreateSecrets(resourcegroup, eventhubNamespace, eventhubName, secretName, instance.Spec.AuthorizationRule.Name, instance) if err != nil { r.Recorder.Event(instance, "Warning", "Failed", "Unable to listAccessKeysAndCreateSecrets") return err @@ -231,7 +236,7 @@ func (r *EventhubReconciler) createOrUpdateAccessPolicyEventHub(resourcegroup st return nil } -func (r *EventhubReconciler) listAccessKeysAndCreateSecrets(resourcegroup string, eventhubNamespace string, eventhubName string, authorizationRuleName string, instance *azurev1.Eventhub) error { +func (r *EventhubReconciler) listAccessKeysAndCreateSecrets(resourcegroup string, eventhubNamespace string, eventhubName string, secretName string, authorizationRuleName string, instance *azurev1.Eventhub) error { var err error var result model.AccessKeys @@ -251,6 +256,7 @@ func (r *EventhubReconciler) listAccessKeysAndCreateSecrets(resourcegroup string *result.PrimaryKey, *result.SecondaryKey, eventhubNamespace, + secretName, authorizationRuleName, instance, ) @@ -271,6 +277,7 @@ func (r *EventhubReconciler) createEventhubSecrets( primaryKey string, secondaryKey string, eventhubNamespace string, + secretName string, sharedAccessKey string, instance *azurev1.Eventhub) error { @@ -280,7 +287,7 @@ func (r *EventhubReconciler) createEventhubSecrets( APIVersion: "apps/v1beta1", }, ObjectMeta: metav1.ObjectMeta{ - Name: eventhubName, + Name: secretName, Namespace: namespace, }, Data: map[string][]byte{ @@ -290,6 +297,7 @@ func (r *EventhubReconciler) createEventhubSecrets( "secondaryKey": []byte(secondaryKey), "sharedaccesskey": []byte(sharedAccessKey), "eventhubnamespace": []byte(eventhubNamespace), + "eventhubName": []byte(eventhubName), }, Type: "Opaque", } diff --git a/controllers/eventhub_controller_test.go b/controllers/eventhub_controller_test.go index c63eebe0396..0411204bf6e 100644 --- a/controllers/eventhub_controller_test.go +++ b/controllers/eventhub_controller_test.go @@ -148,6 +148,7 @@ var _ = Describe("EventHub Controller", func() { "secondaryKey": []byte("secondaryKeyValue"), "sharedaccesskey": []byte("sharedAccessKeyValue"), "eventhubnamespace": []byte(eventhubInstance.Namespace), + "eventhubName": []byte(eventhubName), }, Type: "Opaque", } @@ -170,5 +171,93 @@ var _ = Describe("EventHub Controller", func() { ).Should(BeTrue()) }) + + It("should create and delete eventhubs with custom secret name", func() { + + eventhubName := "t-eh-" + helpers.RandomString(10) + secretName := "secret-" + eventhubName + + var err error + + // Create the EventHub object and expect the Reconcile to be created + eventhubInstance := &azurev1.Eventhub{ + ObjectMeta: metav1.ObjectMeta{ + Name: eventhubName, + Namespace: "default", + }, + Spec: azurev1.EventhubSpec{ + Location: "westus", + Namespace: ehnName, + ResourceGroup: rgName, + Properties: azurev1.EventhubProperties{ + MessageRetentionInDays: 7, + PartitionCount: 1, + }, + AuthorizationRule: azurev1.EventhubAuthorizationRule{ + Name: "RootManageSharedAccessKey", + Rights: []string{"Listen"}, + }, + SecretName: secretName, + }, + } + + err = k8sClient.Create(context.Background(), eventhubInstance) + Expect(apierrors.IsInvalid(err)).To(Equal(false)) + Expect(err).NotTo(HaveOccurred()) + + eventhubNamespacedName := types.NamespacedName{Name: eventhubName, Namespace: "default"} + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubInstance) + return eventhubInstance.HasFinalizer(eventhubFinalizerName) + }, timeout, + ).Should(BeTrue()) + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubInstance) + return eventhubInstance.IsSubmitted() + }, timeout, + ).Should(BeTrue()) + + //create secret in k8s + csecret := &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "apps/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: "default", + }, + Data: map[string][]byte{ + "primaryconnectionstring": []byte("primaryConnectionValue"), + "secondaryconnectionstring": []byte("secondaryConnectionValue"), + "primaryKey": []byte("primaryKeyValue"), + "secondaryKey": []byte("secondaryKeyValue"), + "sharedaccesskey": []byte("sharedAccessKeyValue"), + "eventhubnamespace": []byte(eventhubInstance.Namespace), + "eventhubName": []byte(eventhubName), + }, + Type: "Opaque", + } + + err = k8sClient.Create(context.Background(), csecret) + Expect(err).NotTo(HaveOccurred()) + + //get secret from k8s + secret := &v1.Secret{} + err = k8sClient.Get(context.Background(), types.NamespacedName{Name: secretName, Namespace: eventhubInstance.Namespace}, secret) + Expect(err).NotTo(HaveOccurred()) + Expect(secret.Data).To(Equal(csecret.Data)) + Expect(secret.ObjectMeta).To(Equal(csecret.ObjectMeta)) + + k8sClient.Delete(context.Background(), eventhubInstance) + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), eventhubNamespacedName, eventhubInstance) + return eventhubInstance.IsBeingDeleted() + }, timeout, + ).Should(BeTrue()) + + }) }) }) diff --git a/go.mod b/go.mod index 24079a36543..ad261c7cb3b 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/marstr/randname v0.0.0-20181206212954-d5b0f288ab8c github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega v1.5.0 - github.com/satori/go.uuid v1.2.0 // indirect + github.com/satori/go.uuid v1.2.0 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 // indirect golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/sys v0.0.0-20190621203818-d432491b9138 // indirect From 95e804de1d755d5dd07d2aea5bd7da123b94c4bd Mon Sep 17 00:00:00 2001 From: jananivMS Date: Tue, 1 Oct 2019 12:34:27 -0600 Subject: [PATCH 19/19] first cut of changes --- PROJECT | 10 ++++----- api/{v1 => v1alpha1}/consumergroup_types.go | 2 +- .../consumergroup_types_test.go | 2 +- api/{v1 => v1alpha1}/eventhub_types.go | 2 +- api/{v1 => v1alpha1}/eventhub_types_test.go | 2 +- api/{v1 => v1alpha1}/eventhub_webhook.go | 2 +- .../eventhubnamespace_types.go | 2 +- .../eventhubnamespace_types_test.go | 2 +- .../eventhubnamespace_webhook.go | 2 +- api/{v1 => v1alpha1}/groupversion_info.go | 6 ++--- api/{v1 => v1alpha1}/keyvault_types.go | 2 +- api/{v1 => v1alpha1}/resourcegroup_types.go | 2 +- .../resourcegroup_types_test.go | 2 +- api/{v1 => v1alpha1}/suite_test.go | 2 +- api/{v1 => v1alpha1}/zz_generated.deepcopy.go | 2 +- ...yaml => azure_v1alpha1_consumergroup.yaml} | 0 ...thub.yaml => azure_v1alpha1_eventhub.yaml} | 0 ... => azure_v1alpha1_eventhubnamespace.yaml} | 0 ...ault.yaml => azure_v1alpha1_keyvault.yaml} | 0 ...yaml => azure_v1alpha1_resourcegroup.yaml} | 0 controllers/consumergroup_controller.go | 14 ++++++------ .../consumergroup_controller_finalizer.go | 2 +- controllers/consumergroup_controller_test.go | 6 ++--- controllers/eventhub_controller.go | 16 +++++++------- controllers/eventhub_controller_finalizer.go | 8 +++---- controllers/eventhub_controller_test.go | 22 +++++++++---------- controllers/eventhubnamespace_controller.go | 10 ++++----- .../eventhubnamespace_controller_finalizer.go | 8 +++---- .../eventhubnamespace_controller_test.go | 14 ++++++------ controllers/suite_test.go | 13 ++++++----- main.go | 6 ++--- 31 files changed, 81 insertions(+), 80 deletions(-) rename api/{v1 => v1alpha1}/consumergroup_types.go (99%) rename api/{v1 => v1alpha1}/consumergroup_types_test.go (99%) rename api/{v1 => v1alpha1}/eventhub_types.go (99%) rename api/{v1 => v1alpha1}/eventhub_types_test.go (99%) rename api/{v1 => v1alpha1}/eventhub_webhook.go (99%) rename api/{v1 => v1alpha1}/eventhubnamespace_types.go (99%) rename api/{v1 => v1alpha1}/eventhubnamespace_types_test.go (99%) rename api/{v1 => v1alpha1}/eventhubnamespace_webhook.go (99%) rename api/{v1 => v1alpha1}/groupversion_info.go (89%) rename api/{v1 => v1alpha1}/keyvault_types.go (99%) rename api/{v1 => v1alpha1}/resourcegroup_types.go (99%) rename api/{v1 => v1alpha1}/resourcegroup_types_test.go (99%) rename api/{v1 => v1alpha1}/suite_test.go (99%) rename api/{v1 => v1alpha1}/zz_generated.deepcopy.go (99%) rename config/samples/{azure_v1_consumergroup.yaml => azure_v1alpha1_consumergroup.yaml} (100%) rename config/samples/{azure_v1_eventhub.yaml => azure_v1alpha1_eventhub.yaml} (100%) rename config/samples/{azure_v1_eventhubnamespace.yaml => azure_v1alpha1_eventhubnamespace.yaml} (100%) rename config/samples/{azure_v1_keyvault.yaml => azure_v1alpha1_keyvault.yaml} (100%) rename config/samples/{azure_v1_resourcegroup.yaml => azure_v1alpha1_resourcegroup.yaml} (100%) diff --git a/PROJECT b/PROJECT index 40562d2d55c..c3e5ff1a9fb 100644 --- a/PROJECT +++ b/PROJECT @@ -3,17 +3,17 @@ domain: microsoft.com repo: github.com/Azure/azure-service-operator resources: - group: azure - version: v1 + version: v1alpha1 kind: Eventhub - group: azure - version: v1 + version: v1alpha1 kind: ResourceGroup - group: azure - version: v1 + version: v1alpha1 kind: EventhubNamespace - group: azure - version: v1 + version: v1alpha1 kind: KeyVault - group: azure - version: v1 + version: v1alpha1 kind: ConsumerGroup diff --git a/api/v1/consumergroup_types.go b/api/v1alpha1/consumergroup_types.go similarity index 99% rename from api/v1/consumergroup_types.go rename to api/v1alpha1/consumergroup_types.go index d7dd1c5de99..9d93ead5478 100644 --- a/api/v1/consumergroup_types.go +++ b/api/v1alpha1/consumergroup_types.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( helpers "github.com/Azure/azure-service-operator/pkg/helpers" diff --git a/api/v1/consumergroup_types_test.go b/api/v1alpha1/consumergroup_types_test.go similarity index 99% rename from api/v1/consumergroup_types_test.go rename to api/v1alpha1/consumergroup_types_test.go index 6d326e40322..9940afd85fa 100644 --- a/api/v1/consumergroup_types_test.go +++ b/api/v1alpha1/consumergroup_types_test.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( . "github.com/onsi/ginkgo" diff --git a/api/v1/eventhub_types.go b/api/v1alpha1/eventhub_types.go similarity index 99% rename from api/v1/eventhub_types.go rename to api/v1alpha1/eventhub_types.go index 89d0f5f6dda..c9d80c2318e 100644 --- a/api/v1/eventhub_types.go +++ b/api/v1alpha1/eventhub_types.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( helpers "github.com/Azure/azure-service-operator/pkg/helpers" diff --git a/api/v1/eventhub_types_test.go b/api/v1alpha1/eventhub_types_test.go similarity index 99% rename from api/v1/eventhub_types_test.go rename to api/v1alpha1/eventhub_types_test.go index c448093dc21..1cedfde4667 100644 --- a/api/v1/eventhub_types_test.go +++ b/api/v1alpha1/eventhub_types_test.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( . "github.com/onsi/ginkgo" diff --git a/api/v1/eventhub_webhook.go b/api/v1alpha1/eventhub_webhook.go similarity index 99% rename from api/v1/eventhub_webhook.go rename to api/v1alpha1/eventhub_webhook.go index 363ea839e10..09345b76500 100644 --- a/api/v1/eventhub_webhook.go +++ b/api/v1alpha1/eventhub_webhook.go @@ -1,4 +1,4 @@ -package v1 +package v1alpha1 import ( eventhubsmanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/eventhubs" diff --git a/api/v1/eventhubnamespace_types.go b/api/v1alpha1/eventhubnamespace_types.go similarity index 99% rename from api/v1/eventhubnamespace_types.go rename to api/v1alpha1/eventhubnamespace_types.go index e2687c8243d..86f72cc64cf 100644 --- a/api/v1/eventhubnamespace_types.go +++ b/api/v1alpha1/eventhubnamespace_types.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( helpers "github.com/Azure/azure-service-operator/pkg/helpers" diff --git a/api/v1/eventhubnamespace_types_test.go b/api/v1alpha1/eventhubnamespace_types_test.go similarity index 99% rename from api/v1/eventhubnamespace_types_test.go rename to api/v1alpha1/eventhubnamespace_types_test.go index 73323527c34..ee2188891fc 100644 --- a/api/v1/eventhubnamespace_types_test.go +++ b/api/v1alpha1/eventhubnamespace_types_test.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( . "github.com/onsi/ginkgo" diff --git a/api/v1/eventhubnamespace_webhook.go b/api/v1alpha1/eventhubnamespace_webhook.go similarity index 99% rename from api/v1/eventhubnamespace_webhook.go rename to api/v1alpha1/eventhubnamespace_webhook.go index 2fce158ca06..e9c99bc21a9 100644 --- a/api/v1/eventhubnamespace_webhook.go +++ b/api/v1alpha1/eventhubnamespace_webhook.go @@ -1,4 +1,4 @@ -package v1 +package v1alpha1 import ( resoucegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" diff --git a/api/v1/groupversion_info.go b/api/v1alpha1/groupversion_info.go similarity index 89% rename from api/v1/groupversion_info.go rename to api/v1alpha1/groupversion_info.go index 39a37cfc9c4..29c6dc9997f 100644 --- a/api/v1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -13,10 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1 contains API Schema definitions for the azure v1 API group +// Package v1alpha1 contains API Schema definitions for the azure v1alpha1 API group // +kubebuilder:object:generate=true // +groupName=azure.microsoft.com -package v1 +package v1alpha1 import ( "k8s.io/apimachinery/pkg/runtime/schema" @@ -25,7 +25,7 @@ import ( var ( // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "azure.microsoft.com", Version: "v1"} + GroupVersion = schema.GroupVersion{Group: "azure.microsoft.com", Version: "v1alpha1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/api/v1/keyvault_types.go b/api/v1alpha1/keyvault_types.go similarity index 99% rename from api/v1/keyvault_types.go rename to api/v1alpha1/keyvault_types.go index c671ab60ee1..74956394274 100644 --- a/api/v1/keyvault_types.go +++ b/api/v1alpha1/keyvault_types.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/api/v1/resourcegroup_types.go b/api/v1alpha1/resourcegroup_types.go similarity index 99% rename from api/v1/resourcegroup_types.go rename to api/v1alpha1/resourcegroup_types.go index 02b93849435..caf0b3c94e4 100644 --- a/api/v1/resourcegroup_types.go +++ b/api/v1alpha1/resourcegroup_types.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( helpers "github.com/Azure/azure-service-operator/pkg/helpers" diff --git a/api/v1/resourcegroup_types_test.go b/api/v1alpha1/resourcegroup_types_test.go similarity index 99% rename from api/v1/resourcegroup_types_test.go rename to api/v1alpha1/resourcegroup_types_test.go index 235b56823bb..700292e44e7 100644 --- a/api/v1/resourcegroup_types_test.go +++ b/api/v1alpha1/resourcegroup_types_test.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( . "github.com/onsi/ginkgo" diff --git a/api/v1/suite_test.go b/api/v1alpha1/suite_test.go similarity index 99% rename from api/v1/suite_test.go rename to api/v1alpha1/suite_test.go index e4d5357f792..a9367db992c 100644 --- a/api/v1/suite_test.go +++ b/api/v1alpha1/suite_test.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1 +package v1alpha1 import ( "path/filepath" diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go similarity index 99% rename from api/v1/zz_generated.deepcopy.go rename to api/v1alpha1/zz_generated.deepcopy.go index 760242cc0f6..6a0282ea09d 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -17,7 +17,7 @@ limitations under the License. // autogenerated by controller-gen object, do not modify manually -package v1 +package v1alpha1 import ( "k8s.io/apimachinery/pkg/runtime" diff --git a/config/samples/azure_v1_consumergroup.yaml b/config/samples/azure_v1alpha1_consumergroup.yaml similarity index 100% rename from config/samples/azure_v1_consumergroup.yaml rename to config/samples/azure_v1alpha1_consumergroup.yaml diff --git a/config/samples/azure_v1_eventhub.yaml b/config/samples/azure_v1alpha1_eventhub.yaml similarity index 100% rename from config/samples/azure_v1_eventhub.yaml rename to config/samples/azure_v1alpha1_eventhub.yaml diff --git a/config/samples/azure_v1_eventhubnamespace.yaml b/config/samples/azure_v1alpha1_eventhubnamespace.yaml similarity index 100% rename from config/samples/azure_v1_eventhubnamespace.yaml rename to config/samples/azure_v1alpha1_eventhubnamespace.yaml diff --git a/config/samples/azure_v1_keyvault.yaml b/config/samples/azure_v1alpha1_keyvault.yaml similarity index 100% rename from config/samples/azure_v1_keyvault.yaml rename to config/samples/azure_v1alpha1_keyvault.yaml diff --git a/config/samples/azure_v1_resourcegroup.yaml b/config/samples/azure_v1alpha1_resourcegroup.yaml similarity index 100% rename from config/samples/azure_v1_resourcegroup.yaml rename to config/samples/azure_v1alpha1_resourcegroup.yaml diff --git a/controllers/consumergroup_controller.go b/controllers/consumergroup_controller.go index f6988cee06e..3d7e895833e 100644 --- a/controllers/consumergroup_controller.go +++ b/controllers/consumergroup_controller.go @@ -30,7 +30,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" eventhubsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/eventhubs" ) @@ -49,7 +49,7 @@ func (r *ConsumerGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro ctx := context.Background() log := r.Log.WithValues("consumergroup", req.NamespacedName) - var instance azurev1.ConsumerGroup + var instance azurev1alpha1.ConsumerGroup if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { log.Error(err, "unable to fetch consumergroup") // we'll ignore not-found errors, since they can't be fixed by an immediate @@ -95,11 +95,11 @@ func (r *ConsumerGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro func (r *ConsumerGroupReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&azurev1.ConsumerGroup{}). + For(&azurev1alpha1.ConsumerGroup{}). Complete(r) } -func (r *ConsumerGroupReconciler) createConsumerGroup(instance *azurev1.ConsumerGroup) error { +func (r *ConsumerGroupReconciler) createConsumerGroup(instance *azurev1alpha1.ConsumerGroup) error { ctx := context.Background() var err error @@ -112,7 +112,7 @@ func (r *ConsumerGroupReconciler) createConsumerGroup(instance *azurev1.Consumer instance.Status.Provisioning = true //get owner instance - var ownerInstance azurev1.Eventhub + var ownerInstance azurev1alpha1.Eventhub eventhubNamespacedName := types.NamespacedName{Name: eventhubName, Namespace: instance.Namespace} err = r.Get(ctx, eventhubNamespacedName, &ownerInstance) @@ -123,7 +123,7 @@ func (r *ConsumerGroupReconciler) createConsumerGroup(instance *azurev1.Consumer //set owner reference for consumer group if it exists references := []metav1.OwnerReference{ metav1.OwnerReference{ - APIVersion: "v1", + APIVersion: "v1alpha1", Kind: "Eventhub", Name: ownerInstance.GetName(), UID: ownerInstance.GetUID(), @@ -166,7 +166,7 @@ func (r *ConsumerGroupReconciler) createConsumerGroup(instance *azurev1.Consumer } -func (r *ConsumerGroupReconciler) deleteConsumerGroup(instance *azurev1.ConsumerGroup) error { +func (r *ConsumerGroupReconciler) deleteConsumerGroup(instance *azurev1alpha1.ConsumerGroup) error { ctx := context.Background() consumergroupName := instance.ObjectMeta.Name diff --git a/controllers/consumergroup_controller_finalizer.go b/controllers/consumergroup_controller_finalizer.go index 99cc3883fd8..5cfbe048591 100644 --- a/controllers/consumergroup_controller_finalizer.go +++ b/controllers/consumergroup_controller_finalizer.go @@ -20,7 +20,7 @@ import ( "context" "fmt" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" ) const consumerGroupFinalizerName = "consumergroup.finalizers.com" diff --git a/controllers/consumergroup_controller_test.go b/controllers/consumergroup_controller_test.go index 21e82883515..6a71f49af12 100644 --- a/controllers/consumergroup_controller_test.go +++ b/controllers/consumergroup_controller_test.go @@ -18,7 +18,7 @@ package controllers import ( "context" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" helpers "github.com/Azure/azure-service-operator/pkg/helpers" "time" @@ -62,12 +62,12 @@ var _ = Describe("ConsumerGroup Controller", func() { var err error // Create the consumer group object and expect the Reconcile to be created - consumerGroupInstance := &azurev1.ConsumerGroup{ + consumerGroupInstance := &azurev1alpha1.ConsumerGroup{ ObjectMeta: metav1.ObjectMeta{ Name: consumerGroupName, Namespace: "default", }, - Spec: azurev1.ConsumerGroupSpec{ + Spec: azurev1alpha1.ConsumerGroupSpec{ NamespaceName: ehnName, ResourceGroupName: rgName, EventhubName: ehName, diff --git a/controllers/eventhub_controller.go b/controllers/eventhub_controller.go index 6712fa8b2ee..ecf66dc9ac4 100644 --- a/controllers/eventhub_controller.go +++ b/controllers/eventhub_controller.go @@ -21,7 +21,7 @@ import ( "time" model "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" "github.com/Azure/azure-service-operator/pkg/errhelp" "github.com/Azure/azure-service-operator/pkg/helpers" @@ -58,7 +58,7 @@ func (r *EventhubReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { log := r.Log.WithValues("eventhub", req.NamespacedName) // your logic here - var instance azurev1.Eventhub + var instance azurev1alpha1.Eventhub if err := r.Get(ctx, req.NamespacedName, &instance); err != nil { log.Info("Unable to retrieve eventhub resource", "err", err.Error()) @@ -110,11 +110,11 @@ func (r *EventhubReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { // SetupWithManager binds the reconciler to a manager instance func (r *EventhubReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&azurev1.Eventhub{}). + For(&azurev1alpha1.Eventhub{}). Complete(r) } -func (r *EventhubReconciler) reconcileExternal(instance *azurev1.Eventhub) error { +func (r *EventhubReconciler) reconcileExternal(instance *azurev1alpha1.Eventhub) error { ctx := context.Background() var err error @@ -134,7 +134,7 @@ func (r *EventhubReconciler) reconcileExternal(instance *azurev1.Eventhub) error instance.Status.Provisioning = true //get owner instance - var ownerInstance azurev1.EventhubNamespace + var ownerInstance azurev1alpha1.EventhubNamespace eventhubNamespacedName := types.NamespacedName{Name: eventhubNamespace, Namespace: instance.Namespace} err = r.Get(ctx, eventhubNamespacedName, &ownerInstance) @@ -195,7 +195,7 @@ func (r *EventhubReconciler) reconcileExternal(instance *azurev1.Eventhub) error return nil } -func (r *EventhubReconciler) deleteEventhub(instance *azurev1.Eventhub) error { +func (r *EventhubReconciler) deleteEventhub(instance *azurev1alpha1.Eventhub) error { ctx := context.Background() @@ -279,7 +279,7 @@ func (r *EventhubReconciler) createEventhubSecrets( eventhubNamespace string, secretName string, sharedAccessKey string, - instance *azurev1.Eventhub) error { + instance *azurev1alpha1.Eventhub) error { csecret := &v1.Secret{ TypeMeta: metav1.TypeMeta{ @@ -319,7 +319,7 @@ func (r *EventhubReconciler) createEventhubSecrets( return nil } -func (r *EventhubReconciler) getEventhubSecrets(name string, instance *azurev1.Eventhub) error { +func (r *EventhubReconciler) getEventhubSecrets(name string, instance *azurev1alpha1.Eventhub) error { var err error secret := &v1.Secret{} diff --git a/controllers/eventhub_controller_finalizer.go b/controllers/eventhub_controller_finalizer.go index a6656a5c2db..f4be8fd1d6c 100644 --- a/controllers/eventhub_controller_finalizer.go +++ b/controllers/eventhub_controller_finalizer.go @@ -20,13 +20,13 @@ import ( "context" "fmt" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" "github.com/Azure/azure-service-operator/pkg/errhelp" ) const eventhubFinalizerName = "eventhub.finalizers.com" -func (r *EventhubReconciler) addFinalizer(instance *azurev1.Eventhub) error { +func (r *EventhubReconciler) addFinalizer(instance *azurev1alpha1.Eventhub) error { instance.AddFinalizer(eventhubFinalizerName) err := r.Update(context.Background(), instance) if err != nil { @@ -36,7 +36,7 @@ func (r *EventhubReconciler) addFinalizer(instance *azurev1.Eventhub) error { return nil } -func (r *EventhubReconciler) handleFinalizer(instance *azurev1.Eventhub) error { +func (r *EventhubReconciler) handleFinalizer(instance *azurev1alpha1.Eventhub) error { if instance.HasFinalizer(eventhubFinalizerName) { // our finalizer is present, so lets handle our external dependency if err := r.deleteExternalDependency(instance); err != nil { @@ -53,7 +53,7 @@ func (r *EventhubReconciler) handleFinalizer(instance *azurev1.Eventhub) error { return nil } -func (r *EventhubReconciler) deleteExternalDependency(instance *azurev1.Eventhub) error { +func (r *EventhubReconciler) deleteExternalDependency(instance *azurev1alpha1.Eventhub) error { return r.deleteEventhub(instance) } diff --git a/controllers/eventhub_controller_test.go b/controllers/eventhub_controller_test.go index 0411204bf6e..ebff08aaa50 100644 --- a/controllers/eventhub_controller_test.go +++ b/controllers/eventhub_controller_test.go @@ -18,7 +18,7 @@ package controllers import ( "context" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" helpers "github.com/Azure/azure-service-operator/pkg/helpers" "time" @@ -59,12 +59,12 @@ var _ = Describe("EventHub Controller", func() { eventhubName := "t-eh-" + helpers.RandomString(10) // Create the EventHub object and expect the Reconcile to be created - eventhubInstance := &azurev1.Eventhub{ + eventhubInstance := &azurev1alpha1.Eventhub{ ObjectMeta: metav1.ObjectMeta{ Name: eventhubName, Namespace: "default", }, - Spec: azurev1.EventhubSpec{ + Spec: azurev1alpha1.EventhubSpec{ Location: "westus", Namespace: "t-ns-dev-eh-" + helpers.RandomString(10), ResourceGroup: "t-rg-dev-eh-" + helpers.RandomString(10), @@ -93,20 +93,20 @@ var _ = Describe("EventHub Controller", func() { var err error // Create the EventHub object and expect the Reconcile to be created - eventhubInstance := &azurev1.Eventhub{ + eventhubInstance := &azurev1alpha1.Eventhub{ ObjectMeta: metav1.ObjectMeta{ Name: eventhubName, Namespace: "default", }, - Spec: azurev1.EventhubSpec{ + Spec: azurev1alpha1.EventhubSpec{ Location: "westus", Namespace: ehnName, ResourceGroup: rgName, - Properties: azurev1.EventhubProperties{ + Properties: azurev1alpha1.EventhubProperties{ MessageRetentionInDays: 7, PartitionCount: 1, }, - AuthorizationRule: azurev1.EventhubAuthorizationRule{ + AuthorizationRule: azurev1alpha1.EventhubAuthorizationRule{ Name: "RootManageSharedAccessKey", Rights: []string{"Listen"}, }, @@ -180,20 +180,20 @@ var _ = Describe("EventHub Controller", func() { var err error // Create the EventHub object and expect the Reconcile to be created - eventhubInstance := &azurev1.Eventhub{ + eventhubInstance := &azurev1alpha1.Eventhub{ ObjectMeta: metav1.ObjectMeta{ Name: eventhubName, Namespace: "default", }, - Spec: azurev1.EventhubSpec{ + Spec: azurev1alpha1.EventhubSpec{ Location: "westus", Namespace: ehnName, ResourceGroup: rgName, - Properties: azurev1.EventhubProperties{ + Properties: azurev1alpha1.EventhubProperties{ MessageRetentionInDays: 7, PartitionCount: 1, }, - AuthorizationRule: azurev1.EventhubAuthorizationRule{ + AuthorizationRule: azurev1alpha1.EventhubAuthorizationRule{ Name: "RootManageSharedAccessKey", Rights: []string{"Listen"}, }, diff --git a/controllers/eventhubnamespace_controller.go b/controllers/eventhubnamespace_controller.go index ede2773458d..2b6a76692bd 100644 --- a/controllers/eventhubnamespace_controller.go +++ b/controllers/eventhubnamespace_controller.go @@ -20,7 +20,7 @@ import ( "fmt" "time" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" "github.com/Azure/azure-service-operator/pkg/errhelp" "github.com/Azure/azure-service-operator/pkg/helpers" eventhubsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/eventhubs" @@ -96,11 +96,11 @@ func (r *EventhubNamespaceReconciler) Reconcile(req ctrl.Request) (ctrl.Result, //SetupWithManager sets up the functions for the controller func (r *EventhubNamespaceReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&azurev1.EventhubNamespace{}). + For(&azurev1alpha1.EventhubNamespace{}). Complete(r) } -func (r *EventhubNamespaceReconciler) reconcileExternal(instance *azurev1.EventhubNamespace) error { +func (r *EventhubNamespaceReconciler) reconcileExternal(instance *azurev1alpha1.EventhubNamespace) error { ctx := context.Background() var err error @@ -113,7 +113,7 @@ func (r *EventhubNamespaceReconciler) reconcileExternal(instance *azurev1.Eventh instance.Status.Provisioning = true //get owner instance - var ownerInstance azurev1.ResourceGroup + var ownerInstance azurev1alpha1.ResourceGroup resourceGroupNamespacedName := types.NamespacedName{Name: resourcegroup, Namespace: instance.Namespace} err = r.Get(ctx, resourceGroupNamespacedName, &ownerInstance) @@ -168,7 +168,7 @@ func (r *EventhubNamespaceReconciler) reconcileExternal(instance *azurev1.Eventh } -func (r *EventhubNamespaceReconciler) deleteEventhubNamespace(instance *azurev1.EventhubNamespace) error { +func (r *EventhubNamespaceReconciler) deleteEventhubNamespace(instance *azurev1alpha1.EventhubNamespace) error { ctx := context.Background() diff --git a/controllers/eventhubnamespace_controller_finalizer.go b/controllers/eventhubnamespace_controller_finalizer.go index 0b8bf6b3a9d..73d25b24647 100644 --- a/controllers/eventhubnamespace_controller_finalizer.go +++ b/controllers/eventhubnamespace_controller_finalizer.go @@ -20,12 +20,12 @@ import ( "context" "fmt" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" ) const eventhubNamespaceFinalizerName = "eventhubnamespace.finalizers.com" -func (r *EventhubNamespaceReconciler) addFinalizer(instance *azurev1.EventhubNamespace) error { +func (r *EventhubNamespaceReconciler) addFinalizer(instance *azurev1alpha1.EventhubNamespace) error { instance.AddFinalizer(eventhubNamespaceFinalizerName) err := r.Update(context.Background(), instance) if err != nil { @@ -35,7 +35,7 @@ func (r *EventhubNamespaceReconciler) addFinalizer(instance *azurev1.EventhubNam return nil } -func (r *EventhubNamespaceReconciler) handleFinalizer(instance *azurev1.EventhubNamespace) error { +func (r *EventhubNamespaceReconciler) handleFinalizer(instance *azurev1alpha1.EventhubNamespace) error { if instance.HasFinalizer(eventhubNamespaceFinalizerName) { // our finalizer is present, so lets handle our external dependency if err := r.deleteExternalDependency(instance); err != nil { @@ -51,7 +51,7 @@ func (r *EventhubNamespaceReconciler) handleFinalizer(instance *azurev1.Eventhub return nil } -func (r *EventhubNamespaceReconciler) deleteExternalDependency(instance *azurev1.EventhubNamespace) error { +func (r *EventhubNamespaceReconciler) deleteExternalDependency(instance *azurev1alpha1.EventhubNamespace) error { return r.deleteEventhubNamespace(instance) } diff --git a/controllers/eventhubnamespace_controller_test.go b/controllers/eventhubnamespace_controller_test.go index 15f6c0d061b..53558bb48b2 100644 --- a/controllers/eventhubnamespace_controller_test.go +++ b/controllers/eventhubnamespace_controller_test.go @@ -18,7 +18,7 @@ package controllers import ( "context" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" helpers "github.com/Azure/azure-service-operator/pkg/helpers" "time" @@ -58,12 +58,12 @@ var _ = Describe("EventHubNamespace Controller", func() { eventhubNamespaceName := "t-ns" // Create the EventHubNamespace object and expect the Reconcile to be created - eventhubNamespaceInstance := &azurev1.EventhubNamespace{ + eventhubNamespaceInstance := &azurev1alpha1.EventhubNamespace{ ObjectMeta: metav1.ObjectMeta{ Name: eventhubNamespaceName, Namespace: "default", }, - Spec: azurev1.EventhubNamespaceSpec{ + Spec: azurev1alpha1.EventhubNamespaceSpec{ Location: "westus", ResourceGroup: resourceGroupName, }, @@ -87,12 +87,12 @@ var _ = Describe("EventHubNamespace Controller", func() { eventhubNamespaceName := "t-ns-dev-eh-" + helpers.RandomString(10) // Create the EventHubNamespace object and expect the Reconcile to be created - eventhubNamespaceInstance := &azurev1.EventhubNamespace{ + eventhubNamespaceInstance := &azurev1alpha1.EventhubNamespace{ ObjectMeta: metav1.ObjectMeta{ Name: eventhubNamespaceName, Namespace: "default", }, - Spec: azurev1.EventhubNamespaceSpec{ + Spec: azurev1alpha1.EventhubNamespaceSpec{ Location: "westus", ResourceGroup: resourceGroupName, }, @@ -117,12 +117,12 @@ var _ = Describe("EventHubNamespace Controller", func() { var err error // Create the Eventhub namespace object and expect the Reconcile to be created - eventhubNamespaceInstance := &azurev1.EventhubNamespace{ + eventhubNamespaceInstance := &azurev1alpha1.EventhubNamespace{ ObjectMeta: metav1.ObjectMeta{ Name: eventhubNamespaceName, Namespace: "default", }, - Spec: azurev1.EventhubNamespaceSpec{ + Spec: azurev1alpha1.EventhubNamespaceSpec{ Location: "westus", ResourceGroup: rgName, }, diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 30be158aed9..13a64bca218 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -17,12 +17,13 @@ package controllers import ( "context" - helpers "github.com/Azure/azure-service-operator/pkg/helpers" "os" "path/filepath" "testing" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + helpers "github.com/Azure/azure-service-operator/pkg/helpers" + + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" resourcemanagerconfig "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" eventhubs "github.com/Azure/azure-service-operator/pkg/resourcemanager/eventhubs" @@ -89,16 +90,16 @@ var _ = BeforeSuite(func(done Done) { Expect(err).ToNot(HaveOccurred()) Expect(cfg).ToNot(BeNil()) - err = azurev1.AddToScheme(scheme.Scheme) + err = azurev1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = azurev1.AddToScheme(scheme.Scheme) + err = azurev1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = azurev1.AddToScheme(scheme.Scheme) + err = azurev1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = azurev1.AddToScheme(scheme.Scheme) + err = azurev1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) // +kubebuilder:scaffold:scheme diff --git a/main.go b/main.go index 1ed35c2930f..2a3d2f0f142 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,7 @@ import ( "flag" "os" - azurev1 "github.com/Azure/azure-service-operator/api/v1" + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" "github.com/Azure/azure-service-operator/controllers" resourcemanagerconfig "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" @@ -38,9 +38,9 @@ var ( func init() { - azurev1.AddToScheme(scheme) + azurev1alpha1.AddToScheme(scheme) kscheme.AddToScheme(scheme) - _ = azurev1.AddToScheme(scheme) + _ = azurev1alpha1.AddToScheme(scheme) // +kubebuilder:scaffold:scheme }