From 647dd4a7b15f22ba2125c26efcb53f5446f04e1c Mon Sep 17 00:00:00 2001 From: William Mortl <32373900+WilliamMortlMicrosoft@users.noreply.github.com> Date: Mon, 27 Apr 2020 08:52:38 -0700 Subject: [PATCH 1/6] first (#999) Co-authored-by: William Mortl --- .../azuresql/azuresqlserver/azuresqlserver_reconcile.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/resourcemanager/azuresql/azuresqlserver/azuresqlserver_reconcile.go b/pkg/resourcemanager/azuresql/azuresqlserver/azuresqlserver_reconcile.go index 8e76fb24ab9..a1506bfed50 100644 --- a/pkg/resourcemanager/azuresql/azuresqlserver/azuresqlserver_reconcile.go +++ b/pkg/resourcemanager/azuresql/azuresqlserver/azuresqlserver_reconcile.go @@ -200,11 +200,13 @@ func (s *AzureSqlServerManager) Ensure(ctx context.Context, obj runtime.Object, case errhelp.LocationNotAvailableForResourceType, errhelp.RequestDisallowedByPolicy, errhelp.RegionDoesNotAllowProvisioning, + errhelp.InvalidResourceLocation, errhelp.QuotaExceeded: instance.Status.Message = "Unable to provision Azure SQL Server due to error: " + errhelp.StripErrorIDs(err) instance.Status.Provisioning = false instance.Status.Provisioned = false + instance.Status.FailedProvisioning = true return true, nil } From c003029db3e6800a85e9a72fa3c4850561782512 Mon Sep 17 00:00:00 2001 From: Claudia Nadolny Date: Mon, 27 Apr 2020 15:03:46 -0700 Subject: [PATCH 2/6] Updating Helm Chart and adding custom namespace (#985) * initial changes for vnet rule operator * added mysql docs * remaining changes, added tests + controller logic * minor cleanup * fixed test * updated SKU type * updating helm chart * custom namespace changes * merged custom namespace changes * removing unrelated change * updated chart version + deps Co-authored-by: William Mortl <32373900+WilliamMortlMicrosoft@users.noreply.github.com> Co-authored-by: Erin Corson Co-authored-by: Janani Vasudevan <49576785+jananivMS@users.noreply.github.com> --- Makefile | 11 ++++++++--- charts/azure-service-operator-0.1.0.tgz | Bin 20132 -> 23655 bytes charts/azure-service-operator/Chart.yaml | 2 +- charts/azure-service-operator/README.md | 11 +++++++++-- .../charts/aad-pod-identity-1.5.5.tgz | Bin 0 -> 7750 bytes .../azure-service-operator/requirements.lock | 6 ++++++ .../templates/namespace.yaml | 2 +- .../templates/secret.yaml | 3 ++- charts/azure-service-operator/values.yaml | 10 +++++++--- charts/index.yaml | 13 +++++++++---- docs/development.md | 13 +++++++++++++ 11 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 charts/azure-service-operator/charts/aad-pod-identity-1.5.5.tgz create mode 100644 charts/azure-service-operator/requirements.lock diff --git a/Makefile b/Makefile index 54cfbdffeb3..cd72d52f505 100644 --- a/Makefile +++ b/Makefile @@ -121,12 +121,17 @@ validate-copyright-headers: # Generate manifests for helm and package them up helm-chart-manifests: manifests - kustomize build ./config/default -o ./charts/azure-service-operator/templates - rm charts/azure-service-operator/templates/~g_v1_namespace_azureoperator-system.yaml - sed -i '' -e 's@controller:latest@{{ .Values.image.repository }}@' ./charts/azure-service-operator/templates/apps_v1_deployment_azureoperator-controller-manager.yaml + mkdir charts/azure-service-operator/templates/generated + kustomize build ./config/default -o ./charts/azure-service-operator/templates/generated + rm charts/azure-service-operator/templates/generated/~g_v1_namespace_azureoperator-system.yaml + sed -i '' -e 's@controller:latest@{{ .Values.image.repository }}@' ./charts/azure-service-operator/templates/generated/apps_v1_deployment_azureoperator-controller-manager.yaml + find ./charts/azure-service-operator/templates/generated/ -type f -exec sed -i '' -e 's@namespace: azureoperator-system@namespace: {{ .Values.namespace }}@' {} \; helm package ./charts/azure-service-operator -d ./charts helm repo index ./charts +delete-helm-gen-manifests: + rm -rf charts/azure-service-operator/templates/generated/ + # Generate manifests e.g. CRD, RBAC etc. manifests: controller-gen $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases diff --git a/charts/azure-service-operator-0.1.0.tgz b/charts/azure-service-operator-0.1.0.tgz index 10035abebc83029d852f5c13d6e5cb380f749405..a054aac8e41877069b731cfe204a7dbb3f8ccd7a 100644 GIT binary patch literal 23655 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 m2}lx3>NKU%%ysK5RjEo?V0O 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 z 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 u5NTDc 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 Date: Mon, 27 Apr 2020 12:34:27 +0800 Subject: [PATCH 3/6] task#965#AddVnetToPostgreSQLServer --- PROJECT | 3 + api/v1alpha1/postgresqlvnetrule_types.go | 47 +++++ config/crd/kustomization.yaml | 3 + .../cainjection_in_postgresqlvnetrules.yaml | 8 + .../webhook_in_postgresqlvnetrules.yaml | 17 ++ .../azure_v1alpha1_postgresqlserver.yaml | 8 +- .../azure_v1alpha1_postgresqlvnetrule.yaml | 11 ++ controllers/postgresqlvnetrule_controller.go | 28 +++ main.go | 17 ++ pkg/errhelp/errors.go | 1 + pkg/resourcemanager/psql/vnetrule/client.go | 114 ++++++++++++ pkg/resourcemanager/psql/vnetrule/manager.go | 26 +++ .../psql/vnetrule/reconcile.go | 173 ++++++++++++++++++ 13 files changed, 452 insertions(+), 4 deletions(-) create mode 100644 api/v1alpha1/postgresqlvnetrule_types.go create mode 100644 config/crd/patches/cainjection_in_postgresqlvnetrules.yaml create mode 100644 config/crd/patches/webhook_in_postgresqlvnetrules.yaml create mode 100644 config/samples/azure_v1alpha1_postgresqlvnetrule.yaml create mode 100644 controllers/postgresqlvnetrule_controller.go create mode 100644 pkg/resourcemanager/psql/vnetrule/client.go create mode 100644 pkg/resourcemanager/psql/vnetrule/manager.go create mode 100644 pkg/resourcemanager/psql/vnetrule/reconcile.go diff --git a/PROJECT b/PROJECT index 465436a9551..85521c1cf3d 100644 --- a/PROJECT +++ b/PROJECT @@ -50,6 +50,9 @@ resources: - group: azure version: v1alpha1 kind: PostgreSQLFirewallRule +- group: azure + version: v1alpha1 + kind: PostgreSQLVNetRule - group: azure version: v1alpha1 kind: APIMgmtAPI diff --git a/api/v1alpha1/postgresqlvnetrule_types.go b/api/v1alpha1/postgresqlvnetrule_types.go new file mode 100644 index 00000000000..c75e65d01f2 --- /dev/null +++ b/api/v1alpha1/postgresqlvnetrule_types.go @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package v1alpha1 + +import ( + 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. + +// PotgreSQLVNetRuleSpec defines the desired state of PostgreSQLVNetRule +type PostgreSQLVNetRuleSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + ResourceGroup string `json:"resourceGroup"` + Server string `json:"server"` + VNetResourceGroup string `json:"vNetResourceGroup"` + VNetName string `json:"vNetName"` + SubnetName string `json:"subnetName"` + IgnoreMissingServiceEndpoint bool `json:"ignoreMissingServiceEndpoint,omitempty"` +} + +// +kubebuilder:object:root=true + +// PostgreSQLVNetRule is the Schema for the PostgreSQLVNetRules API +type PostgreSQLVNetRule struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PostgreSQLVNetRuleSpec `json:"spec,omitempty"` + Status ASOStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// PostgreSQLVNetRuleList contains a list of PostgreSQLVNetRule +type PostgreSQLVNetRuleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PostgreSQLVNetRule `json:"items"` +} + +func init() { + SchemeBuilder.Register(&PostgreSQLVNetRule{}, &PostgreSQLVNetRuleList{}) +} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index b80b3e70e20..5923dfd3bb8 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -21,6 +21,7 @@ resources: - bases/azure.microsoft.com_postgresqlservers.yaml - bases/azure.microsoft.com_postgresqldatabases.yaml - bases/azure.microsoft.com_postgresqlfirewallrules.yaml +- bases/azure.microsoft.com_postgresqlvnetrules.yaml - bases/azure.microsoft.com_apimservices.yaml - bases/azure.microsoft.com_apimgmtapis.yaml - bases/azure.microsoft.com_virtualnetworks.yaml @@ -54,6 +55,7 @@ resources: #- patches/webhook_in_postgresqlservers.yaml #- patches/webhook_in_postgresqldatabases.yaml #- patches/webhook_in_postgresqlfirewallrules.yaml +#- patches/webhook_in_postgresqlvnetrules.yaml #- patches/webhook_in_apimservices.yaml #- patches/webhook_in_apimgmtapis.yaml #- patches/webhook_in_virtualnetworks.yaml @@ -86,6 +88,7 @@ resources: #- patches/cainjection_in_postgresqlservers.yaml #- patches/cainjection_in_postgresqldatabases.yaml #- patches/cainjection_in_postgresqlfirewallrules.yaml +#- patches/cainjection_in_postgresqlvnetrules.yaml #- patches/cainjection_in_apimservices.yaml #- patches/cainjection_in_apimgmtapis.yaml #- patches/cainjection_in_virtualnetworks.yaml diff --git a/config/crd/patches/cainjection_in_postgresqlvnetrules.yaml b/config/crd/patches/cainjection_in_postgresqlvnetrules.yaml new file mode 100644 index 00000000000..13ff16ea354 --- /dev/null +++ b/config/crd/patches/cainjection_in_postgresqlvnetrules.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: postgresqlvnetrules.azure.microsoft.com diff --git a/config/crd/patches/webhook_in_postgresqlvnetrules.yaml b/config/crd/patches/webhook_in_postgresqlvnetrules.yaml new file mode 100644 index 00000000000..0137abcbfa6 --- /dev/null +++ b/config/crd/patches/webhook_in_postgresqlvnetrules.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: postgresqlvnetrules.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/samples/azure_v1alpha1_postgresqlserver.yaml b/config/samples/azure_v1alpha1_postgresqlserver.yaml index f4ac4bfcb33..47c6b8aebf4 100644 --- a/config/samples/azure_v1alpha1_postgresqlserver.yaml +++ b/config/samples/azure_v1alpha1_postgresqlserver.yaml @@ -3,16 +3,16 @@ kind: PostgreSQLServer metadata: name: postgresqlserver-sample spec: - location: westus2 + location: eastus resourceGroup: resourcegroup-azure-operators serverVersion: "10" sslEnforcement: Enabled sku: - name: B_Gen5_2 - tier: Basic + name: GP_Gen5_4 # tier + family + cores eg. - B_Gen4_1, GP_Gen5_4 + tier: GeneralPurpose # possible values - 'Basic', 'GeneralPurpose', 'MemoryOptimized' family: Gen5 size: "51200" - capacity: 2 + capacity: 4 # Use the field below to optionally specify a different keyvault # to store the server admin credential secrets in #keyVaultToStoreSecrets: asoSecretKeyVault \ No newline at end of file diff --git a/config/samples/azure_v1alpha1_postgresqlvnetrule.yaml b/config/samples/azure_v1alpha1_postgresqlvnetrule.yaml new file mode 100644 index 00000000000..2195e8f978f --- /dev/null +++ b/config/samples/azure_v1alpha1_postgresqlvnetrule.yaml @@ -0,0 +1,11 @@ +apiVersion: azure.microsoft.com/v1alpha1 +kind: PostgreSQLVNetRule +metadata: + name: postgresqlvnetrule-sample1 +spec: + resourceGroup: resourcegroup-azure-operators + server: postgresqlserver-sample + vNetResourceGroup: resourcegroup-azure-operators + vNetName: virtualnetwork-sample + subnetName: test1 + ignoreMissingServiceEndpoint: true diff --git a/controllers/postgresqlvnetrule_controller.go b/controllers/postgresqlvnetrule_controller.go new file mode 100644 index 00000000000..e7c5772f7d1 --- /dev/null +++ b/controllers/postgresqlvnetrule_controller.go @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package controllers + +import ( + ctrl "sigs.k8s.io/controller-runtime" + + azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" +) + +// PostgreSQLVNetRuleReconciler reconciles a PostgreSQLVNetRule object +type PostgreSQLVNetRuleReconciler struct { + Reconciler *AsyncReconciler +} + +// +kubebuilder:rbac:groups=azure.microsoft.com,resources=postgresqlvnetrules,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=azure.microsoft.com,resources=postgresqlvnetrules/status,verbs=get;update;patch + +func (r *PostgreSQLVNetRuleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + return r.Reconciler.Reconcile(req, &azurev1alpha1.PostgreSQLVNetRule{}) +} + +func (r *PostgreSQLVNetRuleReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&azurev1alpha1.PostgreSQLVNetRule{}). + Complete(r) +} diff --git a/main.go b/main.go index 9de65c32d31..3edc9ea740c 100644 --- a/main.go +++ b/main.go @@ -34,6 +34,7 @@ import ( psqldatabase "github.com/Azure/azure-service-operator/pkg/resourcemanager/psql/database" psqlfirewallrule "github.com/Azure/azure-service-operator/pkg/resourcemanager/psql/firewallrule" psqlserver "github.com/Azure/azure-service-operator/pkg/resourcemanager/psql/server" + psqlvnetrule "github.com/Azure/azure-service-operator/pkg/resourcemanager/psql/vnetrule" resourcemanagerrediscache "github.com/Azure/azure-service-operator/pkg/resourcemanager/rediscaches" resourcemanagerresourcegroup "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" blobContainerManager "github.com/Azure/azure-service-operator/pkg/resourcemanager/storages/blobcontainer" @@ -669,6 +670,22 @@ func main() { os.Exit(1) } + if err = (&controllers.PostgreSQLVNetRuleReconciler{ + Reconciler: &controllers.AsyncReconciler{ + Client: mgr.GetClient(), + AzureClient: psqlvnetrule.NewPostgreSQLVNetRuleClient(), + Telemetry: telemetry.InitializeTelemetryDefault( + "PostgreSQLVNetRule", + ctrl.Log.WithName("controllers").WithName("PostgreSQLVNetRule"), + ), + Recorder: mgr.GetEventRecorderFor("PostgreSQLVNetRule-controller"), + Scheme: scheme, + }, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "PostgreSQLVNetRule") + os.Exit(1) + } + // +kubebuilder:scaffold:builder setupLog.Info("starting manager") diff --git a/pkg/errhelp/errors.go b/pkg/errhelp/errors.go index 4e2e2b39ab7..e6b5947db8e 100644 --- a/pkg/errhelp/errors.go +++ b/pkg/errhelp/errors.go @@ -61,6 +61,7 @@ const ( NetworkAclsValidationFailure = "NetworkAclsValidationFailure" SubnetHasServiceEndpointWithInvalidServiceName = "SubnetHasServiceEndpointWithInvalidServiceName" InvalidAddressPrefixFormat = "InvalidAddressPrefixFormat" + FeatureNotSupportedForEdition = "FeatureNotSupportedForEdition" ) func NewAzureError(err error) error { diff --git a/pkg/resourcemanager/psql/vnetrule/client.go b/pkg/resourcemanager/psql/vnetrule/client.go new file mode 100644 index 00000000000..466af00bcd6 --- /dev/null +++ b/pkg/resourcemanager/psql/vnetrule/client.go @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package server + +import ( + "context" + + network "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-01/network" + psql "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" + "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" + "github.com/Azure/azure-service-operator/pkg/resourcemanager/iam" +) + +type PostgreSQLVNetRuleClient struct { +} + +func NewPostgreSQLVNetRuleClient() *PostgreSQLVNetRuleClient { + return &PostgreSQLVNetRuleClient{} +} + +func GetPostgreSQLVNetRulesClient() psql.VirtualNetworkRulesClient { + VNetRulesClient := psql.NewVirtualNetworkRulesClientWithBaseURI(config.BaseURI(), config.SubscriptionID()) + a, _ := iam.GetResourceManagementAuthorizer() + VNetRulesClient.Authorizer = a + VNetRulesClient.AddToUserAgent(config.UserAgent()) + return VNetRulesClient +} + +// retrieves the Subnetclient +func GetGoNetworkSubnetClient() network.SubnetsClient { + SubnetsClient := network.NewSubnetsClientWithBaseURI(config.BaseURI(), config.SubscriptionID()) + a, _ := iam.GetResourceManagementAuthorizer() + SubnetsClient.Authorizer = a + SubnetsClient.AddToUserAgent(config.UserAgent()) + return SubnetsClient +} + +// GetPostgreSQLVNetRule returns a VNet rule +func (vr *PostgreSQLVNetRuleClient) GetPostgreSQLVNetRule( + ctx context.Context, + resourceGroupName string, + serverName string, + ruleName string) (result psql.VirtualNetworkRule, err error) { + + VNetRulesClient := GetPostgreSQLVNetRulesClient() + + return VNetRulesClient.Get( + ctx, + resourceGroupName, + serverName, + ruleName, + ) +} + +// deletes a VNet rule +func (vr *PostgreSQLVNetRuleClient) DeletePostgreSQLVNetRule(ctx context.Context, resourceGroupName string, serverName string, ruleName string) (err error) { + + // check to see if the rule exists, if it doesn't then short-circuit + _, err = vr.GetPostgreSQLVNetRule(ctx, resourceGroupName, serverName, ruleName) + if err != nil { + return nil + } + + VNetRulesClient := GetPostgreSQLVNetRulesClient() + _, err = VNetRulesClient.Delete( + ctx, + resourceGroupName, + serverName, + ruleName, + ) + + return err +} + +// creates or updates a VNet rule +func (vr *PostgreSQLVNetRuleClient) CreateOrUpdatePostgreSQLVNetRule( + ctx context.Context, + resourceGroupName string, + serverName string, + ruleName string, + VNetRG string, + VNetName string, + SubnetName string, + IgnoreServiceEndpoint bool) (vnr psql.VirtualNetworkRule, err error) { + + VNetRulesClient := GetPostgreSQLVNetRulesClient() + SubnetClient := GetGoNetworkSubnetClient() + + // Get ARM Resource ID of Subnet based on the VNET name, Subnet name and Subnet Address Prefix + subnet, err := SubnetClient.Get(ctx, VNetRG, VNetName, SubnetName, "") + if err != nil { + return vnr, err + } + subnetResourceID := *subnet.ID + + // Populate parameters with the right ID + parameters := psql.VirtualNetworkRule{ + VirtualNetworkRuleProperties: &psql.VirtualNetworkRuleProperties{ + VirtualNetworkSubnetID: &subnetResourceID, + IgnoreMissingVnetServiceEndpoint: &IgnoreServiceEndpoint, + }, + } + + // Call CreateOrUpdate + result, err := VNetRulesClient.CreateOrUpdate( + ctx, + resourceGroupName, + serverName, + ruleName, + parameters, + ) + return result.Result(VNetRulesClient) +} diff --git a/pkg/resourcemanager/psql/vnetrule/manager.go b/pkg/resourcemanager/psql/vnetrule/manager.go new file mode 100644 index 00000000000..8032c17588d --- /dev/null +++ b/pkg/resourcemanager/psql/vnetrule/manager.go @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package server + +import ( + "context" + + psql "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" + "github.com/Azure/azure-service-operator/pkg/resourcemanager" +) + +type PostgreSqlVNetRuleManager interface { + CreateOrUpdatePostgreSQLVNetRule( + ctx context.Context, + resourceGroupName string, + serverName string, + ruleName string, + VNetRG string, + VNetName string, + SubnetName string, + IgnoreServiceEndpoint bool) (result psql.VirtualNetworkRule, err error) + DeletePostgreSQLVNetRule(ctx context.Context, resourceGroupName string, serverName string, ruleName string) (err error) + GetPostgreSQLVNetRulesClient(ctx context.Context, resourceGroupName string, serverName string, ruleName string) (result psql.VirtualNetworkRule, err error) + resourcemanager.ARMClient +} diff --git a/pkg/resourcemanager/psql/vnetrule/reconcile.go b/pkg/resourcemanager/psql/vnetrule/reconcile.go new file mode 100644 index 00000000000..0c529d2e4ba --- /dev/null +++ b/pkg/resourcemanager/psql/vnetrule/reconcile.go @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package server + +import ( + "context" + "fmt" + "strings" + + psql "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" + + 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" + "github.com/Azure/azure-service-operator/pkg/resourcemanager" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" +) + +// Ensure creates a postgresqlvnetrule +func (vr *PostgreSQLVNetRuleClient) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { + instance, err := vr.convert(obj) + if err != nil { + return false, err + } + + groupName := instance.Spec.ResourceGroup + server := instance.Spec.Server + ruleName := instance.ObjectMeta.Name + virtualNetworkRG := instance.Spec.VNetResourceGroup + virtualnetworkname := instance.Spec.VNetName + subnetName := instance.Spec.SubnetName + ignoreendpoint := instance.Spec.IgnoreMissingServiceEndpoint + + vnetrule, err := vr.GetPostgreSQLVNetRule(ctx, groupName, server, ruleName) + if err == nil { + if vnetrule.VirtualNetworkRuleProperties != nil && vnetrule.VirtualNetworkRuleProperties.State == psql.Ready { + instance.Status.Provisioning = false + instance.Status.Provisioned = true + instance.Status.Message = resourcemanager.SuccessMsg + instance.Status.ResourceId = *vnetrule.ID + return true, nil + } + return false, nil + } + instance.Status.Message = fmt.Sprintf("PostgreSQLVNetRule Get error %s", err.Error()) + requeuErrors := []string{ + errhelp.ResourceGroupNotFoundErrorCode, + errhelp.ParentNotFoundErrorCode, + } + azerr := errhelp.NewAzureErrorAzureError(err) + if helpers.ContainsString(requeuErrors, azerr.Type) { + instance.Status.Provisioning = false + return false, nil + } + + instance.Status.Provisioning = true + _, err = vr.CreateOrUpdatePostgreSQLVNetRule(ctx, groupName, server, ruleName, virtualNetworkRG, virtualnetworkname, subnetName, ignoreendpoint) + if err != nil { + instance.Status.Message = err.Error() + azerr := errhelp.NewAzureErrorAzureError(err) + + if azerr.Type == errhelp.AsyncOpIncompleteError { + instance.Status.Provisioning = true + instance.Status.Message = "Resource request submitted to Azure successfully" + return false, nil + } + + ignorableErrors := []string{ + errhelp.ResourceGroupNotFoundErrorCode, + errhelp.ParentNotFoundErrorCode, + errhelp.ResourceNotFound, + errhelp.FeatureNotSupportedForEdition, + } + if helpers.ContainsString(ignorableErrors, azerr.Type) { + instance.Status.Provisioning = false + return false, nil + } + + // this happens when we try to create the VNet rule and the server doesnt exist yet + errorString := err.Error() + if strings.Contains(errorString, "does not have the server") { + instance.Status.Provisioning = false + return false, nil + } + + return false, err + } + + return false, nil // We requeue so the success can be caught in the Get() path +} + +// Delete Vnetrules +func (vr *PostgreSQLVNetRuleClient) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { + instance, err := vr.convert(obj) + if err != nil { + return false, err + } + + groupName := instance.Spec.ResourceGroup + server := instance.Spec.Server + ruleName := instance.ObjectMeta.Name + + err = vr.DeletePostgreSQLVNetRule(ctx, groupName, server, ruleName) + if err != nil { + instance.Status.Message = err.Error() + + azerr := errhelp.NewAzureErrorAzureError(err) + // these errors are expected + ignore := []string{ + errhelp.AsyncOpIncompleteError, + } + + // this means the thing doesn't exist + finished := []string{ + errhelp.ResourceNotFound, + errhelp.ParentNotFoundErrorCode, + } + + if helpers.ContainsString(ignore, azerr.Type) { + return true, nil //requeue + } + + if helpers.ContainsString(finished, azerr.Type) { + return false, nil //end reconcile + } + return false, err + } + + return false, nil +} + +// GetParents returns the parent of postgresqlvnetrule +func (vr *PostgreSQLVNetRuleClient) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { + instance, err := vr.convert(obj) + if err != nil { + return nil, err + } + + return []resourcemanager.KubeParent{ + { + Key: types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.Server, + }, + Target: &azurev1alpha1.PostgreSQLServer{}, + }, + { + Key: types.NamespacedName{ + Namespace: instance.Namespace, + Name: instance.Spec.ResourceGroup, + }, + Target: &azurev1alpha1.ResourceGroup{}, + }, + }, nil +} + +func (vr *PostgreSQLVNetRuleClient) GetStatus(obj runtime.Object) (*azurev1alpha1.ASOStatus, error) { + instance, err := vr.convert(obj) + if err != nil { + return nil, err + } + return &instance.Status, nil +} + +func (vr *PostgreSQLVNetRuleClient) convert(obj runtime.Object) (*azurev1alpha1.PostgreSQLVNetRule, error) { + local, ok := obj.(*azurev1alpha1.PostgreSQLVNetRule) + if !ok { + return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) + } + return local, nil +} From 98020e96ae617c7e77470d007ef6171b4636ddc2 Mon Sep 17 00:00:00 2001 From: Erin Corson Date: Mon, 27 Apr 2020 12:36:54 -0600 Subject: [PATCH 4/6] removing mock clients --- .../mock/appinsights/appinsights.go | 148 --------- .../mock/azuresql/azuresqldb.go | 164 ---------- .../mock/azuresql/azuresqlfailovergroup.go | 127 -------- .../mock/azuresql/azuresqlfirewallrule.go | 120 -------- .../mock/azuresql/azuresqlserver.go | 99 ------ .../mock/azuresql/azuresqluser.go | 124 -------- .../mock/eventhubs/consumergroup.go | 213 ------------- pkg/resourcemanager/mock/eventhubs/hub.go | 284 ------------------ .../mock/eventhubs/managers.go | 12 - .../mock/eventhubs/namespace.go | 235 --------------- pkg/resourcemanager/mock/helpers/helpers.go | 19 -- .../mock/keyvaults/keyvault.go | 164 ---------- .../mock/psql/postgresqldatabase.go | 136 --------- .../mock/psql/postgresqlfirewallrule.go | 140 --------- .../mock/psql/postgresqlserver.go | 159 ---------- .../mock/rediscaches/rediscaches.go | 125 -------- .../mock/resourcegroups/resourcegroup.go | 133 -------- .../mock/sqlclient/sqlclient_gosdk.go | 132 -------- .../mock/storages/blob_container.go | 95 ------ .../mock/storages/storageaccount.go | 104 ------- 20 files changed, 2733 deletions(-) delete mode 100644 pkg/resourcemanager/mock/appinsights/appinsights.go delete mode 100644 pkg/resourcemanager/mock/azuresql/azuresqldb.go delete mode 100644 pkg/resourcemanager/mock/azuresql/azuresqlfailovergroup.go delete mode 100644 pkg/resourcemanager/mock/azuresql/azuresqlfirewallrule.go delete mode 100644 pkg/resourcemanager/mock/azuresql/azuresqlserver.go delete mode 100644 pkg/resourcemanager/mock/azuresql/azuresqluser.go delete mode 100644 pkg/resourcemanager/mock/eventhubs/consumergroup.go delete mode 100644 pkg/resourcemanager/mock/eventhubs/hub.go delete mode 100644 pkg/resourcemanager/mock/eventhubs/managers.go delete mode 100644 pkg/resourcemanager/mock/eventhubs/namespace.go delete mode 100644 pkg/resourcemanager/mock/helpers/helpers.go delete mode 100644 pkg/resourcemanager/mock/keyvaults/keyvault.go delete mode 100644 pkg/resourcemanager/mock/psql/postgresqldatabase.go delete mode 100644 pkg/resourcemanager/mock/psql/postgresqlfirewallrule.go delete mode 100644 pkg/resourcemanager/mock/psql/postgresqlserver.go delete mode 100644 pkg/resourcemanager/mock/rediscaches/rediscaches.go delete mode 100644 pkg/resourcemanager/mock/resourcegroups/resourcegroup.go delete mode 100644 pkg/resourcemanager/mock/sqlclient/sqlclient_gosdk.go delete mode 100644 pkg/resourcemanager/mock/storages/blob_container.go delete mode 100644 pkg/resourcemanager/mock/storages/storageaccount.go diff --git a/pkg/resourcemanager/mock/appinsights/appinsights.go b/pkg/resourcemanager/mock/appinsights/appinsights.go deleted file mode 100644 index 6d1ce44cb02..00000000000 --- a/pkg/resourcemanager/mock/appinsights/appinsights.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package appinsights - -import ( - "context" - "errors" - "fmt" - "net/http" - - "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2015-05-01/insights" - "github.com/Azure/azure-service-operator/api/v1alpha1" - resourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" - "k8s.io/apimachinery/pkg/runtime" -) - -// MockAppInsightsManager mocks the implementation of an AppInsights Manager type -type MockAppInsightsManager struct { - Scheme *runtime.Scheme - resourceGroupName string - appinsights []insights.ApplicationInsightsComponent -} - -// NewMockAppInsightsManager creates a new MockAppInsightsManager -func NewMockAppInsightsManager(scheme *runtime.Scheme) *MockAppInsightsManager { - return &MockAppInsightsManager{ - Scheme: scheme, - appinsights: []insights.ApplicationInsightsComponent{}, - } -} - -// GetParents fetches the ARM hierarchy of resources for this operator -func (m *MockAppInsightsManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} - -// CreateAppInsights creates or updates a mock Application Insights service -func (m *MockAppInsightsManager) CreateAppInsights( - ctx context.Context, - resourceGroupName string, - kind string, - applicationType string, - location string, - resourceName string) (insights.ApplicationInsightsComponent, error) { - - index, _ := findAppInsight(m.appinsights, func(i insights.ApplicationInsightsComponent) bool { - return *i.Name == resourceName - }) - - insights := insights.ApplicationInsightsComponent{ - Kind: to.StringPtr(kind), - Location: to.StringPtr(location), - Name: to.StringPtr(resourceName), - } - - if index == -1 { - m.appinsights = append(m.appinsights, insights) - } - - return insights, nil -} - -// Delete removes the operator from desired state -func (m *MockAppInsightsManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := m.convert(obj) - if err != nil { - return false, err - } - - _, err = m.DeleteAppInsights(ctx, instance.Spec.ResourceGroup, instance.ObjectMeta.Name) - if err != nil { - return false, err - } - - return false, nil -} - -// DeleteAppInsights removes an Application Insights service from a subscription -func (m *MockAppInsightsManager) DeleteAppInsights(ctx context.Context, resourceGroupName string, resourceName string) (autorest.Response, error) { - appinsights := m.appinsights - - index, _ := findAppInsight(appinsights, func(i insights.ApplicationInsightsComponent) bool { - return *i.Name == resourceName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("appinsights instance not found") - } - - m.appinsights = append(appinsights[:index], appinsights[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -// GetAppInsights fetches an Application Insights service reference -func (m *MockAppInsightsManager) GetAppInsights(ctx context.Context, resourceGroupName string, resourceName string) (insights.ApplicationInsightsComponent, error) { - appinsights := m.appinsights - - index, appinsight := findAppInsight(appinsights, func(i insights.ApplicationInsightsComponent) bool { - return *i.Name == resourceName - }) - - if index == -1 { - return insights.ApplicationInsightsComponent{ - Response: helpers.GetRestResponse(http.StatusNotFound), - }, errors.New("appinsights instance not found") - } - - appinsight.Response = helpers.GetRestResponse(http.StatusOK) - return appinsight, nil -} - -// Ensure checks the desired state of the operator -func (m *MockAppInsightsManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - i, err := m.convert(obj) - if err != nil { - return true, err - } - - _, _ = m.CreateAppInsights(ctx, i.Spec.ResourceGroup, "web", "other", i.Spec.Location, i.Name) - - i.Status.Provisioned = true - - return true, nil -} - -func findAppInsight(res []insights.ApplicationInsightsComponent, predicate func(insights.ApplicationInsightsComponent) bool) (int, insights.ApplicationInsightsComponent) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, insights.ApplicationInsightsComponent{} -} - -func (m *MockAppInsightsManager) convert(obj runtime.Object) (*v1alpha1.AppInsights, error) { - i, ok := obj.(*v1alpha1.AppInsights) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return i, nil -} diff --git a/pkg/resourcemanager/mock/azuresql/azuresqldb.go b/pkg/resourcemanager/mock/azuresql/azuresqldb.go deleted file mode 100644 index 9e9403b2a5e..00000000000 --- a/pkg/resourcemanager/mock/azuresql/azuresqldb.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package azuresql - -import ( - "context" - "errors" - "fmt" - "net/http" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" - azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" - "github.com/Azure/azure-service-operator/pkg/resourcemanager" - azuresqlshared "github.com/Azure/azure-service-operator/pkg/resourcemanager/azuresql/azuresqlshared" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" -) - -type MockSqlDbManager struct { - resourceGroupName string - sqlDbs []MockSqlDbResource -} - -type MockSqlDbResource struct { - resourceGroupName string - sqlServerName string - sqlDb sql.Database -} - -func NewMockSqlDbManager() *MockSqlDbManager { - return &MockSqlDbManager{} -} - -func findSqlDb(res []MockSqlDbResource, predicate func(MockSqlDbResource) bool) (int, MockSqlDbResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, MockSqlDbResource{} -} - -//CreateorUpdateDB creates a sql Db -func (manager *MockSqlDbManager) CreateOrUpdateDB(ctx context.Context, resourceGroupName string, location string, serverName string, tags map[string]*string, properties azuresqlshared.SQLDatabaseProperties) (sql.DatabasesCreateOrUpdateFuture, error) { - index, _ := findSqlDb(manager.sqlDbs, func(s MockSqlDbResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlDb.Name == properties.DatabaseName - }) - - sqlD := sql.Database{ - Location: to.StringPtr(location), - Name: to.StringPtr(properties.DatabaseName), - } - - q := MockSqlDbResource{ - resourceGroupName: resourceGroupName, - sqlServerName: serverName, - sqlDb: sqlD, - } - - if index == -1 { - manager.sqlDbs = append(manager.sqlDbs, q) - } - - return sql.DatabasesCreateOrUpdateFuture{}, nil - -} - -func (manager *MockSqlDbManager) GetDB(ctx context.Context, resourceGroupName string, serverName string, databaseName string) (result sql.Database, err error) { - index, _ := findSqlDb(manager.sqlDbs, func(s MockSqlDbResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlDb.Name == databaseName - }) - - if index == -1 { - return sql.Database{}, errors.New("Sql Db Not Found") - } - - return manager.sqlDbs[index].sqlDb, nil -} - -//GetServer gets a sql server -func (manager *MockSqlDbManager) GetServer(ctx context.Context, resourceGroupName string, serverName string) (result sql.Server, err error) { - sqlManager := MockSqlServerManager{} - return sqlManager.GetServer(ctx, resourceGroupName, serverName) -} - -// DeleteDb removes the sql db -func (manager *MockSqlDbManager) DeleteDB(ctx context.Context, resourceGroupName string, serverName string, databaseName string) (result autorest.Response, err error) { - sqlDbs := manager.sqlDbs - - index, _ := findSqlDb(manager.sqlDbs, func(s MockSqlDbResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlDb.Name == databaseName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("Sql Db Not Found") - } - - manager.sqlDbs = append(sqlDbs[:index], sqlDbs[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -func (db *MockSqlDbManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := db.convert(obj) - if err != nil { - return false, err - } - - location := instance.Spec.Location - groupName := instance.Spec.ResourceGroup - server := instance.Spec.Server - dbName := instance.ObjectMeta.Name - dbEdition := instance.Spec.Edition - tags := map[string]*string{} - - azureSqlDatabaseProperties := azuresqlshared.SQLDatabaseProperties{ - DatabaseName: dbName, - Edition: dbEdition, - } - - _, err = db.CreateOrUpdateDB(ctx, groupName, location, server, tags, azureSqlDatabaseProperties) - if err != nil { - return false, err - } - - instance.Status.Provisioning = true - instance.Status.Provisioned = true - - return true, nil -} - -func (db *MockSqlDbManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := db.convert(obj) - if err != nil { - return false, err - } - - groupName := instance.Spec.ResourceGroup - server := instance.Spec.Server - dbName := instance.ObjectMeta.Name - - _, err = db.DeleteDB(ctx, groupName, server, dbName) - if err != nil { - return false, err - } - - return false, nil -} - -func (g *MockSqlDbManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return nil, nil -} - -func (*MockSqlDbManager) convert(obj runtime.Object) (*azurev1alpha1.AzureSqlDatabase, error) { - local, ok := obj.(*azurev1alpha1.AzureSqlDatabase) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} diff --git a/pkg/resourcemanager/mock/azuresql/azuresqlfailovergroup.go b/pkg/resourcemanager/mock/azuresql/azuresqlfailovergroup.go deleted file mode 100644 index a59f02837b2..00000000000 --- a/pkg/resourcemanager/mock/azuresql/azuresqlfailovergroup.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package azuresql - -import ( - "context" - "errors" - "fmt" - "net/http" - - "k8s.io/apimachinery/pkg/runtime" - - "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" - azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" - "github.com/Azure/azure-service-operator/pkg/resourcemanager" - azuresqlshared "github.com/Azure/azure-service-operator/pkg/resourcemanager/azuresql/azuresqlshared" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" -) - -type MockSqlFailoverGroupManager struct { - sqlFailoverGroups []MockSqlFailoverResource -} - -type MockSqlFailoverResource struct { - resourceGroupName string - sqlServerName string - sqlFailover sql.FailoverGroup -} - -func NewMockSqlFailoverGroupManager() *MockSqlFailoverGroupManager { - return &MockSqlFailoverGroupManager{} -} - -func findSqlFailoverGroup(res []MockSqlFailoverResource, predicate func(MockSqlFailoverResource) bool) (int, MockSqlFailoverResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, MockSqlFailoverResource{} -} - -func (manager *MockSqlFailoverGroupManager) CreateOrUpdateFailoverGroup(ctx context.Context, resourceGroupName string, serverName string, failovergroupname string, properties azuresqlshared.SQLFailoverGroupProperties) (result sql.FailoverGroupsCreateOrUpdateFuture, err error) { - index, _ := findSqlFailoverGroup(manager.sqlFailoverGroups, func(s MockSqlFailoverResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlFailover.Name == failovergroupname - }) - - sqlFG := sql.FailoverGroup{ - Name: to.StringPtr(failovergroupname), - } - - q := MockSqlFailoverResource{ - resourceGroupName: resourceGroupName, - sqlServerName: serverName, - sqlFailover: sqlFG, - } - - if index == -1 { - manager.sqlFailoverGroups = append(manager.sqlFailoverGroups, q) - } - - return sql.FailoverGroupsCreateOrUpdateFuture{}, nil -} - -//DeleteFailoverGroup deletes a failover group -func (manager *MockSqlFailoverGroupManager) DeleteFailoverGroup(ctx context.Context, resourceGroupName string, serverName string, failoverGroupName string) (result autorest.Response, err error) { - sqlFailoverGroups := manager.sqlFailoverGroups - - index, _ := findSqlFailoverGroup(manager.sqlFailoverGroups, func(s MockSqlFailoverResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlFailover.Name == failoverGroupName - }) - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("Sql Firewall Rule Found") - } - - manager.sqlFailoverGroups = append(sqlFailoverGroups[:index], sqlFailoverGroups[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -//GetFailoverGroup gets a failover group -func (manager *MockSqlFailoverGroupManager) GetFailoverGroup(ctx context.Context, resourceGroupName string, serverName string, failovergroupname string) (result sql.FailoverGroup, err error) { - - index, _ := findSqlFailoverGroup(manager.sqlFailoverGroups, func(s MockSqlFailoverResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlFailover.Name == failovergroupname - }) - if index == -1 { - return sql.FailoverGroup{}, errors.New("Sql FailoverGroup Not Found") - } - - return manager.sqlFailoverGroups[index].sqlFailover, nil -} - -//GetServer gets a sql server -func (manager *MockSqlFailoverGroupManager) GetServer(ctx context.Context, resourceGroupName string, serverName string) (result sql.Server, err error) { - sqlManager := MockSqlServerManager{} - return sqlManager.GetServer(ctx, resourceGroupName, serverName) -} - -//GetDb gets a sql db server -func (manager *MockSqlFailoverGroupManager) GetDB(ctx context.Context, resourceGroupName string, serverName string, databaseName string) (result sql.Database, err error) { - sqlManager := MockSqlDbManager{} - return sqlManager.GetDB(ctx, resourceGroupName, serverName, databaseName) -} - -func (fg *MockSqlFailoverGroupManager) Ensure(ctx context.Context, obj runtime.Object) (bool, error) { - return true, nil -} - -func (fg *MockSqlFailoverGroupManager) Delete(ctx context.Context, obj runtime.Object) (bool, error) { - return true, nil -} - -func (g *MockSqlFailoverGroupManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} - -func (*MockSqlFailoverGroupManager) convert(obj runtime.Object) (*azurev1alpha1.AzureSqlFailoverGroup, error) { - local, ok := obj.(*azurev1alpha1.AzureSqlFailoverGroup) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} diff --git a/pkg/resourcemanager/mock/azuresql/azuresqlfirewallrule.go b/pkg/resourcemanager/mock/azuresql/azuresqlfirewallrule.go deleted file mode 100644 index ab6c35b62a0..00000000000 --- a/pkg/resourcemanager/mock/azuresql/azuresqlfirewallrule.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package azuresql - -import ( - "context" - "errors" - "fmt" - - "github.com/Azure/go-autorest/autorest/to" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" - azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" - "github.com/Azure/azure-service-operator/pkg/resourcemanager" -) - -type MockSqlFirewallRuleManager struct { - sqlFirewallRules []MockSqlFirewallRuleResource -} - -type MockSqlFirewallRuleResource struct { - resourceGroupName string - sqlServerName string - sqlFirewallRule sql.FirewallRule -} - -func NewMockSqlFirewallRuleManager() *MockSqlFirewallRuleManager { - return &MockSqlFirewallRuleManager{} -} - -func findSqlFirewallRule(res []MockSqlFirewallRuleResource, predicate func(MockSqlFirewallRuleResource) bool) (int, MockSqlFirewallRuleResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, MockSqlFirewallRuleResource{} -} - -// CreateOrUpdateSQLFirewallRule creates a sql firewall rule -func (manager *MockSqlFirewallRuleManager) CreateOrUpdateSQLFirewallRule(ctx context.Context, resourceGroupName string, serverName string, ruleName string, startIP string, endIP string) (result bool, err error) { - index, _ := findSqlFirewallRule(manager.sqlFirewallRules, func(s MockSqlFirewallRuleResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlFirewallRule.Name == ruleName - }) - - sqlFR := sql.FirewallRule{ - Name: to.StringPtr(ruleName), - } - - q := MockSqlFirewallRuleResource{ - resourceGroupName: resourceGroupName, - sqlServerName: serverName, - sqlFirewallRule: sqlFR, - } - - if index == -1 { - manager.sqlFirewallRules = append(manager.sqlFirewallRules, q) - } - - return true, nil -} - -// GetSQLFirewallRule gets a sql firewall rule -func (manager *MockSqlFirewallRuleManager) GetSQLFirewallRule(ctx context.Context, resourceGroupName string, serverName string, ruleName string) (result sql.FirewallRule, err error) { - index, _ := findSqlFirewallRule(manager.sqlFirewallRules, func(s MockSqlFirewallRuleResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlFirewallRule.Name == ruleName - }) - - if index == -1 { - return sql.FirewallRule{}, errors.New("Sql Firewall Rule Not Found") - } - - return manager.sqlFirewallRules[index].sqlFirewallRule, nil -} - -//GetServer gets a sql server -func (manager *MockSqlFirewallRuleManager) GetServer(ctx context.Context, resourceGroupName string, serverName string) (result sql.Server, err error) { - sqlManager := MockSqlServerManager{} - return sqlManager.GetServer(ctx, resourceGroupName, serverName) -} - -// DeleteSQLFirewallRule deletes a sql firewall rule -func (manager *MockSqlFirewallRuleManager) DeleteSQLFirewallRule(ctx context.Context, resourceGroupName string, serverName string, ruleName string) (err error) { - - sqlFirewallRules := manager.sqlFirewallRules - - index, _ := findSqlFirewallRule(manager.sqlFirewallRules, func(s MockSqlFirewallRuleResource) bool { - return s.resourceGroupName == resourceGroupName && s.sqlServerName == serverName && *s.sqlFirewallRule.Name == ruleName - }) - - if index == -1 { - return errors.New("Sql Firewall Rule Found") - } - - manager.sqlFirewallRules = append(sqlFirewallRules[:index], sqlFirewallRules[index+1:]...) - - return nil -} - -func (fw *MockSqlFirewallRuleManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - return true, nil -} - -func (fw *MockSqlFirewallRuleManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - return true, nil -} - -func (g *MockSqlFirewallRuleManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} - -func (*MockSqlFirewallRuleManager) convert(obj runtime.Object) (*azurev1alpha1.AzureSqlFirewallRule, error) { - local, ok := obj.(*azurev1alpha1.AzureSqlFirewallRule) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} diff --git a/pkg/resourcemanager/mock/azuresql/azuresqlserver.go b/pkg/resourcemanager/mock/azuresql/azuresqlserver.go deleted file mode 100644 index 436b75b3de3..00000000000 --- a/pkg/resourcemanager/mock/azuresql/azuresqlserver.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package azuresql - -import ( - "context" - "errors" - "net/http" - - "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" - azuresqlshared "github.com/Azure/azure-service-operator/pkg/resourcemanager/azuresql/azuresqlshared" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest/to" - - "github.com/Azure/go-autorest/autorest" -) - -type MockSqlServerManager struct { - resourceGroupName string - sqlServers []MockSqlServerResource -} - -type MockSqlServerResource struct { - resourceGroupName string - sqlServer sql.Server -} - -func NewMockSqlServerManager() *MockSqlServerManager { - return &MockSqlServerManager{} -} - -func findSqlServer(res []MockSqlServerResource, predicate func(MockSqlServerResource) bool) (int, error) { - for index, r := range res { - if predicate(r) { - return index, nil - } - } - return -1, errors.New("not found") -} - -// CreateOrUpdateSqlServer creates a new sql server -func (manager *MockSqlServerManager) CreateOrUpdateSQLServer(ctx context.Context, resourceGroupName string, location string, serverName string, tags map[string]*string, properties azuresqlshared.SQLServerProperties, forceUpdate bool) (result sql.Server, err error) { - index, _ := findSqlServer(manager.sqlServers, func(s MockSqlServerResource) bool { - return s.resourceGroupName == resourceGroupName && *s.sqlServer.Name == serverName - }) - - sqlS := sql.Server{ - Response: helpers.GetRestResponse(http.StatusCreated), - Location: to.StringPtr(location), - Name: to.StringPtr(serverName), - } - - q := MockSqlServerResource{ - resourceGroupName: resourceGroupName, - sqlServer: sqlS, - } - - if index == -1 { - manager.sqlServers = append(manager.sqlServers, q) - } - - return q.sqlServer, nil -} - -// DeleteSQLServer removes the sqlserver -func (manager *MockSqlServerManager) DeleteSQLServer(ctx context.Context, resourceGroupName string, serverName string) (result autorest.Response, err error) { - sqlServers := manager.sqlServers - - index, _ := findSqlServer(sqlServers, func(s MockSqlServerResource) bool { - return s.resourceGroupName == resourceGroupName && *s.sqlServer.Name == serverName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("Sql Server Not Found") - } - - manager.sqlServers = append(sqlServers[:index], sqlServers[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -// GetServer gets a sql server -func (manager *MockSqlServerManager) GetServer(ctx context.Context, resourceGroupName string, serverName string) (result sql.Server, err error) { - index, _ := findSqlServer(manager.sqlServers, func(s MockSqlServerResource) bool { - return s.resourceGroupName == resourceGroupName && *s.sqlServer.Name == serverName - }) - - if index == -1 { - return sql.Server{}, errors.New("Sql Server Not Found") - } - - state := "Ready" - serverProperties := sql.ServerProperties{State: &state} - server := manager.sqlServers[index].sqlServer - server.ServerProperties = &serverProperties - - return server, nil -} diff --git a/pkg/resourcemanager/mock/azuresql/azuresqluser.go b/pkg/resourcemanager/mock/azuresql/azuresqluser.go deleted file mode 100644 index 84487197986..00000000000 --- a/pkg/resourcemanager/mock/azuresql/azuresqluser.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package azuresql - -import ( - "context" - "errors" - "fmt" - - "database/sql" - dbsql "database/sql" - - "github.com/Azure/azure-service-operator/api/v1alpha1" - "github.com/Azure/azure-service-operator/pkg/resourcemanager" - azuresqluser "github.com/Azure/azure-service-operator/pkg/resourcemanager/azuresql/azuresqluser" - "k8s.io/apimachinery/pkg/runtime" -) - -type MockSqlUserManager struct { - sqlUsers []MockSqlUserResource -} - -type MockSqlUserResource struct { - username string - roles []string - sqlDB *dbsql.DB -} - -func NewMockAzureSqlUserManager() *MockSqlUserManager { - return &MockSqlUserManager{} -} - -func findSqlUser(res []MockSqlUserResource, predicate func(MockSqlUserResource) bool) (int, MockSqlUserResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, MockSqlUserResource{} -} - -func (manager *MockSqlUserManager) ConnectToSqlDb(ctx context.Context, drivername string, server string, dbname string, port int, username string, password string) (*sql.DB, error) { - db := &sql.DB{} - return db, nil -} - -func (manager *MockSqlUserManager) CreateUser(ctx context.Context, secret map[string][]byte, db *dbsql.DB) (string, error) { - newUser := string(secret[azuresqluser.SecretUsernameKey]) - - q := MockSqlUserResource{ - username: newUser, - roles: []string{}, - sqlDB: db, - } - - manager.sqlUsers = append(manager.sqlUsers, q) - - return newUser, nil -} - -func (manager *MockSqlUserManager) DropUser(ctx context.Context, db *sql.DB, user string) error { - sqlUsers := manager.sqlUsers - - index, _ := findSqlUser(manager.sqlUsers, func(s MockSqlUserResource) bool { - return s.username == user - }) - - if index == -1 { - return errors.New("Sql User Not Found") - } - - manager.sqlUsers = append(sqlUsers[:index], sqlUsers[index+1:]...) - - return nil -} - -func (manager *MockSqlUserManager) UserExists(ctx context.Context, db *sql.DB, username string) (bool, error) { - - index, _ := findSqlUser(manager.sqlUsers, func(s MockSqlUserResource) bool { - return s.username == username - }) - - if index == -1 { - return false, errors.New("Sql User Not Found") - } - - return true, nil -} - -func (manager *MockSqlUserManager) GrantUserRoles(ctx context.Context, user string, roles []string, db *sql.DB) error { - - index, _ := findSqlUser(manager.sqlUsers, func(s MockSqlUserResource) bool { - return s.username == user - }) - - if index == -1 { - return errors.New("Sql User Not Found") - } - - manager.sqlUsers[index].roles = roles - - return nil -} - -func (s *MockSqlUserManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - return true, nil -} - -func (s *MockSqlUserManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - return true, nil -} - -func (s *MockSqlUserManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} - -func (s *MockSqlUserManager) convert(obj runtime.Object) (*v1alpha1.AzureSQLUser, error) { - local, ok := obj.(*v1alpha1.AzureSQLUser) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} diff --git a/pkg/resourcemanager/mock/eventhubs/consumergroup.go b/pkg/resourcemanager/mock/eventhubs/consumergroup.go deleted file mode 100644 index a77ba263888..00000000000 --- a/pkg/resourcemanager/mock/eventhubs/consumergroup.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package eventhubs - -import ( - "context" - "errors" - "fmt" - "net/http" - - "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" - "github.com/Azure/azure-service-operator/api/v1alpha1" - "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" -) - -type consumerGroupResource struct { - resourceGroupName string - namespaceName string - eventHubName string - consumerGroupName string - ConsumerGroup eventhub.ConsumerGroup -} - -type mockConsumerGroupManager struct { - consumerGroupResources []consumerGroupResource -} - -func NewMockConsumerGroupClient() *mockConsumerGroupManager { - return &mockConsumerGroupManager{} -} - -func findConsumerGroup(res []consumerGroupResource, predicate func(consumerGroupResource) bool) (int, consumerGroupResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, consumerGroupResource{} -} - -func find(res []interface{}, predicate func(interface{}) bool) (int, interface{}) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, consumerGroupResource{} -} - -func (manager *mockConsumerGroupManager) CreateConsumerGroup(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, consumerGroupName string) (eventhub.ConsumerGroup, error) { - var consumerGroup = eventhub.ConsumerGroup{ - Response: helpers.GetRestResponse(201), - Name: to.StringPtr(consumerGroupName), - } - manager.consumerGroupResources = append(manager.consumerGroupResources, consumerGroupResource{ - resourceGroupName: resourceGroupName, - namespaceName: namespaceName, - eventHubName: eventHubName, - consumerGroupName: consumerGroupName, - ConsumerGroup: consumerGroup, - }) - return consumerGroup, nil -} - -func (manager *mockConsumerGroupManager) DeleteConsumerGroup(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, consumerGroupName string) (autorest.Response, error) { - groups := manager.consumerGroupResources - - index, _ := findConsumerGroup(groups, func(g consumerGroupResource) bool { - return g.resourceGroupName == resourceGroupName && - g.namespaceName == namespaceName && - g.eventHubName == eventHubName && - g.consumerGroupName == consumerGroupName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("consumer group not found") - } - - manager.consumerGroupResources = append(groups[:index], groups[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -func (manager *mockConsumerGroupManager) GetConsumerGroup(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, consumerGroupName string) (eventhub.ConsumerGroup, error) { - groups := manager.consumerGroupResources - - index, group := findConsumerGroup(groups, func(g consumerGroupResource) bool { - return g.resourceGroupName == resourceGroupName && - g.namespaceName == namespaceName && - g.eventHubName == eventHubName && - g.consumerGroupName == consumerGroupName - }) - - if index == -1 { - return eventhub.ConsumerGroup{ - Response: helpers.GetRestResponse(http.StatusNotFound), - }, errors.New("consumer group not found") - } - - group.ConsumerGroup.Response = helpers.GetRestResponse(http.StatusOK) - return group.ConsumerGroup, nil -} - -func (cg *mockConsumerGroupManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := cg.convert(obj) - if err != nil { - return false, err - } - - // write information back to instance - instance.Status.Provisioning = true - - kubeObjectName := instance.Name - namespaceName := instance.Spec.Namespace - resourcegroup := instance.Spec.ResourceGroup - eventhubName := instance.Spec.Eventhub - azureConsumerGroupName := instance.Spec.ConsumerGroupName - - // if no need for shared consumer group name, use the kube name - if len(azureConsumerGroupName) == 0 { - azureConsumerGroupName = kubeObjectName - } - - resp, err := cg.CreateConsumerGroup(ctx, resourcegroup, namespaceName, eventhubName, azureConsumerGroupName) - if err != nil { - instance.Status.Message = err.Error() - instance.Status.Provisioning = false - return false, err - } - instance.Status.State = resp.Status - instance.Status.Message = "success" - // write information back to instance - instance.Status.Provisioning = false - instance.Status.Provisioned = true - - return true, nil -} - -func (cg *mockConsumerGroupManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := cg.convert(obj) - if err != nil { - return false, err - } - - kubeObjectName := instance.Name - namespaceName := instance.Spec.Namespace - resourcegroup := instance.Spec.ResourceGroup - eventhubName := instance.Spec.Eventhub - azureConsumerGroupName := instance.Spec.ConsumerGroupName - - // if no need for shared consumer group name, use the kube name - if len(azureConsumerGroupName) == 0 { - azureConsumerGroupName = kubeObjectName - } - - _, err = cg.DeleteConsumerGroup(ctx, resourcegroup, namespaceName, eventhubName, azureConsumerGroupName) - if err != nil { - return false, err - } - - instance.Status.Message = "deleted" - - return true, nil -} - -func (cg *mockConsumerGroupManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - - instance, err := cg.convert(obj) - if err != nil { - return nil, err - } - - return []resourcemanager.KubeParent{ - { - Key: types.NamespacedName{ - Namespace: instance.Namespace, - Name: instance.Spec.Namespace, - }, - Target: &v1alpha1.EventhubNamespace{}, - }, - { - Key: types.NamespacedName{ - Namespace: instance.Namespace, - Name: instance.Spec.ResourceGroup, - }, - Target: &v1alpha1.ResourceGroup{}, - }, - }, nil -} - -func (g *mockConsumerGroupManager) GetStatus(obj runtime.Object) (*v1alpha1.ASOStatus, error) { - instance, err := g.convert(obj) - if err != nil { - return nil, err - } - return &instance.Status, nil -} - -func (cg *mockConsumerGroupManager) convert(obj runtime.Object) (*v1alpha1.ConsumerGroup, error) { - local, ok := obj.(*v1alpha1.ConsumerGroup) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} diff --git a/pkg/resourcemanager/mock/eventhubs/hub.go b/pkg/resourcemanager/mock/eventhubs/hub.go deleted file mode 100644 index 75b711679d1..00000000000 --- a/pkg/resourcemanager/mock/eventhubs/hub.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package eventhubs - -import ( - "context" - "errors" - "fmt" - "net/http" - - "github.com/Azure/azure-service-operator/api/v1alpha1" - azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" - - "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" - pkghelpers "github.com/Azure/azure-service-operator/pkg/helpers" - "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/azure-service-operator/pkg/secrets" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" - "k8s.io/apimachinery/pkg/runtime" -) - -type eventHubAccess struct { - rule eventhub.AuthorizationRule - keys eventhub.AccessKeys -} - -type eventHubResource struct { - resourceGroupName string - namespaceName string - eventHubName string - eventHub eventhub.Model - eventHubAccesses []eventHubAccess -} - -func findEventHub(res []eventHubResource, predicate func(eventHubResource) bool) (int, eventHubResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, eventHubResource{} -} - -func findAccess(res []eventHubAccess, name string) (int, eventHubAccess) { - for index, r := range res { - if *r.rule.Name == name { - return index, r - } - } - return -1, eventHubAccess{} -} - -type mockEventHubManager struct { - eventHubResources []eventHubResource - SecretClient secrets.SecretClient - Scheme *runtime.Scheme -} - -func NewMockEventHubClient(secretClient secrets.SecretClient, scheme *runtime.Scheme) *mockEventHubManager { - return &mockEventHubManager{ - SecretClient: secretClient, - Scheme: scheme, - eventHubResources: []eventHubResource{}, - } -} - -func (manager *mockEventHubManager) DeleteHub(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string) (result autorest.Response, err error) { - hubs := manager.eventHubResources - - index, _ := findEventHub(hubs, func(g eventHubResource) bool { - return g.resourceGroupName == resourceGroupName && - g.namespaceName == namespaceName && - g.eventHubName == eventHubName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("eventhub not found") - } - - manager.eventHubResources = append(hubs[:index], hubs[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -func (manager *mockEventHubManager) CreateHub(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, messageRetentionInDays int32, partitionCount int32, captureDescription *eventhub.CaptureDescription) (eventhub.Model, error) { - - var eventHub = eventhub.Model{ - Response: helpers.GetRestResponse(201), - Properties: &eventhub.Properties{ - MessageRetentionInDays: to.Int64Ptr(int64(messageRetentionInDays)), - PartitionCount: to.Int64Ptr(int64(partitionCount)), - Status: "", - CaptureDescription: captureDescription, - }, - Name: &eventHubName, - } - manager.eventHubResources = append(manager.eventHubResources, eventHubResource{ - resourceGroupName: resourceGroupName, - namespaceName: namespaceName, - eventHubName: eventHubName, - eventHub: eventHub, - eventHubAccesses: []eventHubAccess{}, - }) - - return eventHub, nil -} - -func (manager *mockEventHubManager) GetHub(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string) (eventhub.Model, error) { - hubs := manager.eventHubResources - - index, hub := findEventHub(hubs, func(g eventHubResource) bool { - return g.resourceGroupName == resourceGroupName && - g.namespaceName == namespaceName && - g.eventHubName == eventHubName - }) - - if index == -1 { - return eventhub.Model{}, errors.New("eventhub not found") - } - - return hub.eventHub, nil -} - -func (manager *mockEventHubManager) getHubAccess(resourceGroupName string, namespaceName string, eventHubName string, authorizationRuleName string) (eventHubResource, int, eventHubAccess, error) { - hubs := manager.eventHubResources - hubIndex, hub := findEventHub(hubs, func(g eventHubResource) bool { - return g.resourceGroupName == resourceGroupName && - g.namespaceName == namespaceName && - g.eventHubName == eventHubName - }) - if hubIndex == -1 { - return eventHubResource{}, 0, eventHubAccess{}, errors.New("eventhub not found") - } - authRules := hub.eventHubAccesses - ruleIndex, rule := findAccess(authRules, authorizationRuleName) - - return hub, ruleIndex, rule, nil -} - -func (manager *mockEventHubManager) CreateOrUpdateAuthorizationRule(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, authorizationRuleName string, parameters eventhub.AuthorizationRule) (eventhub.AuthorizationRule, error) { - hub, accessIndex, _, err := manager.getHubAccess(resourceGroupName, namespaceName, eventHubName, authorizationRuleName) - if err != nil { - return eventhub.AuthorizationRule{}, err - } - - if accessIndex == -1 { - hub.eventHubAccesses = append(hub.eventHubAccesses, eventHubAccess{ - rule: parameters, - keys: eventhub.AccessKeys{ - Response: helpers.GetRestResponse(http.StatusOK), - PrimaryConnectionString: to.StringPtr(pkghelpers.RandomString(40)), - SecondaryConnectionString: to.StringPtr(pkghelpers.RandomString(40)), - AliasPrimaryConnectionString: to.StringPtr(pkghelpers.RandomString(40)), - AliasSecondaryConnectionString: to.StringPtr(pkghelpers.RandomString(40)), - PrimaryKey: to.StringPtr(pkghelpers.RandomString(15)), - SecondaryKey: to.StringPtr(pkghelpers.RandomString(15)), - KeyName: to.StringPtr(pkghelpers.RandomString(10)), - }, - }) - } else { - hub.eventHubAccesses[accessIndex].rule = parameters - } - - return parameters, nil -} - -func (manager *mockEventHubManager) ListKeys(ctx context.Context, resourceGroupName string, namespaceName string, eventHubName string, authorizationRuleName string) (eventhub.AccessKeys, error) { - _, accessIndex, access, err := manager.getHubAccess(resourceGroupName, namespaceName, eventHubName, authorizationRuleName) - - if err != nil { - return eventhub.AccessKeys{}, err - } - - if accessIndex == -1 { - return eventhub.AccessKeys{}, errors.New("eventhub access rule not found") - } - - return access.keys, nil -} - -func (e *mockEventHubManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := e.convert(obj) - if err != nil { - return false, err - } - - eventhubName := instance.ObjectMeta.Name - eventhubNamespace := instance.Spec.Namespace - resourcegroup := instance.Spec.ResourceGroup - partitionCount := instance.Spec.Properties.PartitionCount - messageRetentionInDays := instance.Spec.Properties.MessageRetentionInDays - secretName := instance.Spec.SecretName - captureDescription := instance.Spec.Properties.CaptureDescription - - if len(secretName) == 0 { - secretName = eventhubName - instance.Spec.SecretName = eventhubName - } - - // write information back to instance - instance.Status.Provisioning = true - - capturePtr := getCaptureDescriptionPtr(captureDescription) - - _, err = e.CreateHub(ctx, resourcegroup, eventhubNamespace, eventhubName, messageRetentionInDays, partitionCount, capturePtr) - if err != nil { - instance.Status.Provisioning = false - return false, err - } - - // write information back to instance - instance.Status.Provisioning = false - instance.Status.Provisioned = true - - return true, nil -} - -func (e *mockEventHubManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := e.convert(obj) - if err != nil { - return false, err - } - - eventhubName := instance.ObjectMeta.Name - eventhubNamespace := instance.Spec.Namespace - resourcegroup := instance.Spec.ResourceGroup - _, err = e.DeleteHub(ctx, resourcegroup, eventhubNamespace, eventhubName) - - return true, err -} - -func (e *mockEventHubManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} - -func (g *mockEventHubManager) GetStatus(obj runtime.Object) (*v1alpha1.ASOStatus, error) { - instance, err := g.convert(obj) - if err != nil { - return nil, err - } - return &instance.Status, nil -} - -func (e *mockEventHubManager) convert(obj runtime.Object) (*azurev1alpha1.Eventhub, error) { - local, ok := obj.(*azurev1alpha1.Eventhub) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} - -const storageAccountResourceFmt = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/%s" - -func getCaptureDescriptionPtr(captureDescription azurev1alpha1.CaptureDescription) *eventhub.CaptureDescription { - // add capture details - var capturePtr *eventhub.CaptureDescription - - storage := captureDescription.Destination.StorageAccount - storageAccountResourceID := fmt.Sprintf(storageAccountResourceFmt, config.SubscriptionID(), storage.ResourceGroup, storage.AccountName) - - if captureDescription.Enabled { - capturePtr = &eventhub.CaptureDescription{ - Enabled: to.BoolPtr(true), - Encoding: eventhub.Avro, - IntervalInSeconds: &captureDescription.IntervalInSeconds, - SizeLimitInBytes: &captureDescription.SizeLimitInBytes, - Destination: &eventhub.Destination{ - Name: &captureDescription.Destination.Name, - DestinationProperties: &eventhub.DestinationProperties{ - StorageAccountResourceID: &storageAccountResourceID, - BlobContainer: &captureDescription.Destination.BlobContainer, - ArchiveNameFormat: &captureDescription.Destination.ArchiveNameFormat, - }, - }, - SkipEmptyArchives: to.BoolPtr(true), - } - } - return capturePtr -} diff --git a/pkg/resourcemanager/mock/eventhubs/managers.go b/pkg/resourcemanager/mock/eventhubs/managers.go deleted file mode 100644 index fd4414211b8..00000000000 --- a/pkg/resourcemanager/mock/eventhubs/managers.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package eventhubs - -import . "github.com/Azure/azure-service-operator/pkg/resourcemanager/eventhubs" - -var MockEventHubManagers = EventHubManagers{ - EventHubNamespace: &mockEventHubNamespaceManager{}, - EventHub: &mockEventHubManager{}, - ConsumerGroup: &mockConsumerGroupManager{}, -} diff --git a/pkg/resourcemanager/mock/eventhubs/namespace.go b/pkg/resourcemanager/mock/eventhubs/namespace.go deleted file mode 100644 index ee835018d83..00000000000 --- a/pkg/resourcemanager/mock/eventhubs/namespace.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package eventhubs - -import ( - "context" - "errors" - "fmt" - "net/http" - - "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub" - "github.com/Azure/azure-service-operator/api/v1alpha1" - "github.com/Azure/azure-service-operator/pkg/errhelp" - apphelpers "github.com/Azure/azure-service-operator/pkg/helpers" - "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" -) - -type eventHubNamespaceResource struct { - resourceGroupName string - namespaceName string - eHNamespace eventhub.EHNamespace -} - -type mockEventHubNamespaceManager struct { - eventHubNamespaceResources []eventHubNamespaceResource -} - -func NewMockEventHubNamespaceClient() *mockEventHubNamespaceManager { - return &mockEventHubNamespaceManager{} -} - -func findEventHubNamespace(res []eventHubNamespaceResource, predicate func(eventHubNamespaceResource) bool) (int, eventHubNamespaceResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, eventHubNamespaceResource{} -} - -func (manager *mockEventHubNamespaceManager) CreateNamespaceAndWait(ctx context.Context, resourceGroupName string, namespaceName string, location string) (*eventhub.EHNamespace, error) { - var eventHubNamespace = eventhub.EHNamespace{ - Response: helpers.GetRestResponse(201), - EHNamespaceProperties: &eventhub.EHNamespaceProperties{ - ProvisioningState: to.StringPtr("Succeeded"), - }, - Location: &location, - Name: to.StringPtr(namespaceName), - } - manager.eventHubNamespaceResources = append(manager.eventHubNamespaceResources, eventHubNamespaceResource{ - resourceGroupName: resourceGroupName, - namespaceName: namespaceName, - eHNamespace: eventHubNamespace, - }) - return &eventHubNamespace, nil -} - -func (manager *mockEventHubNamespaceManager) CreateNamespace(ctx context.Context, resourceGroupName string, namespaceName string, location string) (*eventhub.EHNamespace, error) { - var eventHubNamespace = eventhub.EHNamespace{ - Response: helpers.GetRestResponse(201), - EHNamespaceProperties: &eventhub.EHNamespaceProperties{ - ProvisioningState: to.StringPtr("Succeeded"), - }, - Location: &location, - Name: to.StringPtr(namespaceName), - } - manager.eventHubNamespaceResources = append(manager.eventHubNamespaceResources, eventHubNamespaceResource{ - resourceGroupName: resourceGroupName, - namespaceName: namespaceName, - eHNamespace: eventHubNamespace, - }) - return &eventHubNamespace, nil -} - -func (manager *mockEventHubNamespaceManager) DeleteNamespace(ctx context.Context, resourceGroupName string, namespaceName string) (autorest.Response, error) { - namespaces := manager.eventHubNamespaceResources - - index, _ := findEventHubNamespace(namespaces, func(g eventHubNamespaceResource) bool { - return g.resourceGroupName == resourceGroupName && - g.namespaceName == namespaceName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("eventhub namespace not found") - } - - manager.eventHubNamespaceResources = append(namespaces[:index], namespaces[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -func (manager *mockEventHubNamespaceManager) GetNamespace(ctx context.Context, resourceGroupName string, namespaceName string) (*eventhub.EHNamespace, error) { - groups := manager.eventHubNamespaceResources - - index, group := findEventHubNamespace(groups, func(g eventHubNamespaceResource) bool { - return g.resourceGroupName == resourceGroupName && - g.namespaceName == namespaceName - }) - - if index == -1 { - return &eventhub.EHNamespace{}, errors.New("eventhub namespace not found") - } - - return &group.eHNamespace, nil -} - -func (ns *mockEventHubNamespaceManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := ns.convert(obj) - if err != nil { - return false, err - } - - namespaceLocation := instance.Spec.Location - namespaceName := instance.Name - resourcegroup := instance.Spec.ResourceGroup - - // write information back to instance - instance.Status.Provisioning = true - - // @todo handle updates - _, err = ns.GetNamespace(ctx, resourcegroup, namespaceName) - if err == nil { - instance.Status.Provisioning = false - instance.Status.Provisioned = true - - return true, nil - } - - // set this message so the tests are happy - if instance.Spec.ResourceGroup == "gone" { - instance.Status.Message = "ResourceGroupNotFound" - } - - // create Event Hubs namespace - _, err = ns.CreateNamespace(ctx, resourcegroup, namespaceName, namespaceLocation) - if err != nil { - catch := []string{ - errhelp.ResourceGroupNotFoundErrorCode, - errhelp.AsyncOpIncompleteError, - } - azerr := errhelp.NewAzureErrorAzureError(err) - if apphelpers.ContainsString(catch, azerr.Type) { - instance.Status.Message = err.Error() - return false, nil - } - - instance.Status.Provisioning = false - - return true, fmt.Errorf("EventhubNamespace create error %v", err) - - } - // write information back to instance - instance.Status.Provisioning = false - instance.Status.Provisioned = true - - return true, nil -} - -func (ns *mockEventHubNamespaceManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := ns.convert(obj) - if err != nil { - return false, err - } - - namespaceName := instance.Name - resourcegroup := instance.Spec.ResourceGroup - - _, err = ns.DeleteNamespace(ctx, resourcegroup, namespaceName) - if err != nil { - azerr := errhelp.NewAzureErrorAzureError(err) - - // check if async op from previous delete was still happening - catch := []string{ - errhelp.AsyncOpIncompleteError, - errhelp.RequestConflictError, - } - if apphelpers.ContainsString(catch, azerr.Type) { - return true, nil - } - - // check if namespace was already gone - catch = []string{ - errhelp.NotFoundErrorCode, - } - if apphelpers.ContainsString(catch, azerr.Type) { - return false, nil - } - - // some error we don't know about or can't handle happened - instance.Status.Provisioning = false - - return true, fmt.Errorf("EventhubNamespace delete error %v", err) - - } - - return false, nil -} - -func (ns *mockEventHubNamespaceManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - - instance, err := ns.convert(obj) - if err != nil { - return nil, err - } - - key := types.NamespacedName{Namespace: instance.Namespace, Name: instance.Spec.ResourceGroup} - - return []resourcemanager.KubeParent{ - {Key: key, Target: &v1alpha1.ResourceGroup{}}, - }, nil -} - -func (g *mockEventHubNamespaceManager) GetStatus(obj runtime.Object) (*v1alpha1.ASOStatus, error) { - instance, err := g.convert(obj) - if err != nil { - return nil, err - } - return &instance.Status, nil -} - -func (ns *mockEventHubNamespaceManager) convert(obj runtime.Object) (*v1alpha1.EventhubNamespace, error) { - local, ok := obj.(*v1alpha1.EventhubNamespace) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} diff --git a/pkg/resourcemanager/mock/helpers/helpers.go b/pkg/resourcemanager/mock/helpers/helpers.go deleted file mode 100644 index 0c2238a50ae..00000000000 --- a/pkg/resourcemanager/mock/helpers/helpers.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package helpers - -import ( - "fmt" - "github.com/Azure/go-autorest/autorest" - "net/http" -) - -func GetRestResponse(statusCode int) autorest.Response { - return autorest.Response{ - Response: &http.Response{ - Status: fmt.Sprintf("%d", statusCode), - StatusCode: statusCode, - }, - } -} diff --git a/pkg/resourcemanager/mock/keyvaults/keyvault.go b/pkg/resourcemanager/mock/keyvaults/keyvault.go deleted file mode 100644 index 7158422549d..00000000000 --- a/pkg/resourcemanager/mock/keyvaults/keyvault.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package keyvaults - -import ( - "context" - "errors" - "fmt" - "net/http" - - "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2018-02-14/keyvault" - "github.com/Azure/azure-service-operator/api/v1alpha1" - pkghelpers "github.com/Azure/azure-service-operator/pkg/helpers" - "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" - "k8s.io/apimachinery/pkg/runtime" -) - -type keyVaultResource struct { - resourceGroupName string - vaultName string - KeyVault keyvault.Vault -} - -type MockKeyVaultManager struct { - keyVaultResources []keyVaultResource -} - -func findKeyVault(res []keyVaultResource, predicate func(keyVaultResource) bool) (int, keyVaultResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, keyVaultResource{} -} - -// CreateVault creates a new key vault -func (manager *MockKeyVaultManager) CreateVault(ctx context.Context, instance *v1alpha1.KeyVault, tags map[string]*string) (keyvault.Vault, error) { - vaultName := instance.Name - groupName := instance.Spec.ResourceGroup - location := instance.Spec.Location - - v := keyvault.Vault{ - Response: helpers.GetRestResponse(http.StatusOK), - Properties: &keyvault.VaultProperties{}, - ID: to.StringPtr(pkghelpers.RandomString(10)), - Name: to.StringPtr(vaultName), - Location: to.StringPtr(location), - } - - _, err := manager.GetVault(ctx, groupName, vaultName) - if err != nil { - manager.keyVaultResources = append(manager.keyVaultResources, keyVaultResource{ - resourceGroupName: groupName, - vaultName: vaultName, - KeyVault: v, - }) - } - - return v, nil -} - -// CreateVaultWithAccessPolicies creates a new key vault -func (manager *MockKeyVaultManager) CreateVaultWithAccessPolicies(ctx context.Context, groupName string, vaultName string, location string, clientID string) (keyvault.Vault, error) { - v := keyvault.Vault{ - Response: helpers.GetRestResponse(http.StatusOK), - Properties: &keyvault.VaultProperties{}, - ID: to.StringPtr(pkghelpers.RandomString(10)), - Name: to.StringPtr(vaultName), - Location: to.StringPtr(location), - } - - _, err := manager.GetVault(ctx, groupName, vaultName) - if err != nil { - manager.keyVaultResources = append(manager.keyVaultResources, keyVaultResource{ - resourceGroupName: groupName, - vaultName: vaultName, - KeyVault: v, - }) - } - - return v, nil -} - -// DeleteVault removes the resource group named by env var -func (manager *MockKeyVaultManager) DeleteVault(ctx context.Context, groupName string, vaultName string) (result autorest.Response, err error) { - vaults := manager.keyVaultResources - - index, _ := findKeyVault(vaults, func(g keyVaultResource) bool { - return g.resourceGroupName == groupName && - g.vaultName == vaultName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("key vault not found") - } - - manager.keyVaultResources = append(vaults[:index], vaults[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -// Returns an existing keyvault instance -func (manager *MockKeyVaultManager) GetVault(ctx context.Context, groupName string, vaultName string) (result keyvault.Vault, err error) { - vaults := manager.keyVaultResources - - index, v := findKeyVault(vaults, func(g keyVaultResource) bool { - return g.resourceGroupName == groupName && - g.vaultName == vaultName - }) - - if index == -1 { - return keyvault.Vault{ - Response: helpers.GetRestResponse(http.StatusNotFound), - }, errors.New("key vault not found") - } - - return v.KeyVault, nil -} - -func (manager *MockKeyVaultManager) convert(obj runtime.Object) (*v1alpha1.KeyVault, error) { - local, ok := obj.(*v1alpha1.KeyVault) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} - -func (manager *MockKeyVaultManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := manager.convert(obj) - if err != nil { - return true, err - } - - tags := map[string]*string{} - _, _ = manager.CreateVault( - ctx, - instance, - tags, - ) - - instance.Status.Provisioned = true - - return true, nil -} -func (manager *MockKeyVaultManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := manager.convert(obj) - if err != nil { - return true, err - } - - _, _ = manager.DeleteVault(ctx, instance.Spec.ResourceGroup, instance.Name) - - return false, nil -} -func (manager *MockKeyVaultManager) GetParents(runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} diff --git a/pkg/resourcemanager/mock/psql/postgresqldatabase.go b/pkg/resourcemanager/mock/psql/postgresqldatabase.go deleted file mode 100644 index 50ea2ab9e24..00000000000 --- a/pkg/resourcemanager/mock/psql/postgresqldatabase.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package psql - -import ( - "context" - "errors" - "fmt" - "net/http" - - postgresql "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" - "github.com/Azure/azure-service-operator/api/v1alpha1" - resourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest/to" - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" -) - -type MockPostgreSqlDbManager struct { - resourceGroupName string - PostgresqlDbs []MockPostgreSqlDbResource - Log logr.Logger -} - -type MockPostgreSqlDbResource struct { - resourceGroupName string - PostgresqlServerName string - PostgresqlDb postgresql.Database -} - -func NewMockPostgreSqlDbManager(log logr.Logger) *MockPostgreSqlDbManager { - return &MockPostgreSqlDbManager{ - Log: log, - } -} - -func findPostgreSqlDb(res []MockPostgreSqlDbResource, predicate func(MockPostgreSqlDbResource) bool) (int, error) { - for index, r := range res { - if predicate(r) { - return index, nil - } - } - return -1, errors.New("not found") -} - -//CreateorUpdateDB creates a Postgresql Db -func (manager *MockPostgreSqlDbManager) CreateDatabaseIfValid(ctx context.Context, databasename string, servername string, resourcegroup string) (postgresql.DatabasesCreateOrUpdateFuture, error) { - index, _ := findPostgreSqlDb(manager.PostgresqlDbs, func(s MockPostgreSqlDbResource) bool { - return s.resourceGroupName == resourcegroup && s.PostgresqlServerName == servername && *s.PostgresqlDb.Name == databasename - }) - - PostgresqlD := postgresql.Database{ - Response: helpers.GetRestResponse(http.StatusCreated), - Name: to.StringPtr(databasename), - } - - q := MockPostgreSqlDbResource{ - resourceGroupName: resourcegroup, - PostgresqlServerName: servername, - PostgresqlDb: PostgresqlD, - } - - if index == -1 { - manager.PostgresqlDbs = append(manager.PostgresqlDbs, q) - } - - return postgresql.DatabasesCreateOrUpdateFuture{}, nil - -} - -func (manager *MockPostgreSqlDbManager) GetDatabase(ctx context.Context, resourcegroup string, servername string, database string) (postgresql.Database, error) { - index, _ := findPostgreSqlDb(manager.PostgresqlDbs, func(s MockPostgreSqlDbResource) bool { - return s.resourceGroupName == resourcegroup && s.PostgresqlServerName == servername && *s.PostgresqlDb.Name == database - }) - - if index == -1 { - return postgresql.Database{}, errors.New("Sql Db Not Found") - } - - return manager.PostgresqlDbs[index].PostgresqlDb, nil -} - -// DeleteDb removes the Postgresql db -func (manager *MockPostgreSqlDbManager) DeleteDatabase(ctx context.Context, databasename string, servername string, resourcegroup string) (string, error) { - PostgresqlDbs := manager.PostgresqlDbs - - index, _ := findPostgreSqlDb(manager.PostgresqlDbs, func(s MockPostgreSqlDbResource) bool { - return s.resourceGroupName == resourcegroup && s.PostgresqlServerName == servername && *s.PostgresqlDb.Name == databasename - }) - - if index == -1 { - return "Not found", nil - } - - manager.PostgresqlDbs = append(PostgresqlDbs[:index], PostgresqlDbs[index+1:]...) - - return "Deleted", nil -} - -func (manager *MockPostgreSqlDbManager) convert(obj runtime.Object) (*v1alpha1.PostgreSQLDatabase, error) { - local, ok := obj.(*v1alpha1.PostgreSQLDatabase) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} - -func (manager *MockPostgreSqlDbManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := manager.convert(obj) - if err != nil { - return true, err - } - - _, _ = manager.CreateDatabaseIfValid(ctx, instance.Name, instance.Spec.Server, instance.Spec.ResourceGroup) - - instance.Status.Provisioned = true - - return true, nil -} - -func (manager *MockPostgreSqlDbManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := manager.convert(obj) - if err != nil { - return true, err - } - - _, _ = manager.DeleteDatabase(ctx, instance.Name, instance.Spec.Server, instance.Spec.ResourceGroup) - - return false, nil -} - -func (manager *MockPostgreSqlDbManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} diff --git a/pkg/resourcemanager/mock/psql/postgresqlfirewallrule.go b/pkg/resourcemanager/mock/psql/postgresqlfirewallrule.go deleted file mode 100644 index 9e09275ddd1..00000000000 --- a/pkg/resourcemanager/mock/psql/postgresqlfirewallrule.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package psql - -import ( - "context" - "errors" - "fmt" - "net/http" - - postgresql "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" - "github.com/Azure/azure-service-operator/api/v1alpha1" - resourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest/to" - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" -) - -type MockPostgreSqlFirewallRuleManager struct { - resourceGroupName string - PostgresqlFirewallRules []MockPostgreSqlFirewallRuleResource - Log logr.Logger -} - -type MockPostgreSqlFirewallRuleResource struct { - resourceGroupName string - PostgresqlServerName string - PostgresqlFirewallRule postgresql.FirewallRule -} - -func NewMockPostgreSqlFirewallRuleManager(log logr.Logger) *MockPostgreSqlFirewallRuleManager { - return &MockPostgreSqlFirewallRuleManager{ - Log: log, - } -} - -func findPostgreSqlFirewallRule(res []MockPostgreSqlFirewallRuleResource, predicate func(MockPostgreSqlFirewallRuleResource) bool) (int, error) { - for index, r := range res { - if predicate(r) { - return index, nil - } - } - return -1, errors.New("not found") -} - -//CreateorUpdateFirewallRule creates a Postgresql FirewallRule -func (manager *MockPostgreSqlFirewallRuleManager) CreateFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string, startip string, endip string) (postgresql.FirewallRulesCreateOrUpdateFuture, error) { - index, _ := findPostgreSqlFirewallRule(manager.PostgresqlFirewallRules, func(s MockPostgreSqlFirewallRuleResource) bool { - return s.resourceGroupName == resourcegroup && s.PostgresqlServerName == servername && *s.PostgresqlFirewallRule.Name == firewallrulename - }) - - PostgresqlF := postgresql.FirewallRule{ - Response: helpers.GetRestResponse(http.StatusCreated), - Name: to.StringPtr(firewallrulename), - FirewallRuleProperties: &postgresql.FirewallRuleProperties{ - StartIPAddress: to.StringPtr(startip), - EndIPAddress: to.StringPtr(endip), - }, - } - - q := MockPostgreSqlFirewallRuleResource{ - resourceGroupName: resourcegroup, - PostgresqlServerName: servername, - PostgresqlFirewallRule: PostgresqlF, - } - - if index == -1 { - manager.PostgresqlFirewallRules = append(manager.PostgresqlFirewallRules, q) - } - - return postgresql.FirewallRulesCreateOrUpdateFuture{}, nil - -} - -func (manager *MockPostgreSqlFirewallRuleManager) GetFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string) (postgresql.FirewallRule, error) { - index, _ := findPostgreSqlFirewallRule(manager.PostgresqlFirewallRules, func(s MockPostgreSqlFirewallRuleResource) bool { - return s.resourceGroupName == resourcegroup && s.PostgresqlServerName == servername && *s.PostgresqlFirewallRule.Name == firewallrulename - }) - - if index == -1 { - return postgresql.FirewallRule{}, errors.New("Sql Fw rule Not Found") - } - - return manager.PostgresqlFirewallRules[index].PostgresqlFirewallRule, nil -} - -// DeleteFirewallRule removes the Postgresql FirewallRule -func (manager *MockPostgreSqlFirewallRuleManager) DeleteFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string) (string, error) { - PostgresqlFirewallRules := manager.PostgresqlFirewallRules - - index, _ := findPostgreSqlFirewallRule(manager.PostgresqlFirewallRules, func(s MockPostgreSqlFirewallRuleResource) bool { - return s.resourceGroupName == resourcegroup && s.PostgresqlServerName == servername && *s.PostgresqlFirewallRule.Name == firewallrulename - }) - - if index == -1 { - return "Not found", nil - } - - manager.PostgresqlFirewallRules = append(PostgresqlFirewallRules[:index], PostgresqlFirewallRules[index+1:]...) - - return "Deleted", nil -} - -func (manager *MockPostgreSqlFirewallRuleManager) convert(obj runtime.Object) (*v1alpha1.PostgreSQLFirewallRule, error) { - local, ok := obj.(*v1alpha1.PostgreSQLFirewallRule) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} - -func (manager *MockPostgreSqlFirewallRuleManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := manager.convert(obj) - if err != nil { - return true, err - } - - _, _ = manager.CreateFirewallRule(ctx, instance.Spec.ResourceGroup, instance.Spec.Server, instance.Name, "0.0.0.0", "0.0.0.0") - - instance.Status.Provisioned = true - - return true, nil -} - -func (manager *MockPostgreSqlFirewallRuleManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := manager.convert(obj) - if err != nil { - return true, err - } - - _, _ = manager.DeleteFirewallRule(ctx, instance.Spec.ResourceGroup, instance.Spec.Server, instance.Name) - - return false, nil -} - -func (manager *MockPostgreSqlFirewallRuleManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} diff --git a/pkg/resourcemanager/mock/psql/postgresqlserver.go b/pkg/resourcemanager/mock/psql/postgresqlserver.go deleted file mode 100644 index 14fbeaa66f6..00000000000 --- a/pkg/resourcemanager/mock/psql/postgresqlserver.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package psql - -import ( - "context" - "errors" - "fmt" - "net/http" - - postgresql "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" - "github.com/Azure/azure-service-operator/api/v1alpha1" - resourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/azure-service-operator/pkg/secrets" - "github.com/Azure/go-autorest/autorest/to" - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/runtime" -) - -type MockPostgreSqlServerManager struct { - resourceGroupName string - PostgresqlServers []MockPostgreSqlServerResource - SecretClient secrets.SecretClient - Scheme *runtime.Scheme - Log logr.Logger -} - -type MockPostgreSqlServerResource struct { - resourceGroupName string - PostgresqlServer postgresql.Server -} - -func NewMockPSQLServerClient(log logr.Logger, secretclient secrets.SecretClient, scheme *runtime.Scheme) *MockPostgreSqlServerManager { - return &MockPostgreSqlServerManager{ - Log: log, - SecretClient: secretclient, - Scheme: scheme, - } -} - -func findPostgreSqlServer(res []MockPostgreSqlServerResource, predicate func(MockPostgreSqlServerResource) bool) (int, error) { - for index, r := range res { - if predicate(r) { - return index, nil - } - } - return -1, errors.New("not found") -} - -// CreateServerIfValid creates a new Postgresql server -func (manager *MockPostgreSqlServerManager) CreateServerIfValid(ctx context.Context, servername string, resourcegroup string, location string, tags map[string]*string, serverversion postgresql.ServerVersion, sslenforcement postgresql.SslEnforcementEnum, skuInfo postgresql.Sku, adminlogin string, adminpassword string) (postgresql.ServersCreateFuture, error) { - index, _ := findPostgreSqlServer(manager.PostgresqlServers, func(s MockPostgreSqlServerResource) bool { - return s.resourceGroupName == resourcegroup && *s.PostgresqlServer.Name == servername - }) - - postgresqlS := postgresql.Server{ - Response: helpers.GetRestResponse(http.StatusCreated), - Location: to.StringPtr(location), - Name: to.StringPtr(servername), - } - - q := MockPostgreSqlServerResource{ - resourceGroupName: resourcegroup, - PostgresqlServer: postgresqlS, - } - - if index == -1 { - manager.PostgresqlServers = append(manager.PostgresqlServers, q) - } - - f := postgresql.ServersCreateFuture{} - - return f, nil -} - -// DeleteServer removes the postgresqlserver -func (manager *MockPostgreSqlServerManager) DeleteServer(ctx context.Context, resourcegroup string, servername string) (string, error) { - PostgresqlServers := manager.PostgresqlServers - - index, _ := findPostgreSqlServer(PostgresqlServers, func(s MockPostgreSqlServerResource) bool { - return s.resourceGroupName == resourcegroup && *s.PostgresqlServer.Name == servername - }) - - if index == -1 { - return "Not Found", nil - } - - manager.PostgresqlServers = append(PostgresqlServers[:index], PostgresqlServers[index+1:]...) - - return "Deleted", nil -} - -// GetServer gets a postgresql server -func (manager *MockPostgreSqlServerManager) GetServer(ctx context.Context, resourcegroup string, servername string) (postgresql.Server, error) { - index, _ := findPostgreSqlServer(manager.PostgresqlServers, func(s MockPostgreSqlServerResource) bool { - return s.resourceGroupName == resourcegroup && *s.PostgresqlServer.Name == servername - }) - - if index == -1 { - return postgresql.Server{}, errors.New("Sql Server Not Found") - } - - state := postgresql.ServerState("Ready") - serverProperties := postgresql.ServerProperties{UserVisibleState: state} - server := manager.PostgresqlServers[index].PostgresqlServer - server.ServerProperties = &serverProperties - - return server, nil -} - -func (manager *MockPostgreSqlServerManager) convert(obj runtime.Object) (*v1alpha1.PostgreSQLServer, error) { - local, ok := obj.(*v1alpha1.PostgreSQLServer) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} - -func (manager *MockPostgreSqlServerManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := manager.convert(obj) - if err != nil { - return true, err - } - - tags := map[string]*string{} - _, _ = manager.CreateServerIfValid( - ctx, - instance.Name, - instance.Spec.ResourceGroup, - instance.Spec.Location, - tags, - postgresql.ServerVersion("10"), - postgresql.SslEnforcementEnumEnabled, - postgresql.Sku{}, - "", - "", - ) - - instance.Status.Provisioned = true - - return true, nil -} -func (manager *MockPostgreSqlServerManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - - instance, err := manager.convert(obj) - if err != nil { - return true, err - } - - _, _ = manager.DeleteServer(ctx, instance.Spec.ResourceGroup, instance.Name) - - return false, nil -} -func (manager *MockPostgreSqlServerManager) GetParents(runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} diff --git a/pkg/resourcemanager/mock/rediscaches/rediscaches.go b/pkg/resourcemanager/mock/rediscaches/rediscaches.go deleted file mode 100644 index 4f5883ab21e..00000000000 --- a/pkg/resourcemanager/mock/rediscaches/rediscaches.go +++ /dev/null @@ -1,125 +0,0 @@ -// 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 rediscaches - -import ( - "context" - "errors" - "fmt" - "net/http" - - azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" - resourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest/to" - - "github.com/Azure/azure-sdk-for-go/services/redis/mgmt/2018-03-01/redis" - "k8s.io/apimachinery/pkg/runtime" -) - -type redisCacheResource struct { - resourceGroupName string - redis redis.ResourceType -} - -type mockRedisCacheManager struct { - resourceGroupName string - redisCaches []redisCacheResource -} - -func NewMockRedisCacheManager() *mockRedisCacheManager { - return &mockRedisCacheManager{} -} - -func findRedisCache(res []redisCacheResource, predicate func(redisCacheResource) bool) (int, redisCacheResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, redisCacheResource{} -} - -func (manager *mockRedisCacheManager) CreateRedisCache(ctx context.Context, - groupName string, - redisCacheName string, - location string, - sku azurev1alpha1.RedisCacheSku, - enableNonSSLPort bool, - tags map[string]*string) (*redis.ResourceType, error) { - index, _ := findRedisCache(manager.redisCaches, func(s redisCacheResource) bool { - return s.resourceGroupName == groupName && *s.redis.Name == redisCacheName - }) - - rc := redis.ResourceType{ - Response: helpers.GetRestResponse(http.StatusCreated), - Location: to.StringPtr(location), - Name: to.StringPtr(redisCacheName), - } - - r := redisCacheResource{ - resourceGroupName: groupName, - redis: rc, - } - - if index == -1 { - manager.redisCaches = append(manager.redisCaches, r) - } - - return &r.redis, nil -} - -func (manager *mockRedisCacheManager) DeleteRedisCache(ctx context.Context, groupName string, redisCacheName string) (result redis.DeleteFuture, err error) { - redisCaches := manager.redisCaches - - index, _ := findRedisCache(redisCaches, func(s redisCacheResource) bool { - return s.resourceGroupName == groupName && - *s.redis.Name == redisCacheName - }) - - if index == -1 { - return redis.DeleteFuture{}, errors.New("Redis Cache Not Found") - } - - manager.redisCaches = append(redisCaches[:index], redisCaches[index+1:]...) - - return redis.DeleteFuture{}, nil -} - -func (manager *mockRedisCacheManager) convert(obj runtime.Object) (*azurev1alpha1.RedisCache, error) { - local, ok := obj.(*azurev1alpha1.RedisCache) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} - -func (manager *mockRedisCacheManager) Ensure(ctx context.Context, obj runtime.Object) (bool, error) { - instance, err := manager.convert(obj) - if err != nil { - return false, err - } - tags := map[string]*string{} - _, _ = manager.CreateRedisCache(ctx, instance.Spec.ResourceGroupName, instance.Name, instance.Spec.Location, instance.Spec.Properties.Sku, instance.Spec.Properties.EnableNonSslPort, tags) - - instance.Status.Provisioned = true - return true, nil - -} - -func (manager *mockRedisCacheManager) Delete(ctx context.Context, obj runtime.Object) (bool, error) { - instance, err := manager.convert(obj) - if err != nil { - return false, err - } - - _, _ = manager.DeleteRedisCache(ctx, instance.Spec.ResourceGroupName, instance.Name) - - return false, nil -} -func (manager *mockRedisCacheManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return []resourcemanager.KubeParent{}, nil -} diff --git a/pkg/resourcemanager/mock/resourcegroups/resourcegroup.go b/pkg/resourcemanager/mock/resourcegroups/resourcegroup.go deleted file mode 100644 index 2caaca55ce6..00000000000 --- a/pkg/resourcemanager/mock/resourcegroups/resourcegroup.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package resourcegroups - -import ( - "context" - "errors" - "fmt" - "net/http" - - azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" - - "github.com/Azure/azure-service-operator/pkg/resourcemanager" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2017-05-10/resources" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" -) - -type MockResourceGroupManager struct { - resourceGroups []resources.Group -} - -func findResourceGroup(res []resources.Group, predicate func(resources.Group) bool) (int, resources.Group) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, resources.Group{} -} - -// CreateGroup creates a new resource group -func (manager *MockResourceGroupManager) CreateGroup(ctx context.Context, groupName string, location string) (resources.Group, error) { - index, _ := findResourceGroup(manager.resourceGroups, func(g resources.Group) bool { - return *g.Name == groupName - }) - - r := resources.Group{ - Response: helpers.GetRestResponse(201), - Location: to.StringPtr(location), - Name: to.StringPtr(groupName), - } - - if index == -1 { - manager.resourceGroups = append(manager.resourceGroups, r) - } - - return r, nil -} - -// DeleteGroup removes the resource group -func (manager *MockResourceGroupManager) DeleteGroup(ctx context.Context, groupName string) (autorest.Response, error) { - groups := manager.resourceGroups - - index, _ := findResourceGroup(groups, func(g resources.Group) bool { - return *g.Name == groupName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("resource group not found") - } - - manager.resourceGroups = append(groups[:index], groups[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} - -func (manager *MockResourceGroupManager) DeleteGroupAsync(ctx context.Context, groupName string) (resources.GroupsDeleteFuture, error) { - _, err := manager.DeleteGroup(ctx, groupName) - - return resources.GroupsDeleteFuture{}, err -} - -func (manager *MockResourceGroupManager) CheckExistence(ctx context.Context, groupName string) (autorest.Response, error) { - groups := manager.resourceGroups - index, _ := findResourceGroup(groups, func(g resources.Group) bool { - return *g.Name == groupName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("resource group not found") - } - - return helpers.GetRestResponse(http.StatusNoContent), nil -} - -func (g *MockResourceGroupManager) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := g.convert(obj) - if err != nil { - return false, err - } - - _, err = g.CreateGroup(ctx, instance.ObjectMeta.Name, instance.Spec.Location) - if err != nil { - return false, err - } - - instance.Status.Provisioning = true - instance.Status.Provisioned = true - - return true, nil -} - -func (g *MockResourceGroupManager) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := g.convert(obj) - if err != nil { - return false, err - } - - _, err = g.DeleteGroup(ctx, instance.ObjectMeta.Name) - if err != nil { - return false, err - } - - return false, nil -} - -func (g *MockResourceGroupManager) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - return nil, nil -} - -func (g *MockResourceGroupManager) convert(obj runtime.Object) (*azurev1alpha1.ResourceGroup, error) { - local, ok := obj.(*azurev1alpha1.ResourceGroup) - if !ok { - return nil, fmt.Errorf("failed type assertion on kind: %s", obj.GetObjectKind().GroupVersionKind().String()) - } - return local, nil -} diff --git a/pkg/resourcemanager/mock/sqlclient/sqlclient_gosdk.go b/pkg/resourcemanager/mock/sqlclient/sqlclient_gosdk.go deleted file mode 100644 index c3adc758b08..00000000000 --- a/pkg/resourcemanager/mock/sqlclient/sqlclient_gosdk.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package azuresql - -import ( - "context" - "net/http" - - "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2015-05-01-preview/sql" - azuresqlshared "github.com/Azure/azure-service-operator/pkg/resourcemanager/azuresql/azuresqlshared" - helpers "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest" -) - -// MockGoSDKClient struct -type MockGoSDKClient struct { - Server sql.Server - Database sql.Database - FirewallRule sql.FirewallRule - FailoverGroup sql.FailoverGroup - DatabasesCreateOrUpdateFuture sql.DatabasesCreateOrUpdateFuture - FailoverGroupsCreateOrUpdateFuture sql.FailoverGroupsCreateOrUpdateFuture -} - -// CreateOrUpdateSQLServer creates a new sql server -func (sdk *MockGoSDKClient) CreateOrUpdateSQLServer(ctx context.Context, resourceGroupName string, location string, serverName string, tags map[string]*string, properties azuresqlshared.SQLServerProperties, forceUpdate bool) (result sql.Server, err error) { - var sqlServer = sql.Server{ - Response: helpers.GetRestResponse(http.StatusCreated), - } - - sdk.Server = sqlServer - - return sqlServer, nil -} - -//DeleteSQLServer return StatusOK -func (sdk *MockGoSDKClient) DeleteSQLServer(ctx context.Context, resourceGroupName string, serverName string) (result autorest.Response, err error) { - - return helpers.GetRestResponse(http.StatusOK), nil -} - -//GetServer get server -func (sdk *MockGoSDKClient) GetServer(ctx context.Context, resourceGroupName string, serverName string) (result sql.Server, err error) { - - state := "Ready" - serverProperties := sql.ServerProperties{State: &state} - var sqlServer = sql.Server{ - Response: helpers.GetRestResponse(http.StatusCreated), - ServerProperties: &serverProperties, - } - - sdk.Server = sqlServer - - return sqlServer, nil -} - -//CreateOrUpdateSQLFirewallRule create or -func (sdk *MockGoSDKClient) CreateOrUpdateSQLFirewallRule(ctx context.Context, resourceGroupName string, serverName string, ruleName string, startIP string, endIP string) (result bool, err error) { - - return true, nil -} - -//DeleteSQLFirewallRule delete sql firewall -func (sdk *MockGoSDKClient) DeleteSQLFirewallRule(ctx context.Context, resourceGroupName string, serverName string, ruleName string) (err error) { - return nil -} - -//DeleteDB delete database -func (sdk *MockGoSDKClient) DeleteDB(ctx context.Context, resourceGroupName string, serverName string, databaseName string) (result autorest.Response, err error) { - - return helpers.GetRestResponse(http.StatusOK), nil -} - -//GetSQLFirewallRule get sql firewall rule -func (sdk *MockGoSDKClient) GetSQLFirewallRule(ctx context.Context, resourceGroupName string, serverName string, ruleName string) (result sql.FirewallRule, err error) { - - var sqlFirewallRule = sql.FirewallRule{ - Response: helpers.GetRestResponse(http.StatusCreated), - } - - sdk.FirewallRule = sqlFirewallRule - - return sqlFirewallRule, nil -} - -//GetDB get database -func (sdk *MockGoSDKClient) GetDB(ctx context.Context, resourceGroupName string, serverName string, databaseName string) (sql.Database, error) { - - var sqlDatabase = sql.Database{ - Response: helpers.GetRestResponse(http.StatusCreated), - } - - sdk.Database = sqlDatabase - - return sqlDatabase, nil -} - -//CreateOrUpdateDB create or update DB -func (sdk *MockGoSDKClient) CreateOrUpdateDB(ctx context.Context, resourceGroupName string, location string, serverName string, tags map[string]*string, properties azuresqlshared.SQLDatabaseProperties) (sql.DatabasesCreateOrUpdateFuture, error) { - - var sqlDatabasesCreateOrUpdateFuture = sql.DatabasesCreateOrUpdateFuture{} - - return sqlDatabasesCreateOrUpdateFuture, nil -} - -//CreateOrUpdateFailoverGroup create or update failover group -func (sdk *MockGoSDKClient) CreateOrUpdateFailoverGroup(ctx context.Context, resourceGroupName string, serverName string, failovergroupname string, properties azuresqlshared.SQLFailoverGroupProperties) (result sql.FailoverGroupsCreateOrUpdateFuture, err error) { - - var sqlFailoverGroupsCreateOrUpdateFuture = sql.FailoverGroupsCreateOrUpdateFuture{} - sdk.FailoverGroupsCreateOrUpdateFuture = sqlFailoverGroupsCreateOrUpdateFuture - - return sqlFailoverGroupsCreateOrUpdateFuture, nil - -} - -//DeleteFailoverGroup delete fail over group -func (sdk *MockGoSDKClient) DeleteFailoverGroup(ctx context.Context, resourceGroupName string, serverName string, failoverGroupName string) (result autorest.Response, err error) { - - return helpers.GetRestResponse(http.StatusOK), nil -} - -//DeleteFailoverGroup delete fail over group -func (sdk *MockGoSDKClient) GetFailoverGroup(ctx context.Context, resourceGroupName string, serverName string, failovergroupname string) (sql.FailoverGroup, error) { - var sqlFailovergroup = sql.FailoverGroup{ - Response: helpers.GetRestResponse(http.StatusCreated), - } - - sdk.FailoverGroup = sqlFailovergroup - - return sqlFailovergroup, nil -} diff --git a/pkg/resourcemanager/mock/storages/blob_container.go b/pkg/resourcemanager/mock/storages/blob_container.go deleted file mode 100644 index ca04f55268e..00000000000 --- a/pkg/resourcemanager/mock/storages/blob_container.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package storages - -import ( - "context" - "errors" - "net/http" - - "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest" -) - -type blobContainerResource struct { - resourceGroupName string - storageAccountName string - blobContainerName string - accessLevel storage.PublicAccess - blobContainer storage.BlobContainer -} - -type mockBlobContainerManager struct { - blobContainerResource []blobContainerResource -} - -func findBlobContainer(res []blobContainerResource, predicate func(blobContainerResource) bool) (int, blobContainerResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, blobContainerResource{} -} - -// Creates a blob container in a storage account. -// Parameters: -// resourceGroupName - name of the resource group within the azure subscription. -// accountName - the name of the storage account -// containerName - the name of the container -func (manager *mockBlobContainerManager) CreateBlobContainer(ctx context.Context, resourceGroupName string, accountName string, containerName string, accessLevel storage.PublicAccess) (*storage.BlobContainer, error) { - bc := blobContainerResource{ - resourceGroupName: resourceGroupName, - storageAccountName: accountName, - blobContainerName: containerName, - blobContainer: storage.BlobContainer{}, - } - manager.blobContainerResource = append(manager.blobContainerResource, bc) - return &bc.blobContainer, nil -} - -// Get gets the description of the specified blob container. -// Parameters: -// resourceGroupName - name of the resource group within the azure subscription. -// accountName - the name of the storage account -// containerName - the name of the container -func (manager *mockBlobContainerManager) GetBlobContainer(ctx context.Context, resourceGroupName string, accountName string, containerName string) (storage.BlobContainer, error) { - containers := manager.blobContainerResource - - index, c := findBlobContainer(containers, func(g blobContainerResource) bool { - return g.resourceGroupName == resourceGroupName && - g.storageAccountName == accountName && - g.blobContainerName == containerName - }) - - if index == -1 { - return storage.BlobContainer{}, errors.New("blob container not found") - } - - return c.blobContainer, nil -} - -// Deletes a blob container in a storage account. -// Parameters: -// resourceGroupName - name of the resource group within the azure subscription. -// accountName - the name of the storage account -// containerName - the name of the container -func (manager *mockBlobContainerManager) DeleteBlobContainer(ctx context.Context, resourceGroupName string, accountName string, containerName string) (autorest.Response, error) { - containers := manager.blobContainerResource - - index, _ := findBlobContainer(containers, func(g blobContainerResource) bool { - return g.resourceGroupName == resourceGroupName && - g.storageAccountName == accountName && - g.blobContainerName == containerName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("blob container not found") - } - - manager.blobContainerResource = append(containers[:index], containers[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} diff --git a/pkg/resourcemanager/mock/storages/storageaccount.go b/pkg/resourcemanager/mock/storages/storageaccount.go deleted file mode 100644 index 48a939f73a4..00000000000 --- a/pkg/resourcemanager/mock/storages/storageaccount.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package storages - -import ( - "context" - "errors" - "net/http" - - storage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" - azurev1alpha1 "github.com/Azure/azure-service-operator/api/v1alpha1" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/mock/helpers" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" -) - -type storageResource struct { - resourceGroupName string - storageAccountName string - StorageAccount storage.Account -} - -type mockStorageManager struct { - storageResource []storageResource -} - -func findStorage(res []storageResource, predicate func(storageResource) bool) (int, storageResource) { - for index, r := range res { - if predicate(r) { - return index, r - } - } - return -1, storageResource{} -} - -type Finder interface { -} - -type StorageResources []storageResource - -func (srs *StorageResources) Find(predicate func(storageResource) bool) { - -} - -func (manager *mockStorageManager) CreateStorage(ctx context.Context, groupName string, - storageAccountName string, - location string, - sku azurev1alpha1.StorageAccountSku, - kind azurev1alpha1.StorageAccountKind, - tags map[string]*string, - accessTier azurev1alpha1.StorageAccountAccessTier, - enableHTTPsTrafficOnly *bool, dataLakeEnabled *bool, networkRule *azurev1alpha1.NetworkRuleSet) (result storage.Account, err error) { - s := storageResource{ - resourceGroupName: groupName, - storageAccountName: storageAccountName, - StorageAccount: storage.Account{ - Response: helpers.GetRestResponse(201), - Tags: tags, - Location: to.StringPtr(location), - Name: to.StringPtr(storageAccountName), - AccountProperties: &storage.AccountProperties{ - ProvisioningState: storage.Succeeded, - }, - }, - } - - manager.storageResource = append(manager.storageResource, s) - return s.StorageAccount, nil -} - -// Get gets the description of the specified storage account. -func (manager *mockStorageManager) GetStorage(ctx context.Context, resourceGroupName string, storageAccountName string) (storage.Account, error) { - groups := manager.storageResource - - index, group := findStorage(groups, func(g storageResource) bool { - return g.resourceGroupName == resourceGroupName && - g.storageAccountName == storageAccountName - }) - - if index == -1 { - return storage.Account{}, errors.New("storage account not found") - } - - return group.StorageAccount, nil -} - -// removes the storage account -func (manager *mockStorageManager) DeleteStorage(ctx context.Context, resourceGroupName string, storageAccountName string) (autorest.Response, error) { - groups := manager.storageResource - - index, _ := findStorage(groups, func(g storageResource) bool { - return g.resourceGroupName == resourceGroupName && - g.storageAccountName == storageAccountName - }) - - if index == -1 { - return helpers.GetRestResponse(http.StatusNotFound), errors.New("storage account not found") - } - - manager.storageResource = append(groups[:index], groups[index+1:]...) - - return helpers.GetRestResponse(http.StatusOK), nil -} From 8f623cbde16f3c8d6cb801a0ab6fd6575f6d6b9c Mon Sep 17 00:00:00 2001 From: Erin Corson Date: Mon, 27 Apr 2020 12:37:14 -0600 Subject: [PATCH 5/6] removing references to mocks --- Makefile | 4 ++-- controllers/eventhubnamespace_controller_test.go | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index cd72d52f505..cc1f2fde4e1 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ api-test: generate fmt vet manifests # Run tests test: generate fmt vet manifests - TEST_USE_EXISTING_CLUSTER=false TEST_CONTROLLER_WITH_MOCKS=true REQUEUE_AFTER=20 \ + TEST_USE_EXISTING_CLUSTER=false REQUEUE_AFTER=20 \ go test -tags "$(BUILD_TAGS)" -parallel 3 -v -coverprofile=coverage.txt -covermode count \ ./api/... \ ./controllers/... \ @@ -60,7 +60,7 @@ unit-tests: # Run tests with existing cluster test-existing-managers: generate fmt vet manifests - TEST_USE_EXISTING_CLUSTER=true TEST_CONTROLLER_WITH_MOCKS=false REQUEUE_AFTER=20 \ + TEST_USE_EXISTING_CLUSTER=true REQUEUE_AFTER=20 \ go test -v -coverprofile=coverage-existing.txt -covermode count \ ./api/... \ ./pkg/resourcemanager/eventhubs/... \ diff --git a/controllers/eventhubnamespace_controller_test.go b/controllers/eventhubnamespace_controller_test.go index ffc3817d710..5326a5ed4a0 100644 --- a/controllers/eventhubnamespace_controller_test.go +++ b/controllers/eventhubnamespace_controller_test.go @@ -23,8 +23,7 @@ func TestEventHubNamespaceControllerNoResourceGroup(t *testing.T) { var rgLocation string rgLocation = tc.resourceGroupLocation - // setting this rg name tells the mocks to set a proper error - resourceGroupName := "gone" + resourceGroupName := GenerateTestResourceNameWithRandom("rg", 10) eventhubNamespaceName := GenerateTestResourceNameWithRandom("ns-dev-eh", 10) // Create the EventHubNamespace object and expect the Reconcile to be created From 1646383ac69426d672ae8eb13de28f1645f9fcfc Mon Sep 17 00:00:00 2001 From: Erin Corson Date: Mon, 27 Apr 2020 13:45:24 -0600 Subject: [PATCH 6/6] cleanup after removing mocks --- controllers/suite_test.go | 168 +++++++++++++------------------------- 1 file changed, 57 insertions(+), 111 deletions(-) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 6c7e7c62eef..e64f5ce1e92 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -42,7 +42,6 @@ import ( resourcemanagerpsqlserver "github.com/Azure/azure-service-operator/pkg/resourcemanager/psql/server" resourcemanagerrediscaches "github.com/Azure/azure-service-operator/pkg/resourcemanager/rediscaches" resourcegroupsresourcemanager "github.com/Azure/azure-service-operator/pkg/resourcemanager/resourcegroups" - resourcemanagerstorages "github.com/Azure/azure-service-operator/pkg/resourcemanager/storages" resourcemanagerblobcontainer "github.com/Azure/azure-service-operator/pkg/resourcemanager/storages/blobcontainer" resourcemanagerstorageaccount "github.com/Azure/azure-service-operator/pkg/resourcemanager/storages/storageaccount" "github.com/Azure/azure-service-operator/pkg/resourcemanager/vm" @@ -122,72 +121,10 @@ func setup() error { } secretClient := k8sSecrets.New(k8sManager.GetClient()) - - var appInsightsManager resourcemanagerappinsights.ApplicationInsightsManager - var apiMgmtManager resourcemanagerapimgmt.APIManager - var cosmosDbManager resourcemanagercosmosdb.CosmosDBManager - var resourceGroupManager resourcegroupsresourcemanager.ResourceGroupManager - var eventHubManagers resourcemanagereventhub.EventHubManagers - var storageManagers resourcemanagerstorages.StorageManagers - var eventhubNamespaceClient resourcemanagereventhub.EventHubNamespaceManager - var sqlServerManager resourcemanagersqlserver.SqlServerManager - var sqlDbManager resourcemanagersqldb.SqlDbManager - var sqlFirewallRuleManager resourcemanagersqlfirewallrule.SqlFirewallRuleManager - var sqlFailoverGroupManager resourcemanagersqlfailovergroup.SqlFailoverGroupManager - var sqlUserManager resourcemanagersqluser.SqlUserManager - var sqlActionManager resourcemanagersqlaction.SqlActionManager - var eventhubClient resourcemanagereventhub.EventHubManager - var psqlServerManager resourcemanagerpsqlserver.PostgreSQLServerManager - var psqlDatabaseManager resourcemanagerpsqldatabase.PostgreSQLDatabaseManager - var psqlFirewallRuleManager resourcemanagerpsqlfirewallrule.PostgreSQLFirewallRuleManager - var consumerGroupClient resourcemanagereventhub.ConsumerGroupManager - var sqlVNetRuleManager resourcemanagersqlvnetrule.SqlVNetRuleManager - - appInsightsManager = resourcemanagerappinsights.NewManager( - secretClient, - scheme.Scheme, - ) - cosmosDbManager = resourcemanagercosmosdb.NewAzureCosmosDBManager(secretClient) - apiMgmtManager = resourcemanagerapimgmt.NewManager() - resourceGroupManager = resourcegroupsresourcemanager.NewAzureResourceGroupManager() - eventHubManagers = resourcemanagereventhub.AzureEventHubManagers - storageManagers = resourcemanagerstorages.AzureStorageManagers - storageAccountManager := resourcemanagerstorageaccount.New() - blobContainerManager := resourcemanagerblobcontainer.New() + resourceGroupManager := resourcegroupsresourcemanager.NewAzureResourceGroupManager() keyVaultManager := resourcemanagerkeyvaults.NewAzureKeyVaultManager(k8sManager.GetScheme()) - keyVaultKeyManager := &resourcemanagerkeyvaults.KeyvaultKeyClient{ - KeyvaultClient: keyVaultManager, - } - - virtualNetworkManager := resourcemanagervnet.NewAzureVNetManager() - - eventhubClient = resourcemanagereventhub.NewEventhubClient(secretClient, scheme.Scheme) - psqlServerManager = resourcemanagerpsqlserver.NewPSQLServerClient(secretClient, k8sManager.GetScheme()) - psqlDatabaseManager = resourcemanagerpsqldatabase.NewPSQLDatabaseClient() - psqlFirewallRuleManager = resourcemanagerpsqlfirewallrule.NewPSQLFirewallRuleClient() - eventhubNamespaceClient = resourcemanagereventhub.NewEventHubNamespaceClient() - - sqlServerManager = resourcemanagersqlserver.NewAzureSqlServerManager( - secretClient, - scheme.Scheme, - ) - redisCacheManager := resourcemanagerrediscaches.NewAzureRedisCacheManager( - secretClient, - scheme.Scheme, - ) - sqlDbManager = resourcemanagersqldb.NewAzureSqlDbManager() - sqlFirewallRuleManager = resourcemanagersqlfirewallrule.NewAzureSqlFirewallRuleManager() - sqlVNetRuleManager = resourcemanagersqlvnetrule.NewAzureSqlVNetRuleManager() - sqlFailoverGroupManager = resourcemanagersqlfailovergroup.NewAzureSqlFailoverGroupManager( - secretClient, - scheme.Scheme, - ) - consumerGroupClient = resourcemanagereventhub.NewConsumerGroupClient() - sqlUserManager = resourcemanagersqluser.NewAzureSqlUserManager( - secretClient, - scheme.Scheme, - ) - sqlActionManager = resourcemanagersqlaction.NewAzureSqlActionManager(secretClient, scheme.Scheme) + eventhubClient := resourcemanagereventhub.NewEventhubClient(secretClient, scheme.Scheme) + consumerGroupClient := resourcemanagereventhub.NewConsumerGroupClient() timeout = time.Second * 780 @@ -209,8 +146,10 @@ func setup() error { err = (&KeyVaultKeyReconciler{ Reconciler: &AsyncReconciler{ - Client: k8sManager.GetClient(), - AzureClient: keyVaultKeyManager, + Client: k8sManager.GetClient(), + AzureClient: &resourcemanagerkeyvaults.KeyvaultKeyClient{ + KeyvaultClient: keyVaultManager, + }, Telemetry: telemetry.InitializeTelemetryDefault( "KeyVaultKey", ctrl.Log.WithName("controllers").WithName("KeyVaultKey"), @@ -225,8 +164,11 @@ func setup() error { err = (&AppInsightsReconciler{ Reconciler: &AsyncReconciler{ - Client: k8sManager.GetClient(), - AzureClient: appInsightsManager, + Client: k8sManager.GetClient(), + AzureClient: resourcemanagerappinsights.NewManager( + secretClient, + scheme.Scheme, + ), Telemetry: telemetry.InitializeTelemetryDefault( "AppInsights", ctrl.Log.WithName("controllers").WithName("AppInsights"), @@ -242,7 +184,7 @@ func setup() error { err = (&APIMAPIReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: apiMgmtManager, + AzureClient: resourcemanagerapimgmt.NewManager(), Telemetry: telemetry.InitializeTelemetryDefault( "ApiMgmt", ctrl.Log.WithName("controllers").WithName("ApiMgmt"), @@ -258,7 +200,7 @@ func setup() error { err = (&CosmosDBReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: cosmosDbManager, + AzureClient: resourcemanagercosmosdb.NewAzureCosmosDBManager(secretClient), Telemetry: telemetry.InitializeTelemetryDefault( "CosmosDB", ctrl.Log.WithName("controllers").WithName("CosmosDB"), @@ -305,8 +247,11 @@ func setup() error { err = (&RedisCacheReconciler{ Reconciler: &AsyncReconciler{ - Client: k8sManager.GetClient(), - AzureClient: redisCacheManager, + Client: k8sManager.GetClient(), + AzureClient: resourcemanagerrediscaches.NewAzureRedisCacheManager( + secretClient, + scheme.Scheme, + ), Telemetry: telemetry.InitializeTelemetryDefault( "RedisCache", ctrl.Log.WithName("controllers").WithName("RedisCache"), @@ -322,7 +267,7 @@ func setup() error { err = (&EventhubNamespaceReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: eventhubNamespaceClient, + AzureClient: resourcemanagereventhub.NewEventHubNamespaceClient(), Telemetry: telemetry.InitializeTelemetryDefault( "EventhubNamespace", ctrl.Log.WithName("controllers").WithName("EventhubNamespace"), @@ -353,8 +298,11 @@ func setup() error { err = (&AzureSqlServerReconciler{ Reconciler: &AsyncReconciler{ - Client: k8sManager.GetClient(), - AzureClient: sqlServerManager, + Client: k8sManager.GetClient(), + AzureClient: resourcemanagersqlserver.NewAzureSqlServerManager( + secretClient, + scheme.Scheme, + ), Telemetry: telemetry.InitializeTelemetryDefault( "AzureSqlServer", ctrl.Log.WithName("controllers").WithName("AzureSqlServer"), @@ -370,7 +318,7 @@ func setup() error { err = (&AzureSqlDatabaseReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: sqlDbManager, + AzureClient: resourcemanagersqldb.NewAzureSqlDbManager(), Telemetry: telemetry.InitializeTelemetryDefault( "AzureSqlDb", ctrl.Log.WithName("controllers").WithName("AzureSqlDb"), @@ -386,7 +334,7 @@ func setup() error { err = (&AzureSqlFirewallRuleReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: sqlFirewallRuleManager, + AzureClient: resourcemanagersqlfirewallrule.NewAzureSqlFirewallRuleManager(), Telemetry: telemetry.InitializeTelemetryDefault( "AzureSQLFirewallRuleOperator", ctrl.Log.WithName("controllers").WithName("AzureSQLFirewallRuleOperator"), @@ -402,7 +350,7 @@ func setup() error { err = (&AzureSQLVNetRuleReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: sqlVNetRuleManager, + AzureClient: resourcemanagersqlvnetrule.NewAzureSqlVNetRuleManager(), Telemetry: telemetry.InitializeTelemetryDefault( "AzureSQLVNetRuleOperator", ctrl.Log.WithName("controllers").WithName("AzureSQLVNetRuleOperator"), @@ -417,8 +365,11 @@ func setup() error { err = (&AzureSqlFailoverGroupReconciler{ Reconciler: &AsyncReconciler{ - Client: k8sManager.GetClient(), - AzureClient: sqlFailoverGroupManager, + Client: k8sManager.GetClient(), + AzureClient: resourcemanagersqlfailovergroup.NewAzureSqlFailoverGroupManager( + secretClient, + scheme.Scheme, + ), Telemetry: telemetry.InitializeTelemetryDefault( "AzureSqlFailoverGroup", ctrl.Log.WithName("controllers").WithName("AzureSqlFailoverGroup"), @@ -433,8 +384,11 @@ func setup() error { err = (&AzureSQLUserReconciler{ Reconciler: &AsyncReconciler{ - Client: k8sManager.GetClient(), - AzureClient: sqlUserManager, + Client: k8sManager.GetClient(), + AzureClient: resourcemanagersqluser.NewAzureSqlUserManager( + secretClient, + scheme.Scheme, + ), Telemetry: telemetry.InitializeTelemetryDefault( "AzureSqlUser", ctrl.Log.WithName("controllers").WithName("AzureSqlUser"), @@ -450,7 +404,7 @@ func setup() error { err = (&VirtualNetworkReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: virtualNetworkManager, + AzureClient: resourcemanagervnet.NewAzureVNetManager(), Telemetry: telemetry.InitializeTelemetryDefault( "VirtualNetwork", ctrl.Log.WithName("controllers").WithName("VirtualNetwork"), @@ -523,7 +477,7 @@ func setup() error { err = (&AzureSqlActionReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: sqlActionManager, + AzureClient: resourcemanagersqlaction.NewAzureSqlActionManager(secretClient, scheme.Scheme), Telemetry: telemetry.InitializeTelemetryDefault( "AzureSqlAction", ctrl.Log.WithName("controllers").WithName("AzureSqlAction"), @@ -539,7 +493,7 @@ func setup() error { err = (&BlobContainerReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: blobContainerManager, + AzureClient: resourcemanagerblobcontainer.New(), Telemetry: telemetry.InitializeTelemetryDefault( "BlobContainer", ctrl.Log.WithName("controllers").WithName("BlobContainer"), @@ -622,7 +576,7 @@ func setup() error { err = (&PostgreSQLServerReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: psqlServerManager, + AzureClient: resourcemanagerpsqlserver.NewPSQLServerClient(secretClient, k8sManager.GetScheme()), Telemetry: telemetry.InitializeTelemetryDefault( "PostgreSQLServer", ctrl.Log.WithName("controllers").WithName("PostgreSQLServer"), @@ -638,7 +592,7 @@ func setup() error { err = (&PostgreSQLDatabaseReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: psqlDatabaseManager, + AzureClient: resourcemanagerpsqldatabase.NewPSQLDatabaseClient(), Telemetry: telemetry.InitializeTelemetryDefault( "PostgreSQLDatabase", ctrl.Log.WithName("controllers").WithName("PostgreSQLDatabase"), @@ -654,7 +608,7 @@ func setup() error { err = (&PostgreSQLFirewallRuleReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: psqlFirewallRuleManager, + AzureClient: resourcemanagerpsqlfirewallrule.NewPSQLFirewallRuleClient(), Telemetry: telemetry.InitializeTelemetryDefault( "PostgreSQLFirewallRule", ctrl.Log.WithName("controllers").WithName("PostgreSQLFirewallRule"), @@ -670,7 +624,7 @@ func setup() error { err = (&StorageAccountReconciler{ Reconciler: &AsyncReconciler{ Client: k8sManager.GetClient(), - AzureClient: storageAccountManager, + AzureClient: resourcemanagerstorageaccount.New(), Telemetry: telemetry.InitializeTelemetryDefault( "StorageAccount", ctrl.Log.WithName("controllers").WithName("StorageAccount"), @@ -703,26 +657,18 @@ func setup() error { } tc = TestContext{ - k8sClient: k8sClient, - secretClient: secretClient, - resourceGroupName: resourceGroupName, - resourceGroupLocation: resourcegroupLocation, - keyvaultName: keyvaultName, - eventHubManagers: eventHubManagers, - eventhubClient: eventhubClient, - resourceGroupManager: resourceGroupManager, - redisCacheManager: redisCacheManager, - sqlServerManager: sqlServerManager, - sqlDbManager: sqlDbManager, - sqlFirewallRuleManager: sqlFirewallRuleManager, - sqlFailoverGroupManager: sqlFailoverGroupManager, - sqlUserManager: sqlUserManager, - storageManagers: storageManagers, - keyVaultManager: keyVaultManager, - timeout: timeout, - timeoutFast: time.Minute * 3, - retry: time.Second * 3, - consumerGroupClient: consumerGroupClient, + k8sClient: k8sClient, + secretClient: secretClient, + resourceGroupName: resourceGroupName, + resourceGroupLocation: resourcegroupLocation, + keyvaultName: keyvaultName, + eventhubClient: eventhubClient, + resourceGroupManager: resourceGroupManager, + keyVaultManager: keyVaultManager, + timeout: timeout, + timeoutFast: time.Minute * 3, + retry: time.Second * 3, + consumerGroupClient: consumerGroupClient, } log.Println("Creating KV:", keyvaultName)