diff --git a/Makefile b/Makefile index eb5dbb1af75..cc7088e4478 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ test: generate fmt vet manifests 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/... ./pkg/resourcemanager/eventhubs/... ./pkg/resourcemanager/resourcegroups/... 2>&1 | tee testlogs-existing.txt + TEST_USE_EXISTING_CLUSTER=true go test -test.parallel 3 -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 @@ -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/api/v1/sqldatabase_types.go b/api/v1/sqldatabase_types.go index 13ef329537f..bb89e1619d1 100644 --- a/api/v1/sqldatabase_types.go +++ b/api/v1/sqldatabase_types.go @@ -26,10 +26,10 @@ import ( type SqlDatabaseSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - Location string `json:"location"` - ResourceGroup string `json:"resourcegroup,omitempty"` - Server string `json:"server"` - Edition sql.DBAddition `json:"edition"` + Location string `json:"location"` + ResourceGroup string `json:"resourcegroup,omitempty"` + Server string `json:"server"` + Edition sql.DBEdition `json:"edition"` } // SqlDatabaseStatus defines the observed state of SqlDatabase 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/crd/bases/azure.microsoft.com_sqldatabases.yaml b/config/crd/bases/azure.microsoft.com_sqldatabases.yaml index e02ff88e09e..5b0408a8169 100644 --- a/config/crd/bases/azure.microsoft.com_sqldatabases.yaml +++ b/config/crd/bases/azure.microsoft.com_sqldatabases.yaml @@ -402,7 +402,7 @@ spec: description: SqlDatabaseSpec defines the desired state of SqlDatabase properties: edition: - description: 'DBAddition - wraps: https://godoc.org/github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql#DatabaseEdition' + description: 'DBEdition - wraps: https://godoc.org/github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql#DatabaseEdition' type: integer location: description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster diff --git a/config/crd/bases/azure.microsoft.com_sqlfirewallrules.yaml b/config/crd/bases/azure.microsoft.com_sqlfirewallrules.yaml index 32caf38eca5..64463bcb3da 100644 --- a/config/crd/bases/azure.microsoft.com_sqlfirewallrules.yaml +++ b/config/crd/bases/azure.microsoft.com_sqlfirewallrules.yaml @@ -401,12 +401,16 @@ spec: spec: description: SqlFirewallRuleSpec defines the desired state of SqlFirewallRule properties: + endipaddress: + type: string location: description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run "make" to regenerate code after modifying this file' type: string resourcegroup: type: string + startipaddress: + type: string required: - location type: object diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 451b4729c65..f5685adcc7c 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -9,7 +9,7 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubs + - consumergroups verbs: - create - delete @@ -21,15 +21,11 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubs/status + - resourcegroups/status verbs: - - create - - delete - get - - list - patch - update - - watch - apiGroups: - azure.microsoft.com resources: @@ -51,7 +47,7 @@ rules: - patch - update - apiGroups: - - apps + - "" resources: - secrets verbs: @@ -62,10 +58,17 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - watch - apiGroups: - apps resources: - - consumergroups + - deployments/status verbs: - get - patch @@ -73,15 +76,15 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubnamespaces + - eventhubnamespaces/status verbs: - get - patch - update - apiGroups: - - apps + - azure.microsoft.com resources: - - deployments + - keyvaults verbs: - create - delete @@ -93,38 +96,54 @@ rules: - apiGroups: - azure.microsoft.com resources: - - consumergroups/status + - keyvaults/status verbs: - get - patch - update - apiGroups: - - "" + - azure.microsoft.com resources: - - eventhubnamespaces/status + - resourcegroups verbs: + - create + - delete - get + - list - patch - update + - watch - apiGroups: - azure.microsoft.com resources: - - eventhubs/status + - sqlservers verbs: + - create + - delete - get + - list - patch - update + - watch - apiGroups: - - "" + - azure.microsoft.com resources: - events verbs: - create - - watch + - patch - apiGroups: - azure.microsoft.com resources: - - deployments + - eventhubs/status + verbs: + - get + - patch + - update +- apiGroups: + - azure.microsoft.com + resources: + - sqldatabases verbs: - create - delete @@ -136,7 +155,23 @@ rules: - apiGroups: - azure.microsoft.com resources: - - eventhubnamespaces + - sqlservers/status + verbs: + - get + - patch + - update +- apiGroups: + - azure.microsoft.com + resources: + - consumergroups/status + verbs: + - get + - patch + - update +- apiGroups: + - azure.microsoft.com + resources: + - eventhubs verbs: - create - delete @@ -148,7 +183,7 @@ rules: - apiGroups: - azure.microsoft.com resources: - - resourcegroups + - eventhubnamespaces verbs: - create - delete @@ -160,15 +195,20 @@ rules: - apiGroups: - azure.microsoft.com resources: - - events + - sqldatabases/status verbs: - - create + - get - patch + - update - apiGroups: - apps resources: - - keyvaults/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/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.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 2271e708cf6..ede19e66777 100644 --- a/controllers/eventhub_controller_test.go +++ b/controllers/eventhub_controller_test.go @@ -35,8 +35,13 @@ import ( 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() { @@ -81,8 +86,7 @@ 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 @@ -95,8 +99,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, @@ -143,6 +147,7 @@ var _ = Describe("EventHub Controller", func() { "secondaryKey": []byte("secondaryKeyValue"), "sharedaccesskey": []byte("sharedAccessKeyValue"), "eventhubnamespace": []byte(eventhubInstance.Namespace), + "eventhubName": []byte(eventhubName), }, Type: "Opaque", } @@ -165,5 +170,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/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/keyvault_controller.go b/controllers/keyvault_controller.go index 79b82254f5b..1aa0d9a20db 100644 --- a/controllers/keyvault_controller.go +++ b/controllers/keyvault_controller.go @@ -65,7 +65,7 @@ func (r *KeyVaultReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { if helpers.IsBeingDeleted(&instance) { if helpers.HasFinalizer(&instance, keyVaultFinalizerName) { if err := r.deleteExternal(&instance); err != nil { - log.Info("Delete KeyVault failed with ", err.Error()) + log.Info("Delete KeyVault failed with ", "err", err) return ctrl.Result{}, err } diff --git a/controllers/sqlserver_controller.go b/controllers/sqlserver_controller.go index 7b1c0364599..68133c91216 100644 --- a/controllers/sqlserver_controller.go +++ b/controllers/sqlserver_controller.go @@ -135,7 +135,6 @@ func (r *SqlServerReconciler) reconcileExternal(instance *azurev1.SqlServer) err sqlServerProperties := sql.SQLServerProperties{ AdministratorLogin: to.StringPtr("iamadmin"), AdministratorLoginPassword: to.StringPtr("generate_me_1234"), - AllowAzureServicesAccess: true, } instance.Status.Provisioning = true diff --git a/controllers/sqlserver_controller_test.go b/controllers/sqlserver_controller_test.go index 85e2e8c86cc..e5e4f307a15 100644 --- a/controllers/sqlserver_controller_test.go +++ b/controllers/sqlserver_controller_test.go @@ -48,180 +48,180 @@ var _ = Describe("Sql Server Controller", func() { // test Kubernetes API server, which isn't the goal here. Context("SQL server tests - create/delete, update, validate name", func() { - It("Should create & delete a SQL Server when resource group exists - Happy path", func() { - - resourceGroupName := "t-rg-dev-sqls-" + helpers.RandomString(10) - sqlServerName := "t-sqls-" + helpers.RandomString(10) - - // 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()) - - // Create the sqlServer object and expect the Reconcile to be created - sqlServerInstance := &azurev1.SqlServer{ - ObjectMeta: metav1.ObjectMeta{ - Name: sqlServerName, - Namespace: "default", - }, - Spec: azurev1.SqlServerSpec{ - // TODO : Add Spec values - }, - } - - err = k8sClient.Create(context.Background(), sqlServerInstance) - Expect(apierrors.IsInvalid(err)).To(Equal(false)) - Expect(err).NotTo(HaveOccurred()) - - time.Sleep(60 * time.Second) - - sqlServerNamespacedName := types.NamespacedName{Name: sqlServerName, Namespace: "default"} - - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) - return helpers.HasFinalizer(sqlServerInstance, SQLServerFinalizerName) - }, timeout, - ).Should(BeTrue()) - - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) - return sqlServerInstance.IsSubmitted() - }, timeout, - ).Should(BeTrue()) - - // Delete the SQL server instance - k8sClient.Delete(context.Background(), sqlServerInstance) - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) - return helpers.IsBeingDeleted(sqlServerInstance) - }, timeout, - ).Should(BeTrue()) - - time.Sleep(2 * time.Second) - - _, err = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) - Expect(err).NotTo(HaveOccurred()) - - }) - - It("Should fail to create a SQL server with invalid name", func() { - resourceGroupName := "t-rg-dev-sqls-" + helpers.RandomString(10) - - // Use a name with upper case and underscore which is invalid - sqlServerName := "T-_SQLS-" + helpers.RandomString(10) - - // 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()) - - // Create the sqlServer object and expect the Reconcile to be created - sqlServerInstance := &azurev1.SqlServer{ - ObjectMeta: metav1.ObjectMeta{ - Name: sqlServerName, - Namespace: "default", - }, - Spec: azurev1.SqlServerSpec{ - // TODO : Add Spec values - }, - } - - k8sClient.Create(context.Background(), sqlServerInstance) - - sqlServerNamespacedName := types.NamespacedName{Name: sqlServerName, Namespace: "default"} - - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) - return sqlServerInstance.IsSubmitted() - }, timeout, - ).Should(BeFalse()) - - }) - It("Should create and delete a SQL server when resource group creation happens after SQL server creation (declarative model test)", func() { - resourceGroupName := "t-rg-dev-sqls-" + helpers.RandomString(10) - sqlServerName := "t-sqls-" + helpers.RandomString(10) - - // 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", - }, - } - - // Create the sqlServer object and expect the Reconcile to be created - sqlServerInstance := &azurev1.SqlServer{ - ObjectMeta: metav1.ObjectMeta{ - Name: sqlServerName, - Namespace: "default", - }, - Spec: azurev1.SqlServerSpec{ - // TODO : Add Spec values - }, - } - - // Create the SQL server first before the resource group - err := k8sClient.Create(context.Background(), sqlServerInstance) - Expect(apierrors.IsInvalid(err)).To(Equal(false)) - Expect(err).NotTo(HaveOccurred()) - - // Create the resource group now - err = k8sClient.Create(context.Background(), resourceGroupInstance) - Expect(apierrors.IsInvalid(err)).To(Equal(false)) - Expect(err).NotTo(HaveOccurred()) - - time.Sleep(60 * time.Second) - - sqlServerNamespacedName := types.NamespacedName{Name: sqlServerName, Namespace: "default"} - - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) - return helpers.HasFinalizer(sqlServerInstance, SQLServerFinalizerName) - }, timeout, - ).Should(BeTrue()) - - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) - return sqlServerInstance.IsSubmitted() - }, timeout, - ).Should(BeTrue()) - - // Delete the SQL server instance - k8sClient.Delete(context.Background(), sqlServerInstance) - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) - return helpers.IsBeingDeleted(sqlServerInstance) - }, timeout, - ).Should(BeTrue()) - - time.Sleep(2 * time.Second) - - _, err = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) - Expect(err).NotTo(HaveOccurred()) - }) - + It("Should create & delete a SQL Server when resource group exists - Happy path", func() { + + resourceGroupName := "t-rg-dev-sqls-" + helpers.RandomString(10) + sqlServerName := "t-sqls-" + helpers.RandomString(10) + + // 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()) + + // Create the sqlServer object and expect the Reconcile to be created + sqlServerInstance := &azurev1.SqlServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: sqlServerName, + Namespace: "default", + }, + Spec: azurev1.SqlServerSpec{ + // TODO : Add Spec values + }, + } + + err = k8sClient.Create(context.Background(), sqlServerInstance) + Expect(apierrors.IsInvalid(err)).To(Equal(false)) + Expect(err).NotTo(HaveOccurred()) + + time.Sleep(60 * time.Second) + + sqlServerNamespacedName := types.NamespacedName{Name: sqlServerName, Namespace: "default"} + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) + return helpers.HasFinalizer(sqlServerInstance, SQLServerFinalizerName) + }, timeout, + ).Should(BeTrue()) + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) + return sqlServerInstance.IsSubmitted() + }, timeout, + ).Should(BeTrue()) + + // Delete the SQL server instance + k8sClient.Delete(context.Background(), sqlServerInstance) + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) + return helpers.IsBeingDeleted(sqlServerInstance) + }, timeout, + ).Should(BeTrue()) + + time.Sleep(2 * time.Second) + + _, err = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) + Expect(err).NotTo(HaveOccurred()) + + }) + + It("Should fail to create a SQL server with invalid name", func() { + resourceGroupName := "t-rg-dev-sqls-" + helpers.RandomString(10) + + // Use a name with upper case and underscore which is invalid + sqlServerName := "T-_SQLS-" + helpers.RandomString(10) + + // 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()) + + // Create the sqlServer object and expect the Reconcile to be created + sqlServerInstance := &azurev1.SqlServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: sqlServerName, + Namespace: "default", + }, + Spec: azurev1.SqlServerSpec{ + // TODO : Add Spec values + }, + } + + k8sClient.Create(context.Background(), sqlServerInstance) + + sqlServerNamespacedName := types.NamespacedName{Name: sqlServerName, Namespace: "default"} + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) + return sqlServerInstance.IsSubmitted() + }, timeout, + ).Should(BeFalse()) + + }) + It("Should create and delete a SQL server when resource group creation happens after SQL server creation (declarative model test)", func() { + resourceGroupName := "t-rg-dev-sqls-" + helpers.RandomString(10) + sqlServerName := "t-sqls-" + helpers.RandomString(10) + + // 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", + }, + } + + // Create the sqlServer object and expect the Reconcile to be created + sqlServerInstance := &azurev1.SqlServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: sqlServerName, + Namespace: "default", + }, + Spec: azurev1.SqlServerSpec{ + // TODO : Add Spec values + }, + } + + // Create the SQL server first before the resource group + err := k8sClient.Create(context.Background(), sqlServerInstance) + Expect(apierrors.IsInvalid(err)).To(Equal(false)) + Expect(err).NotTo(HaveOccurred()) + + // Create the resource group now + err = k8sClient.Create(context.Background(), resourceGroupInstance) + Expect(apierrors.IsInvalid(err)).To(Equal(false)) + Expect(err).NotTo(HaveOccurred()) + + time.Sleep(60 * time.Second) + + sqlServerNamespacedName := types.NamespacedName{Name: sqlServerName, Namespace: "default"} + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) + return helpers.HasFinalizer(sqlServerInstance, SQLServerFinalizerName) + }, timeout, + ).Should(BeTrue()) + + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) + return sqlServerInstance.IsSubmitted() + }, timeout, + ).Should(BeTrue()) + + // Delete the SQL server instance + k8sClient.Delete(context.Background(), sqlServerInstance) + Eventually(func() bool { + _ = k8sClient.Get(context.Background(), sqlServerNamespacedName, sqlServerInstance) + return helpers.IsBeingDeleted(sqlServerInstance) + }, timeout, + ).Should(BeTrue()) + + time.Sleep(2 * time.Second) + + _, err = resoucegroupsresourcemanager.DeleteGroup(context.Background(), resourceGroupName) + Expect(err).NotTo(HaveOccurred()) + }) + }) }) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 8b022e99e19..2e164670cb2 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -21,6 +21,8 @@ import ( "path/filepath" "testing" + helpers "github.com/Azure/azure-service-operator/pkg/helpers" + azurev1 "github.com/Azure/azure-service-operator/api/v1" resourcemanagerconfig "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" @@ -55,11 +57,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", @@ -154,6 +156,7 @@ var _ = BeforeSuite(func(done Done) { // Create the Eventhub resource _, err = eventhubs.CreateHub(context.Background(), resourceGroupName, eventhubNamespaceName, eventhubName, int32(7), int32(1)) + close(done) }, 120) 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 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") diff --git a/pkg/resourcemanager/sqlclient/endtoend_test.go b/pkg/resourcemanager/sqlclient/endtoend_test.go index 6fd007a7584..f85d7854b42 100644 --- a/pkg/resourcemanager/sqlclient/endtoend_test.go +++ b/pkg/resourcemanager/sqlclient/endtoend_test.go @@ -40,22 +40,13 @@ func TestCreateOrUpdateSQLServer(t *testing.T) { ServerName: generateName("sqlsrvtest"), Location: "eastus2", } - - // create the sql server properties struct + + // create the server sqlServerProperties := SQLServerProperties{ AdministratorLogin: to.StringPtr("Moss"), AdministratorLoginPassword: to.StringPtr("TheITCrowd_{01}!"), } - // this firewall rule should fail (the server doesn't exist yet) - result, err := sdk.CreateOrUpdateSQLFirewallRule("fail rule", "0.0.0.0", "1.2.3.4") - if result { - util.PrintAndLog("firewall rule add succeeded, but shouldn't have") - t.FailNow() - } else { - util.PrintAndLog("firewall rule add failed (good)") - } - // wait for server to be created, then only proceed once activated for { time.Sleep(time.Second) @@ -77,24 +68,6 @@ func TestCreateOrUpdateSQLServer(t *testing.T) { } } - // this firewall rule should succeed - result, err = sdk.CreateOrUpdateSQLFirewallRule("succeed rule", "0.0.0.0", "4.3.2.1") - if result { - util.PrintAndLog("firewall rule add succeeded") - } else { - util.PrintAndLog("firewall rule add failed, but should have succeeded") - t.FailNow() - } - - // this firewall rule deletion should succeed - err = sdk.DeleteSQLFirewallRule("succeed rule") - if err == nil { - util.PrintAndLog("firewall rule deletion succeeded") - } else { - util.PrintAndLog(fmt.Sprintf("firewall rule deletion failed, but should have succeeded: %v", err)) - t.FailNow() - } - // create a DB sqlDBProperties := SQLDatabaseProperties{ DatabaseName: "testDB", diff --git a/pkg/resourcemanager/sqlclient/resourceclient.go b/pkg/resourcemanager/sqlclient/resourceclient.go index b3329a923b6..e14f3b46a67 100644 --- a/pkg/resourcemanager/sqlclient/resourceclient.go +++ b/pkg/resourcemanager/sqlclient/resourceclient.go @@ -13,10 +13,12 @@ import ( // ResourceClient contains the helper functions for interacting with SQL servers / databases type ResourceClient interface { CreateOrUpdateSQLServer(properties SQLServerProperties) (result sql.Server, err error) + CreateOrUpdateSQLFirewallRule(ruleName string, startIP string, endIP string) (result bool, err error) CreateOrUpdateDB(properties SQLDatabaseProperties) (result sql.Database, err error) DeleteDB(databaseName string) (result autorest.Response, err error) DeleteSQLServer() (result autorest.Response, err error) DeleteSQLFirewallRule(ruleName string) (err error) + IsAsyncNotCompleted(err error) (result bool) } diff --git a/pkg/resourcemanager/sqlclient/sqlclient_godsk.go b/pkg/resourcemanager/sqlclient/sqlclient_godsk.go index 216aefcec0f..072638344d4 100644 --- a/pkg/resourcemanager/sqlclient/sqlclient_godsk.go +++ b/pkg/resourcemanager/sqlclient/sqlclient_godsk.go @@ -12,7 +12,6 @@ import ( "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" ) @@ -251,3 +250,14 @@ func (sdk GoSDKClient) IsAsyncNotCompleted(err error) (result bool) { } return result } + +// GetServer returns a server +func (sdk GoSDKClient) GetServer(rgroup, name string) (sql.Server, error) { + serversClient := getGoServersClient() + + return serversClient.Get( + sdk.Ctx, + sdk.ResourceGroupName, + sdk.ServerName, + ) +}