Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a new operator for SQL Firewall Rules, cleaned up the resource client #239

Merged
merged 9 commits into from
Sep 30, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ CRD_OPTIONS ?= "crd:trivialVersions=true"

all: manager

# Generate test certs for development
generate-test-certs:
echo "[req]" > config.txt
echo "distinguished_name = req_distinguished_name" >> config.txt
echo "[req_distinguished_name]" >> config.txt
echo "[SAN]" >> config.txt
echo "subjectAltName=DNS:azureoperator-webhook-service.azureoperator-system.svc.cluster.local" >> config.txt
openssl req -x509 -days 730 -out tls.crt -keyout tls.key -newkey rsa:4096 -subj "/CN=azureoperator-webhook-service.azureoperator-system" -config config.txt -nodes
rm -rf /tmp/k8s-webhook-server
mkdir -p /tmp/k8s-webhook-server/serving-certs
mv tls.* /tmp/k8s-webhook-server/serving-certs/

# Run API unittests
api-test: generate fmt vet manifests
TEST_USE_EXISTING_CLUSTER=false go test -v -coverprofile=coverage.txt -covermode count ./api/... 2>&1 | tee testlogs.txt
Expand Down
2 changes: 1 addition & 1 deletion api/v1/sqlfirewallrule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import (
type SqlFirewallRuleSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Location string `json:"location"`
ResourceGroup string `json:"resourcegroup,omitempty"`
Server string `json:"server"`
StartIPAddress string `json:"startipaddress,omitempty"`
EndIPAddress string `json:"endipaddress,omitempty"`
}
Expand Down
8 changes: 5 additions & 3 deletions config/samples/azure_v1_sqlfirewallrule.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
apiVersion: azure.microsoft.com/v1
kind: SqlFirewallRule
metadata:
name: sqlfirewallrule-sample
name: sample-rule1
WilliamMortlMicrosoft marked this conversation as resolved.
Show resolved Hide resolved
spec:
# Add fields here
foo: bar
resourcegroup: resourcegroup-sample-1907
server: sqlserver-sample
startipaddress: 1.1.1.1
endipaddress: 6.6.6.6
160 changes: 136 additions & 24 deletions controllers/sqlfirewallrule_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,30 @@ package controllers
import (
"context"
"fmt"
"time"

"github.com/Azure/azure-service-operator/pkg/errhelp"
helpers "github.com/Azure/azure-service-operator/pkg/helpers"
sql "github.com/Azure/azure-service-operator/pkg/resourcemanager/sqlclient"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

azurev1 "github.com/Azure/azure-service-operator/api/v1"
)

const SQLFirewallRuleFinalizerName = "sqlfirewallrule.finalizers.azure.com"

// SqlFirewallRuleReconciler reconciles a SqlFirewallRule object
type SqlFirewallRuleReconciler struct {
client.Client
Log logr.Logger
Recorder record.EventRecorder
Scheme *runtime.Scheme
}

// +kubebuilder:rbac:groups=azure.microsoft.com,resources=sqlfirewallrules,verbs=get;list;watch;create;update;patch;delete
Expand All @@ -53,6 +62,51 @@ func (r *SqlFirewallRuleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, er
return ctrl.Result{}, client.IgnoreNotFound(err)
}

if helpers.IsBeingDeleted(&instance) {
if helpers.HasFinalizer(&instance, SQLFirewallRuleFinalizerName) {
if err := r.deleteExternal(&instance); err != nil {
log.Info("Delete SqlFirewallRule failed with ", "error", err.Error())
return ctrl.Result{}, err
}

helpers.RemoveFinalizer(&instance, SQLFirewallRuleFinalizerName)
if err := r.Update(context.Background(), &instance); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}

if !helpers.HasFinalizer(&instance, SQLFirewallRuleFinalizerName) {
if err := r.addFinalizer(&instance); err != nil {
log.Info("Adding SqlFirewallRule finalizer failed with ", "error", err.Error())
return ctrl.Result{}, err
}
}

if !instance.IsSubmitted() {
r.Recorder.Event(&instance, "Normal", "Submitting", "starting resource reconciliation for SqlFirewallRule")
if err := r.reconcileExternal(&instance); err != nil {

catch := []string{
errhelp.ParentNotFoundErrorCode,
errhelp.ResourceGroupNotFoundErrorCode,
errhelp.NotFoundErrorCode,
errhelp.AsyncOpIncompleteError,
}
if azerr, ok := err.(*errhelp.AzureError); ok {
if helpers.ContainsString(catch, azerr.Type) {
log.Info("Got ignorable error", "type", azerr.Type)
return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, nil
}
}
return ctrl.Result{}, fmt.Errorf("error reconciling sql firewall rule in azure: %v", err)
}
return ctrl.Result{}, nil
}

r.Recorder.Event(&instance, "Normal", "Provisioned", "sqlfirewallrule "+instance.ObjectMeta.Name+" provisioned ")

return ctrl.Result{}, nil
}

Expand All @@ -63,49 +117,107 @@ func (r *SqlFirewallRuleReconciler) SetupWithManager(mgr ctrl.Manager) error {
}

