Skip to content

Commit

Permalink
Refactor controller architecture and improve package seperation (#27)
Browse files Browse the repository at this point in the history
This patch refactors the controller architecture, improves schema handling,
and enhances the overall structure of the codebase. It separates concerns
more clearly, and introduces a more flexible **dynamiccontroller,**
implemenatation and improves various aspects of **CRD** management
and schema processing. In brief:

- Restructure packages for clearer separation of concerns:
  - Move `ResourceGroup` controller to its own controller/resourcegroup
  - Separate `Instance` controller logic from `ResourceGroup` and move to
    controller/instance
  - Move some schema related functionality in resourcegroup package
- Refactor `DynamicController` to use abstract Handler interface:
  - Remove direct dependency on graph execution logic
  - Enhance generic reconciliation process
  - Addedmetrics for `DynamicController` operations
- Enhance schema processing and CRD generation:
  - Move CRD builds/creation to `ResourceGroup` builder
  - Improve schema validation and transformation logic
  - Add `observedGeneration` to default status fields in instance schemas
- Add wait for ready functionality to the CRD manager (dealing with some eventual
  consistency issues)
- Update client initialization to return multiple clients
- Improve error handling, logging, and reporting throughout the codebase

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
  • Loading branch information
a-hilaly authored Sep 23, 2024
1 parent 00ffe90 commit cfba7b7
Show file tree
Hide file tree
Showing 27 changed files with 1,050 additions and 1,360 deletions.
52 changes: 32 additions & 20 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"

xv1alpha1 "github.com/aws-controllers-k8s/symphony/api/v1alpha1"
"github.com/aws-controllers-k8s/symphony/internal/controller"
"github.com/aws-controllers-k8s/symphony/internal/crd"
resourcegroupctrl "github.com/aws-controllers-k8s/symphony/internal/controller/resourcegroup"
"github.com/aws-controllers-k8s/symphony/internal/dynamiccontroller"
"github.com/aws-controllers-k8s/symphony/internal/kubernetes"
"github.com/aws-controllers-k8s/symphony/internal/resourcegroup"
"github.com/aws-controllers-k8s/symphony/internal/typesystem/celextractor"
//+kubebuilder:scaffold:imports
)

Expand Down Expand Up @@ -91,8 +92,10 @@ func main() {
ctrl.SetLogger(rootLogger)

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
Metrics: metricsserver.Options{BindAddress: metricsAddr},
Scheme: scheme,
Metrics: metricsserver.Options{
BindAddress: metricsAddr,
},
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "6f0f64a5.symphony.k8s.aws",
Expand All @@ -112,21 +115,13 @@ func main() {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
// d := mgr.GetCache().WaitForCacheSync(context.Background())

crdClient, err := kubernetes.NewAPIExtensionsClientSet()
if err != nil {
setupLog.Error(err, "unable to create crd client")
os.Exit(1)
}

dynamicClient, err := kubernetes.NewDynamicClient()
kConfig, _, dynamicClient, crdClient, err := kubernetes.NewClients()
if err != nil {
setupLog.Error(err, "unable to create dynamic client")
setupLog.Error(err, "unable to create clients")
os.Exit(1)
}

crdManager := crd.NewManager(crdClient, rootLogger)
crdManager := kubernetes.NewCRDClient(crdClient, rootLogger)

dc := dynamiccontroller.NewDynamicController(rootLogger, dynamiccontroller.Config{
Workers: dynamicControllerConcurrentReconciles,
Expand All @@ -136,12 +131,23 @@ func main() {
QueueMaxRetries: 20,
}, dynamicClient)

reconciler := controller.NewResourceGroupReconciler(
resourceGroupGraphBuilder, err := resourcegroup.NewResourceGroupBuilder(
kConfig,
celextractor.NewCELExpressionParser(),
)
if err != nil {
setupLog.Error(err, "unable to create resource group graph builder")
os.Exit(1)
}

reconciler := resourcegroupctrl.NewResourceGroupReconciler(
rootLogger,
mgr,
dynamicClient,
allowCRDDeletion,
crdManager,
dc,
resourceGroupGraphBuilder,
)
err = ctrl.NewControllerManagedBy(
mgr,
Expand Down Expand Up @@ -172,8 +178,14 @@ func main() {
os.Exit(1)
}

if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
ctx := ctrl.SetupSignalHandler()
go func() {
if err := mgr.Start(ctx); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}()

<-ctx.Done()

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
apiVersion: x.symphony.k8s.aws/v1alpha1
kind: Application
metadata:
name: my-deployment-and-service3
name: my-deployment-and-service6
namespace: test
spec:
name: app3
name: app7
image: terraform6
File renamed without changes.
94 changes: 94 additions & 0 deletions internal/controller/instance/controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package instance

import (
"context"
"fmt"

"github.com/go-logr/logr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
ctrl "sigs.k8s.io/controller-runtime"

"github.com/aws-controllers-k8s/symphony/internal/k8smetadata"
"github.com/aws-controllers-k8s/symphony/internal/resourcegroup"
)

// Controller structure
type Controller struct {
log logr.Logger
gvr schema.GroupVersionResource
client dynamic.Interface
rg *resourcegroup.ResourceGroup
instanceLabeler k8smetadata.Labeler
}

// NewController creates a new Controller instance
func NewController(
log logr.Logger,
gvr schema.GroupVersionResource,
rg *resourcegroup.ResourceGroup,
client dynamic.Interface,
instanceLabeler k8smetadata.Labeler,
) *Controller {
return &Controller{
log: log,
gvr: gvr,
client: client,
rg: rg,
instanceLabeler: instanceLabeler,
}
}

// NewGraphExecReconciler is the main reconciliation loop for the Controller
func (c *Controller) Reconcile(ctx context.Context, req ctrl.Request) error {
namespace, name := getNamespaceName(req)

log := c.log.WithValues("namespace", namespace, "name", name)

instance, err := c.client.Resource(c.gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
log.Info("Instance not found, it may have been deleted")
return nil
}
log.Error(err, "Failed to get instance")
return nil
}

rgRuntime, err := c.rg.NewRuntime(instance)
if err != nil {
return fmt.Errorf("failed to create runtime resource group: %w", err)
}

instanceSubResourcesLabeler, err := k8smetadata.NewInstanceLabeler(instance).Merge(c.instanceLabeler)
if err != nil {
return fmt.Errorf("failed to create instance sub-resources labeler: %w", err)
}

graphExecReconciler := &InstanceGraphReconciler{
log: log,
gvr: c.gvr,
client: c.client,
rg: c.rg,
runtime: rgRuntime,
originalRequest: req,
instanceLabeler: c.instanceLabeler,
instanceSubResourcesLabeler: instanceSubResourcesLabeler,
}
return graphExecReconciler.Reconcile(ctx)
}
Loading

0 comments on commit cfba7b7

Please sign in to comment.