diff --git a/pkg/resourcemanager/psql/firewallrule/client.go b/pkg/resourcemanager/psql/firewallrule/client.go deleted file mode 100644 index 3c2148b1ed7..00000000000 --- a/pkg/resourcemanager/psql/firewallrule/client.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package server - -import ( - "context" - "fmt" - - psql "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" - "github.com/Azure/azure-service-operator/api/v1alpha1" - 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" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/config" - "github.com/Azure/azure-service-operator/pkg/resourcemanager/iam" - "github.com/Azure/go-autorest/autorest/to" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" -) - -type PSQLFirewallRuleClient struct { -} - -func NewPSQLFirewallRuleClient() *PSQLFirewallRuleClient { - return &PSQLFirewallRuleClient{} -} - -func getPSQLFirewallRulesClient() psql.FirewallRulesClient { - firewallRulesClient := psql.NewFirewallRulesClientWithBaseURI(config.BaseURI(), config.SubscriptionID()) - a, _ := iam.GetResourceManagementAuthorizer() - firewallRulesClient.Authorizer = a - firewallRulesClient.AddToUserAgent(config.UserAgent()) - return firewallRulesClient -} - -func (p *PSQLFirewallRuleClient) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := p.convert(obj) - if err != nil { - return true, err - } - - client := getPSQLFirewallRulesClient() - - instance.Status.Provisioning = true - // Check if this server already exists and its state if it does. This is required - // to overcome the issue with the lack of idempotence of the Create call - - firewallrule, err := p.GetFirewallRule(ctx, instance.Spec.ResourceGroup, instance.Spec.Server, instance.Name) - if err == nil { - instance.Status.Provisioned = true - instance.Status.Provisioning = false - instance.Status.State = firewallrule.Status - return true, nil - } - - future, err := p.CreateFirewallRule( - ctx, - instance.Spec.ResourceGroup, - instance.Spec.Server, - instance.Name, - instance.Spec.StartIPAddress, - instance.Spec.EndIPAddress, - ) - - if err != nil { - // let the user know what happened - instance.Status.Message = err.Error() - instance.Status.Provisioning = false - // errors we expect might happen that we are ok with waiting for - catch := []string{ - errhelp.ResourceGroupNotFoundErrorCode, - errhelp.ParentNotFoundErrorCode, - errhelp.NotFoundErrorCode, - errhelp.AsyncOpIncompleteError, - } - - azerr := errhelp.NewAzureErrorAzureError(err) - if helpers.ContainsString(catch, azerr.Type) { - // most of these error technically mean the resource is actually not provisioning - switch azerr.Type { - case errhelp.AsyncOpIncompleteError: - instance.Status.Provisioning = true - } - // reconciliation is not done but error is acceptable - return false, nil - } - // reconciliation not done and we don't know what happened - return false, err - } - - instance.Status.State = future.Status() - - firewallrule, err = future.Result(client) - if err != nil { - // let the user know what happened - instance.Status.Message = err.Error() - instance.Status.Provisioning = false - // errors we expect might happen that we are ok with waiting for - catch := []string{ - errhelp.ResourceGroupNotFoundErrorCode, - errhelp.ParentNotFoundErrorCode, - errhelp.NotFoundErrorCode, - errhelp.AsyncOpIncompleteError, - } - - azerr := errhelp.NewAzureErrorAzureError(err) - if helpers.ContainsString(catch, azerr.Type) { - // most of these error technically mean the resource is actually not provisioning - switch azerr.Type { - case errhelp.AsyncOpIncompleteError: - instance.Status.Provisioning = true - } - // reconciliation is not done but error is acceptable - return false, nil - } - // reconciliation not done and we don't know what happened - return false, err - } - - instance.Status.State = firewallrule.Status - - if instance.Status.Provisioning { - instance.Status.Provisioned = true - instance.Status.Provisioning = false - instance.Status.Message = resourcemanager.SuccessMsg - } else { - instance.Status.Provisioned = false - instance.Status.Provisioning = true - } - - return true, nil -} - -func (p *PSQLFirewallRuleClient) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { - instance, err := p.convert(obj) - if err != nil { - return true, err - } - - status, err := p.DeleteFirewallRule(ctx, instance.Spec.ResourceGroup, instance.Spec.Server, instance.Name) - if err != nil { - if !errhelp.IsAsynchronousOperationNotComplete(err) { - return true, err - } - } - instance.Status.State = status - - if err == nil { - if status != "InProgress" { - return false, nil - } - } - - return true, nil -} - -func (p *PSQLFirewallRuleClient) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { - - instance, err := p.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 (g *PSQLFirewallRuleClient) GetStatus(obj runtime.Object) (*v1alpha1.ASOStatus, error) { - instance, err := g.convert(obj) - if err != nil { - return nil, err - } - return &instance.Status, nil -} - -func (p *PSQLFirewallRuleClient) 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 (p *PSQLFirewallRuleClient) CreateFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string, startip string, endip string) (future psql.FirewallRulesCreateOrUpdateFuture, err error) { - - client := getPSQLFirewallRulesClient() - - firewallRuleProperties := psql.FirewallRuleProperties{ - StartIPAddress: to.StringPtr(startip), - EndIPAddress: to.StringPtr(endip), - } - - future, err = client.CreateOrUpdate( - ctx, - resourcegroup, - servername, - firewallrulename, - psql.FirewallRule{ - FirewallRuleProperties: &firewallRuleProperties, - }, - ) - return future, err -} - -func (p *PSQLFirewallRuleClient) DeleteFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string) (status string, err error) { - - client := getPSQLFirewallRulesClient() - - _, err = client.Get(ctx, resourcegroup, servername, firewallrulename) - if err == nil { // FW rule present, so go ahead and delete - future, err := client.Delete(ctx, resourcegroup, servername, firewallrulename) - return future.Status(), err - } - // FW rule not present so return success anyway - return "Firewall Rule not present", nil - -} - -func (p *PSQLFirewallRuleClient) GetFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string) (firewall psql.FirewallRule, err error) { - - client := getPSQLFirewallRulesClient() - - return client.Get(ctx, resourcegroup, servername, firewallrulename) -} diff --git a/pkg/resourcemanager/psql/firewallrule/firewallrule.go b/pkg/resourcemanager/psql/firewallrule/firewallrule.go new file mode 100644 index 00000000000..bddd1e9e68f --- /dev/null +++ b/pkg/resourcemanager/psql/firewallrule/firewallrule.go @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package server + +import ( + "context" + "net/http" + + 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" + "github.com/Azure/go-autorest/autorest/to" +) + +type PSQLFirewallRuleClient struct { +} + +func NewPSQLFirewallRuleClient() *PSQLFirewallRuleClient { + return &PSQLFirewallRuleClient{} +} + +func getPSQLFirewallRulesClient() (psql.FirewallRulesClient, error) { + firewallRulesClient := psql.NewFirewallRulesClientWithBaseURI(config.BaseURI(), config.SubscriptionID()) + a, err := iam.GetResourceManagementAuthorizer() + if err != nil { + return psql.FirewallRulesClient{}, err + } + firewallRulesClient.Authorizer = a + firewallRulesClient.AddToUserAgent(config.UserAgent()) + return firewallRulesClient, err +} + +func (p *PSQLFirewallRuleClient) CreateFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string, startip string, endip string) (*http.Response, error) { + + client, err := getPSQLFirewallRulesClient() + if err != nil { + return &http.Response{ + StatusCode: 500, + }, err + } + + firewallRuleProperties := psql.FirewallRuleProperties{ + StartIPAddress: to.StringPtr(startip), + EndIPAddress: to.StringPtr(endip), + } + + future, err := client.CreateOrUpdate( + ctx, + resourcegroup, + servername, + firewallrulename, + psql.FirewallRule{ + FirewallRuleProperties: &firewallRuleProperties, + }, + ) + if err != nil { + return &http.Response{ + StatusCode: 500, + }, err + } + + return future.GetResult(client) +} + +func (p *PSQLFirewallRuleClient) DeleteFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string) (status string, err error) { + + client, err := getPSQLFirewallRulesClient() + if err != nil { + return "", err + } + + _, err = client.Get(ctx, resourcegroup, servername, firewallrulename) + if err == nil { // FW rule present, so go ahead and delete + future, err := client.Delete(ctx, resourcegroup, servername, firewallrulename) + return future.Status(), err + } + + // FW rule not present so return success anyway + return "Firewall Rule not present", nil +} + +func (p *PSQLFirewallRuleClient) GetFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string) (firewall psql.FirewallRule, err error) { + + client, err := getPSQLFirewallRulesClient() + if err != nil { + return psql.FirewallRule{}, err + } + + return client.Get(ctx, resourcegroup, servername, firewallrulename) +} diff --git a/pkg/resourcemanager/psql/firewallrule/firewallrule_manager.go b/pkg/resourcemanager/psql/firewallrule/firewallrule_manager.go new file mode 100644 index 00000000000..305b9f7293e --- /dev/null +++ b/pkg/resourcemanager/psql/firewallrule/firewallrule_manager.go @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package server + +import ( + "context" + "net/http" + + psql "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" + "github.com/Azure/azure-service-operator/pkg/resourcemanager" +) + +type PostgreSQLFirewallRuleManager interface { + CreateFirewallRule(ctx context.Context, + resourcegroup string, + servername string, + firewallrulename string, + startip string, + endip string) (*http.Response, error) + + GetFirewallRule(ctx context.Context, + resourcegroup string, + servername string, + firewallrulename string) (psql.FirewallRule, error) + + DeleteFirewallRule(ctx context.Context, + resourcegroup string, + servername string, + firewallrulename string) (string, error) + + // also embed async client methods + resourcemanager.ARMClient +} diff --git a/pkg/resourcemanager/psql/firewallrule/firewallrule_reconcile.go b/pkg/resourcemanager/psql/firewallrule/firewallrule_reconcile.go new file mode 100644 index 00000000000..076b080b86f --- /dev/null +++ b/pkg/resourcemanager/psql/firewallrule/firewallrule_reconcile.go @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package server + +import ( + "context" + "fmt" + + "github.com/Azure/azure-service-operator/api/v1alpha1" + 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 makes sure a Postgres firewall rule exists +func (p *PSQLFirewallRuleClient) Ensure(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { + instance, err := p.convert(obj) + if err != nil { + return true, err + } + + getRule, err := p.GetFirewallRule(ctx, instance.Spec.ResourceGroup, instance.Spec.Server, instance.Name) + if err == nil { + instance.Status.Message = resourcemanager.SuccessMsg + instance.Status.ResourceId = *getRule.ID + instance.Status.State = getRule.Status + instance.Status.Provisioned = true + instance.Status.Provisioning = false + return true, nil + } + + instance.Status.Provisioning = true + resp, err := p.CreateFirewallRule( + ctx, + instance.Spec.ResourceGroup, + instance.Spec.Server, + instance.Name, + instance.Spec.StartIPAddress, + instance.Spec.EndIPAddress, + ) + if err != nil { + instance.Status.Message = errhelp.StripErrorIDs(err) + azerr := errhelp.NewAzureErrorAzureError(err) + + catchInProgress := []string{ + errhelp.AsyncOpIncompleteError, + errhelp.AlreadyExists, + } + catchKnownError := []string{ + errhelp.ResourceGroupNotFoundErrorCode, + errhelp.ParentNotFoundErrorCode, + errhelp.NotFoundErrorCode, + } + + // assertion that a 404 error implies that the Postgres server hasn't been provisioned yet + if resp != nil && resp.StatusCode == 404 { + instance.Status.Message = fmt.Sprintf("Waiting for Postgres server %s to provision", instance.Spec.Server) + instance.Status.Provisioning = false + return false, nil + } + + // handle the errors + if helpers.ContainsString(catchInProgress, azerr.Type) { + instance.Status.Message = "Postgres database exists but may not be ready" + return false, nil + } else if helpers.ContainsString(catchKnownError, azerr.Type) { + instance.Status.Provisioning = false + return false, nil + } else { + + // serious error occured, end reconcilliation and mark it as failed + instance.Status.Message = fmt.Sprintf("Error occurred creating the Postgres server: %s", errhelp.StripErrorIDs(err)) + instance.Status.Provisioned = false + instance.Status.Provisioning = false + instance.Status.FailedProvisioning = true + return true, nil + } + } + + return false, nil +} + +// Delete removes a Postgres firewall rule +func (p *PSQLFirewallRuleClient) Delete(ctx context.Context, obj runtime.Object, opts ...resourcemanager.ConfigOption) (bool, error) { + instance, err := p.convert(obj) + if err != nil { + return true, err + } + + status, err := p.DeleteFirewallRule(ctx, instance.Spec.ResourceGroup, instance.Spec.Server, instance.Name) + if err != nil { + if !errhelp.IsAsynchronousOperationNotComplete(err) { + return true, err + } + } + instance.Status.State = status + + if err == nil { + if status != "InProgress" { + return false, nil + } + } + + return true, nil +} + +// GetParents gets the parents +func (p *PSQLFirewallRuleClient) GetParents(obj runtime.Object) ([]resourcemanager.KubeParent, error) { + + instance, err := p.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 +} + +// GetStatus retrieves the status +func (p *PSQLFirewallRuleClient) GetStatus(obj runtime.Object) (*v1alpha1.ASOStatus, error) { + instance, err := p.convert(obj) + if err != nil { + return nil, err + } + return &instance.Status, nil +} + +func (p *PSQLFirewallRuleClient) 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 +} diff --git a/pkg/resourcemanager/psql/firewallrule/manager.go b/pkg/resourcemanager/psql/firewallrule/manager.go deleted file mode 100644 index 3883ab04088..00000000000 --- a/pkg/resourcemanager/psql/firewallrule/manager.go +++ /dev/null @@ -1,21 +0,0 @@ -// 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 PostgreSQLFirewallRuleManager interface { - //convert(obj runtime.Object) (*v1alpha1.PostgreSQLFirewallRule, error) - - CreateFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string, startip string, endip string) (psql.FirewallRulesCreateOrUpdateFuture, error) - DeleteFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string) (string, error) - GetFirewallRule(ctx context.Context, resourcegroup string, servername string, firewallrulename string) (psql.FirewallRule, error) - // also embed async client methods - resourcemanager.ARMClient -}