func (r *SqlFirewallRuleReconciler) reconcileExternal(instance *azurev1.SqlFirewallRule) error {
// ctx := context.Background()
// location := instance.Spec.Location
// name := instance.ObjectMeta.Name
// groupName := instance.Spec.ResourceGroupName
ctx := context.Background()
ruleName := instance.ObjectMeta.Name
server := instance.Spec.Server
groupName := instance.Spec.ResourceGroup
startIP := instance.Spec.StartIPAddress
endIP := instance.Spec.EndIPAddress

sdkClient := sql.GoSDKClient{
Ctx: ctx,
ResourceGroupName: groupName,
ServerName: server,
}

// // write information back to instance
// instance.Status.Provisioning = true
r.Log.Info("Calling createorupdate SQL firewall rule")

// if err := r.Status().Update(ctx, instance); err != nil {
// r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance")
// }
//get owner instance of SqlServer
r.Recorder.Event(instance, "Normal", "UpdatingOwner", "Updating owner SqlServer instance")
var ownerInstance azurev1.SqlServer
sqlServerNamespacedName := types.NamespacedName{Name: server, Namespace: instance.Namespace}
err := r.Get(ctx, sqlServerNamespacedName, &ownerInstance)

//err = CreateResource()
// if err != nil {
if err != nil {
//log error and kill it, as the parent might not exist in the cluster. It could have been created elsewhere or through the portal directly
r.Recorder.Event(instance, "Warning", "Failed", "Unable to get owner instance of SqlServer")
} else {
r.Recorder.Event(instance, "Normal", "OwnerAssign", "Got owner instance of Sql Server and assigning controller reference now")
innerErr := controllerutil.SetControllerReference(&ownerInstance, instance, r.Scheme)
if innerErr != nil {
r.Recorder.Event(instance, "Warning", "Failed", "Unable to set controller reference to SqlServer")
}
r.Recorder.Event(instance, "Normal", "OwnerAssign", "Owner instance assigned successfully")
}

// }
// write information back to instance
if updateerr := r.Update(ctx, instance); updateerr != nil {
WilliamMortlMicrosoft marked this conversation as resolved.
Show resolved Hide resolved
r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance")
}

// instance.Status.Provisioning = false
// instance.Status.Provisioned = true
// actually add the firewall rule
_, err = sdkClient.CreateOrUpdateSQLFirewallRule(ruleName, startIP, endIP)
if err != nil {
if errhelp.IsAsynchronousOperationNotComplete(err) || errhelp.IsGroupNotFound(err) {
r.Log.Info("Async operation not complete or group not found")
instance.Status.Provisioning = true
if errup := r.Status().Update(ctx, instance); errup != nil {
r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance")
}
}

return errhelp.NewAzureError(err)
}

_, err = sdkClient.GetSQLFirewallRule(ruleName)
if err != nil {
return errhelp.NewAzureError(err)
}

instance.Status.Provisioning = false
instance.Status.Provisioned = true

// if err = r.Status().Update(ctx, instance); err != nil {
// r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance")
// }
if err = r.Status().Update(ctx, instance); err != nil {
r.Recorder.Event(instance, "Warning", "Failed", "Unable to update instance")
}

return nil
}

func (r *SqlFirewallRuleReconciler) deleteExternal(instance *azurev1.SqlFirewallRule) error {
// ctx := context.Background()
name := instance.ObjectMeta.Name
// groupName := instance.Spec.ResourceGroupName
// // delete resource
ctx := context.Background()
ruleName := instance.ObjectMeta.Name
server := instance.Spec.Server
groupName := instance.Spec.ResourceGroup

// create the Go SDK client with relevant info
sdk := sql.GoSDKClient{
Ctx: ctx,
ResourceGroupName: groupName,
ServerName: server,
}

r.Recorder.Event(instance, "Normal", "Deleted", name+" deleted")
r.Log.Info(fmt.Sprintf("deleting external resource: group/%s/server/%s/firewallrule/%s"+groupName, server, ruleName))
err := sdk.DeleteSQLFirewallRule(ruleName)
if err != nil {
if errhelp.IsStatusCode204(err) {
r.Recorder.Event(instance, "Warning", "DoesNotExist", "Resource to delete does not exist")
return nil
}

r.Recorder.Event(instance, "Warning", "Failed", "Couldn't delete resouce in azure")
return err
}
r.Recorder.Event(instance, "Normal", "Deleted", ruleName+" deleted")
return nil
}

func (r *SqlFirewallRuleReconciler) addFinalizer(instance *azurev1.SqlFirewallRule) error {
helpers.AddFinalizer(instance, SQLServerFinalizerName)
helpers.AddFinalizer(instance, SQLFirewallRuleFinalizerName)
err := r.Update(context.Background(), instance)
if err != nil {
return fmt.Errorf("failed to update finalizer: %v", err)
}
r.Recorder.Event(instance, "Normal", "Updated", fmt.Sprintf("finalizer %s added", SQLServerFinalizerName))
r.Recorder.Event(instance, "Normal", "Updated", fmt.Sprintf("finalizer %s added", SQLFirewallRuleFinalizerName))
return nil
}
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@ k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:Ixke
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA=
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/apimachinery v0.0.0-20190814100815-533d101be9a6 h1:g3kHsVIF7tLDtdP1RPw/Kuy3ANzPG5QPVwQ52qYkI0U=
k8s.io/apimachinery v0.0.0-20190831074630-461753078381 h1:gySvpxrHatsZtG3qOkyPIHjWY7D5ogkrrWnD7+5/RGs=
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible h1:U5Bt+dab9K8qaUmXINrkXO135kA11/i5Kg1RUydgaMQ=
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
Expand Down
6 changes: 4 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,10 @@ func main() {
os.Exit(1)
}
if err = (&controllers.SqlFirewallRuleReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("SqlFirewallRule"),
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("SqlFirewallRule"),
Recorder: mgr.GetEventRecorderFor("SqlFirewallRule-controller"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "SqlFirewallRule")
os.Exit(1)
Expand Down
Loading