Skip to content

Commit

Permalink
Merge pull request #230 from kylewuolle/capi-status
Browse files Browse the repository at this point in the history
Add CAPI status/conditions to deployment status
  • Loading branch information
kylewuolle authored Aug 30, 2024
2 parents e9bbdf8 + c12515a commit 10cbff6
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 6 deletions.
1 change: 1 addition & 0 deletions api/v1alpha1/deployment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
const (
DeploymentFinalizer = "hmc.mirantis.com/deployment"

FluxHelmChartNameKey = "helm.toolkit.fluxcd.io/name"
HMCManagedLabelKey = "hmc.mirantis.com/managed"
HMCManagedLabelValue = "true"
)
Expand Down
14 changes: 11 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
sourcev1 "github.com/fluxcd/source-controller/api/v1"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/dynamic"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
Expand Down Expand Up @@ -152,6 +153,12 @@ func main() {
os.Exit(1)
}

dc, err := dynamic.NewForConfig(mgr.GetConfig())
if err != nil {
setupLog.Error(err, "failed to create dynamic client")
os.Exit(1)
}

if err = (&controller.TemplateReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Expand All @@ -160,9 +167,10 @@ func main() {
os.Exit(1)
}
if err = (&controller.DeploymentReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Config: mgr.GetConfig(),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Config: mgr.GetConfig(),
DynamicClient: dc,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Deployment")
os.Exit(1)
Expand Down
82 changes: 79 additions & 3 deletions internal/controller/deployment_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"fmt"
"time"

"k8s.io/apimachinery/pkg/labels"

hcv2 "github.com/fluxcd/helm-controller/api/v2"
fluxmeta "github.com/fluxcd/pkg/apis/meta"
fluxconditions "github.com/fluxcd/pkg/runtime/conditions"
Expand All @@ -30,8 +32,11 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -48,16 +53,16 @@ import (
// DeploymentReconciler reconciles a Deployment object
type DeploymentReconciler struct {
client.Client
Scheme *runtime.Scheme
Config *rest.Config
Scheme *runtime.Scheme
Config *rest.Config
DynamicClient *dynamic.DynamicClient
}

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
func (r *DeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
l := log.FromContext(ctx).WithValues("DeploymentController", req.NamespacedName)
l.Info("Reconciling Deployment")

deployment := &hmc.Deployment{}
if err := r.Get(ctx, req.NamespacedName, deployment); err != nil {
if apierrors.IsNotFound(err) {
Expand Down Expand Up @@ -87,6 +92,63 @@ func (r *DeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return r.Update(ctx, l, deployment)
}

func (r *DeploymentReconciler) setStatusFromClusterStatus(ctx context.Context, l logr.Logger, deployment *hmc.Deployment) (bool, error) {
resourceId := schema.GroupVersionResource{
Group: "cluster.x-k8s.io",
Version: "v1beta1",
Resource: "clusters",
}

list, err := r.DynamicClient.Resource(resourceId).Namespace(deployment.Namespace).List(ctx, metav1.ListOptions{
LabelSelector: labels.SelectorFromSet(map[string]string{hmc.FluxHelmChartNameKey: deployment.Name}).String(),
})

if apierrors.IsNotFound(err) || len(list.Items) == 0 {
l.Info("Clusters not found, ignoring since object must be deleted or not yet created")
return true, nil
}

if err != nil {
return true, fmt.Errorf("failed to get cluster information for deployment %s in namespace: %s: %w",
deployment.Namespace, deployment.Name, err)
}
conditions, found, err := unstructured.NestedSlice(list.Items[0].Object, "status", "conditions")
if err != nil {
return true, fmt.Errorf("failed to get cluster information for deployment %s in namespace: %s: %w",
deployment.Namespace, deployment.Name, err)
}
if !found {
return true, fmt.Errorf("failed to get cluster information for deployment %s in namespace: %s: status.conditions not found",
deployment.Namespace, deployment.Name)
}

allConditionsComplete := true
for _, condition := range conditions {
conditionMap, ok := condition.(map[string]interface{})
if !ok {
return true, fmt.Errorf("failed to cast condition to map[string]interface{} for deployment: %s in namespace: %s: %w",
deployment.Namespace, deployment.Name, err)
}

var metaCondition metav1.Condition
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(conditionMap, &metaCondition); err != nil {
return true, fmt.Errorf("failed to convert unstructured conditions to metav1.Condition for deployment %s in namespace: %s: %w",
deployment.Namespace, deployment.Name, err)
}

if metaCondition.Status != "True" {
allConditionsComplete = false
}

if metaCondition.Reason == "" && metaCondition.Status == "True" {
metaCondition.Reason = "Succeeded"
}
apimeta.SetStatusCondition(deployment.GetConditions(), metaCondition)
}

return !allConditionsComplete, nil
}

func (r *DeploymentReconciler) Update(ctx context.Context, l logr.Logger, deployment *hmc.Deployment) (result ctrl.Result, err error) {
finalizersUpdated := controllerutil.AddFinalizer(deployment, hmc.DeploymentFinalizer)
if finalizersUpdated {
Expand Down Expand Up @@ -224,6 +286,20 @@ func (r *DeploymentReconciler) Update(ctx context.Context, l logr.Logger, deploy
Message: hrReadyCondition.Message,
})
}

requeue, err := r.setStatusFromClusterStatus(ctx, l, deployment)
if err != nil {
if requeue {
return ctrl.Result{RequeueAfter: 10 * time.Second}, err
} else {
return ctrl.Result{}, err
}
}

if requeue {
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
}

if !fluxconditions.IsReady(hr) {
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
}
Expand Down
2 changes: 2 additions & 0 deletions internal/controller/deployment_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

hmc "github.com/Mirantis/hmc/api/v1alpha1"
Expand Down Expand Up @@ -128,6 +129,7 @@ var _ = Describe("Deployment Controller", func() {
controllerReconciler := &DeploymentReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
Config: &rest.Config{},
}

_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
Expand Down
7 changes: 7 additions & 0 deletions templates/hmc/templates/rbac/roles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ metadata:
labels:
{{- include "hmc.labels" . | nindent 4 }}
rules:
- apiGroups:
- cluster.x-k8s.io
resources:
- clusters
verbs:
- get
- list
- apiGroups:
- helm.toolkit.fluxcd.io
resources:
Expand Down

0 comments on commit 10cbff6

Please sign in to comment.