Skip to content

Commit

Permalink
Merge pull request #606 from salasberryfin/patch-release-0.9.1
Browse files Browse the repository at this point in the history
Patch release v0.9.1
  • Loading branch information
salasberryfin authored Jul 18, 2024
2 parents b37d451 + a44c264 commit dcea87c
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 53 deletions.
71 changes: 71 additions & 0 deletions internal/controllers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"net/http"
"time"

batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -179,6 +180,41 @@ func createImportManifest(ctx context.Context, remoteClient client.Client, in io
return nil
}

func validateImportReadiness(ctx context.Context, remoteClient client.Client, in io.Reader) (bool, error) {
log := log.FromContext(ctx)

jobs := &batchv1.JobList{}
if err := remoteClient.List(ctx, jobs, client.MatchingLabels(map[string]string{"cattle.io/creator": "norman"})); err != nil {
return false, fmt.Errorf("error looking for cleanup job: %w", err)
}

for _, job := range jobs.Items {
if job.GenerateName == "cattle-cleanup-" {
log.Info("cleanup job is being performed, waiting...", "gvk", job.GroupVersionKind(), "name", job.GetName(), "namespace", job.GetNamespace())
return true, nil
}
}

reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, 4096))

for {
raw, err := reader.Read()
if errors.Is(err, io.EOF) {
break
}

if err != nil {
return false, err
}

if requeue, err := verifyRawManifest(ctx, remoteClient, raw); err != nil || requeue {
return requeue, err
}
}

return false, nil
}

func createRawManifest(ctx context.Context, remoteClient client.Client, bytes []byte) error {
items, err := utilyaml.ToUnstructured(bytes)
if err != nil {
Expand All @@ -194,6 +230,41 @@ func createRawManifest(ctx context.Context, remoteClient client.Client, bytes []
return nil
}

func verifyRawManifest(ctx context.Context, remoteClient client.Client, bytes []byte) (bool, error) {
items, err := utilyaml.ToUnstructured(bytes)
if err != nil {
return false, fmt.Errorf("error unmarshalling bytes or empty object passed: %w", err)
}

for _, obj := range items {
if requeue, err := checkDeletion(ctx, remoteClient, obj.DeepCopy()); err != nil || requeue {
return requeue, err
}
}

return false, nil
}

func checkDeletion(ctx context.Context, c client.Client, obj client.Object) (bool, error) {
log := log.FromContext(ctx)
gvk := obj.GetObjectKind().GroupVersionKind()

err := c.Get(ctx, client.ObjectKeyFromObject(obj), obj)
if apierrors.IsNotFound(err) {
log.V(4).Info("object is missing, ready to be created", "gvk", gvk, "name", obj.GetName(), "namespace", obj.GetNamespace())
return false, nil
} else if err != nil {
return false, fmt.Errorf("checking object in remote cluster: %w", err)
}

if obj.GetDeletionTimestamp() != nil {
log.Info("object is being deleted, waiting", "gvk", gvk, "name", obj.GetName(), "namespace", obj.GetNamespace())
return true, nil
}

return false, nil
}

func createObject(ctx context.Context, c client.Client, obj client.Object) error {
log := log.FromContext(ctx)
gvk := obj.GetObjectKind().GroupVersionKind()
Expand Down
9 changes: 8 additions & 1 deletion internal/controllers/import_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func (r *CAPIImportReconciler) reconcileNormal(ctx context.Context, capiCluster
log.Info("found cluster name", "name", rancherCluster.Status.ClusterName)

if rancherCluster.Status.AgentDeployed {
log.Info("agent already deployed, no action needed")
log.Info("agent is deployed, no action needed")
return ctrl.Result{}, nil
}

Expand All @@ -273,6 +273,13 @@ func (r *CAPIImportReconciler) reconcileNormal(ctx context.Context, capiCluster
return ctrl.Result{}, fmt.Errorf("getting remote cluster client: %w", err)
}

if requeue, err := validateImportReadiness(ctx, remoteClient, strings.NewReader(manifest)); err != nil {
return ctrl.Result{}, fmt.Errorf("verifying import manifest: %w", err)
} else if requeue {
log.Info("Import manifests are being deleted, not ready to be applied yet, requeue")
return ctrl.Result{RequeueAfter: defaultRequeueDuration}, nil
}

if err := createImportManifest(ctx, remoteClient, strings.NewReader(manifest)); err != nil {
return ctrl.Result{}, fmt.Errorf("creating import manifest: %w", err)
}
Expand Down
64 changes: 42 additions & 22 deletions internal/controllers/import_controller_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,17 @@ func (r *CAPIImportManagementV3Reconciler) Reconcile(ctx context.Context, req ct
return ctrl.Result{Requeue: true}, err
}

log = log.WithValues("cluster", capiCluster.Name)

if capiCluster.ObjectMeta.DeletionTimestamp.IsZero() && !turtlesannotations.HasClusterImportAnnotation(capiCluster) &&
!controllerutil.ContainsFinalizer(capiCluster, managementv3.CapiClusterFinalizer) {
log.Info("CAPI cluster is imported, adding finalizer")
controllerutil.AddFinalizer(capiCluster, managementv3.CapiClusterFinalizer)
controllerutil.AddFinalizer(capiCluster, managementv3.CapiClusterFinalizer) {
log.Info("CAPI cluster is marked for import, adding finalizer")

if err := r.Client.Update(ctx, capiCluster); err != nil {
return ctrl.Result{}, fmt.Errorf("error adding finalizer: %w", err)
}
}

log = log.WithValues("cluster", capiCluster.Name)

// Wait for controlplane to be ready. This should never be false as the predicates
// do the filtering.
if !capiCluster.Status.ControlPlaneReady && !conditions.IsTrue(capiCluster, clusterv1.ControlPlaneReadyCondition) {
Expand Down Expand Up @@ -231,16 +230,25 @@ func (r *CAPIImportManagementV3Reconciler) reconcile(ctx context.Context, capiCl
rancherCluster = &rancherClusterList.Items[0]
}

if !rancherCluster.ObjectMeta.DeletionTimestamp.IsZero() {
if err := r.reconcileDelete(ctx, capiCluster); err != nil {
log.Error(err, "Removing CAPI Cluster failed, retrying")
return ctrl.Result{}, err
}

if controllerutil.RemoveFinalizer(rancherCluster, managementv3.CapiClusterFinalizer) {
if err := r.Client.Update(ctx, rancherCluster); err != nil {
return ctrl.Result{}, fmt.Errorf("error removing rancher cluster finalizer: %w", err)
}
}
}

if !capiCluster.ObjectMeta.DeletionTimestamp.IsZero() {
if err := r.deleteDependentRancherCluster(ctx, capiCluster); err != nil {
return ctrl.Result{}, fmt.Errorf("error deleting associated managementv3.Cluster resources: %w", err)
}
}

if !rancherCluster.ObjectMeta.DeletionTimestamp.IsZero() {
return r.reconcileDelete(ctx, capiCluster)
}

return r.reconcileNormal(ctx, capiCluster, rancherCluster)
}

Expand Down Expand Up @@ -270,6 +278,9 @@ func (r *CAPIImportManagementV3Reconciler) reconcileNormal(ctx context.Context,
capiClusterOwnerNamespace: capiCluster.Namespace,
ownedLabelName: "",
},
Finalizers: []string{
managementv3.CapiClusterFinalizer,
},
},
Spec: managementv3.ClusterSpec{
DisplayName: capiCluster.Name,
Expand All @@ -296,9 +307,10 @@ func (r *CAPIImportManagementV3Reconciler) reconcileNormal(ctx context.Context,
return ctrl.Result{}, err
}

if feature.Gates.Enabled(feature.PropagateLabels) {
patchBase := client.MergeFromWithOptions(rancherCluster.DeepCopy(), client.MergeFromWithOptimisticLock{})
patchBase := client.MergeFromWithOptions(rancherCluster.DeepCopy(), client.MergeFromWithOptimisticLock{})
needsFinalizer := controllerutil.AddFinalizer(rancherCluster, managementv3.CapiClusterFinalizer)

if feature.Gates.Enabled(feature.PropagateLabels) {
if rancherCluster.Labels == nil {
rancherCluster.Labels = map[string]string{}
}
Expand All @@ -312,10 +324,14 @@ func (r *CAPIImportManagementV3Reconciler) reconcileNormal(ctx context.Context,
}

log.Info("Successfully propagated labels to Rancher cluster")
} else if needsFinalizer {
if err := r.Client.Patch(ctx, rancherCluster, patchBase); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to patch Rancher cluster: %w", err)
}
}

if conditions.IsTrue(rancherCluster, managementv3.ClusterConditionAgentDeployed) {
log.Info("agent already deployed, no action needed")
if conditions.IsTrue(rancherCluster, managementv3.ClusterConditionReady) {
log.Info("agent is ready, no action needed")
return ctrl.Result{}, nil
}

Expand All @@ -337,6 +353,13 @@ func (r *CAPIImportManagementV3Reconciler) reconcileNormal(ctx context.Context,
return ctrl.Result{}, fmt.Errorf("getting remote cluster client: %w", err)
}

if requeue, err := validateImportReadiness(ctx, remoteClient, strings.NewReader(manifest)); err != nil {
return ctrl.Result{}, fmt.Errorf("verifying import manifest: %w", err)
} else if requeue {
log.Info("Import manifests are being deleted, not ready to be applied yet, requeue")
return ctrl.Result{RequeueAfter: defaultRequeueDuration}, nil
}

if err := createImportManifest(ctx, remoteClient, strings.NewReader(manifest)); err != nil {
return ctrl.Result{}, fmt.Errorf("creating import manifest: %w", err)
}
Expand Down Expand Up @@ -424,7 +447,7 @@ func (r *CAPIImportManagementV3Reconciler) rancherV1ClusterToCapiCluster(ctx con
}
}

func (r *CAPIImportManagementV3Reconciler) reconcileDelete(ctx context.Context, capiCluster *clusterv1.Cluster) (ctrl.Result, error) {
func (r *CAPIImportManagementV3Reconciler) reconcileDelete(ctx context.Context, capiCluster *clusterv1.Cluster) error {
log := log.FromContext(ctx)
log.Info("Reconciling rancher cluster deletion")

Expand All @@ -440,16 +463,13 @@ func (r *CAPIImportManagementV3Reconciler) reconcileDelete(ctx context.Context,

annotations[turtlesannotations.ClusterImportedAnnotation] = "true"
capiCluster.SetAnnotations(annotations)
controllerutil.RemoveFinalizer(capiCluster, managementv3.CapiClusterFinalizer)

if controllerutil.ContainsFinalizer(capiCluster, managementv3.CapiClusterFinalizer) {
controllerutil.RemoveFinalizer(capiCluster, managementv3.CapiClusterFinalizer)

if err := r.Client.Update(ctx, capiCluster); err != nil {
return ctrl.Result{}, fmt.Errorf("error removing finalizer: %w", err)
}
if err := r.Client.Update(ctx, capiCluster); err != nil {
return fmt.Errorf("error removing finalizer: %w", err)
}

return ctrl.Result{}, nil
return nil
}

func (r *CAPIImportManagementV3Reconciler) deleteDependentRancherCluster(ctx context.Context, capiCluster *clusterv1.Cluster) error {
Expand All @@ -464,7 +484,7 @@ func (r *CAPIImportManagementV3Reconciler) deleteDependentRancherCluster(ctx con
},
}

return r.RancherClient.DeleteAllOf(ctx, &managementv3.Cluster{}, selectors...)
return client.IgnoreNotFound(r.RancherClient.DeleteAllOf(ctx, &managementv3.Cluster{}, selectors...))
}

// verifyV1ClusterMigration verifies if a v1 cluster has been successfully migrated.
Expand Down
37 changes: 25 additions & 12 deletions internal/controllers/import_controller_v3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ var _ = Describe("reconcile CAPI Cluster", func() {
}).Should(Succeed())
})

It("should reconcile a CAPI cluster when rancher cluster doesn't exist", func() {
It("should reconcile a CAPI cluster when rancher cluster doesn't exist, and set finalizers", func() {
ns.Labels = map[string]string{}
Expect(cl.Update(ctx, ns)).To(Succeed())
capiCluster.Labels = map[string]string{
Expand All @@ -195,9 +195,15 @@ var _ = Describe("reconcile CAPI Cluster", func() {
Eventually(ctx, func(g Gomega) {
g.Expect(cl.List(ctx, rancherClusters, selectors...)).ToNot(HaveOccurred())
g.Expect(rancherClusters.Items).To(HaveLen(1))
g.Expect(rancherClusters.Items[0].Name).To(ContainSubstring("c-"))
g.Expect(rancherClusters.Items[0].Labels).To(HaveKeyWithValue(testLabelName, testLabelVal))
g.Expect(rancherClusters.Items[0].Finalizers).To(ContainElement(managementv3.CapiClusterFinalizer))
}).Should(Succeed())

Eventually(ctx, func(g Gomega) {
g.Expect(cl.Get(ctx, client.ObjectKeyFromObject(capiCluster), capiCluster)).ToNot(HaveOccurred())
g.Expect(capiCluster.Finalizers).To(ContainElement(managementv3.CapiClusterFinalizer))
}).Should(Succeed())
Expect(rancherClusters.Items[0].Name).To(ContainSubstring("c-"))
Expect(rancherClusters.Items[0].Labels).To(HaveKeyWithValue(testLabelName, testLabelVal))
})

It("should reconcile a CAPI cluster when rancher cluster doesn't exist and annotation is set on the namespace", func() {
Expand All @@ -223,7 +229,7 @@ var _ = Describe("reconcile CAPI Cluster", func() {
Expect(rancherClusters.Items[0].Name).To(ContainSubstring("c-"))
})

It("should reconcile a CAPI cluster when rancher cluster exists", func() {
It("should reconcile a CAPI cluster when rancher cluster exists, and have finalizers set", func() {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(sampleTemplate))
Expand All @@ -244,6 +250,7 @@ var _ = Describe("reconcile CAPI Cluster", func() {
Eventually(ctx, func(g Gomega) {
g.Expect(cl.List(ctx, rancherClusters, selectors...)).ToNot(HaveOccurred())
g.Expect(rancherClusters.Items).To(HaveLen(1))
g.Expect(rancherClusters.Items[0].Finalizers).ToNot(ContainElement(managementv3.CapiClusterFinalizer))
}).Should(Succeed())
cluster := rancherClusters.Items[0]
Expect(cluster.Name).To(ContainSubstring("c-"))
Expand Down Expand Up @@ -282,11 +289,16 @@ var _ = Describe("reconcile CAPI Cluster", func() {
Name: unstructuredObj.GetName(),
}, unstructuredObj)).To(Succeed())

g.Expect(cl.List(ctx, rancherClusters, selectors...)).ToNot(HaveOccurred())
g.Expect(rancherClusters.Items).To(HaveLen(1))
g.Expect(rancherClusters.Items[0].Name).To(ContainSubstring("c-"))
g.Expect(rancherClusters.Items[0].Labels).To(HaveKeyWithValue(testLabelName, testLabelVal))
}

g.Expect(cl.Get(ctx, client.ObjectKeyFromObject(capiCluster), capiCluster)).ToNot(HaveOccurred())
g.Expect(capiCluster.Finalizers).To(ContainElement(managementv3.CapiClusterFinalizer))

g.Expect(cl.List(ctx, rancherClusters, selectors...)).ToNot(HaveOccurred())
g.Expect(rancherClusters.Items).To(HaveLen(1))
g.Expect(rancherClusters.Items[0].Name).To(ContainSubstring("c-"))
g.Expect(rancherClusters.Items[0].Labels).To(HaveKeyWithValue(testLabelName, testLabelVal))
g.Expect(rancherClusters.Items[0].Finalizers).To(ContainElement(managementv3.CapiClusterFinalizer))
}, 10*time.Second).Should(Succeed())
})

Expand Down Expand Up @@ -332,8 +344,8 @@ var _ = Describe("reconcile CAPI Cluster", func() {
cluster := rancherClusters.Items[0]
Expect(cluster.Name).To(ContainSubstring("c-"))

conditions.Set(&cluster, conditions.TrueCondition(managementv3.ClusterConditionAgentDeployed))
Expect(conditions.IsTrue(&cluster, managementv3.ClusterConditionAgentDeployed)).To(BeTrue())
conditions.Set(&cluster, conditions.TrueCondition(managementv3.ClusterConditionReady))
Expect(conditions.IsTrue(&cluster, managementv3.ClusterConditionReady)).To(BeTrue())
Expect(cl.Status().Update(ctx, &cluster)).To(Succeed())

_, err := r.Reconcile(ctx, reconcile.Request{
Expand Down Expand Up @@ -477,8 +489,8 @@ var _ = Describe("reconcile CAPI Cluster", func() {

Eventually(ctx, func(g Gomega) {
g.Expect(cl.Get(ctx, client.ObjectKeyFromObject(rancherCluster), rancherCluster)).To(Succeed())
conditions.Set(rancherCluster, conditions.TrueCondition(managementv3.ClusterConditionAgentDeployed))
g.Expect(conditions.IsTrue(rancherCluster, managementv3.ClusterConditionAgentDeployed)).To(BeTrue())
conditions.Set(rancherCluster, conditions.TrueCondition(managementv3.ClusterConditionReady))
g.Expect(conditions.IsTrue(rancherCluster, managementv3.ClusterConditionReady)).To(BeTrue())
g.Expect(cl.Status().Update(ctx, rancherCluster)).To(Succeed())
}).Should(Succeed())

Expand Down Expand Up @@ -534,4 +546,5 @@ var _ = Describe("reconcile CAPI Cluster", func() {
Expect(rancherClusters.Items).To(HaveLen(0))
}).Should(Succeed())
})

})
14 changes: 14 additions & 0 deletions internal/rancher/provisioning/v1/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package v1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)

// Cluster is the struct representing a Rancher Cluster.
Expand All @@ -41,6 +43,8 @@ type ClusterStatus struct {
ClusterName string `json:"clusterName,omitempty"`
AgentDeployed bool `json:"agentDeployed,omitempty"`
Ready bool `json:"ready,omitempty"`

Conditions clusterv1.Conditions `json:"conditions,omitempty"`
}

// ClusterList contains a list of ClusterList.
Expand All @@ -51,6 +55,16 @@ type ClusterList struct {
Items []Cluster `json:"items"`
}

// GetConditions method to implement capi conditions getter interface.
func (c *Cluster) GetConditions() clusterv1.Conditions {
return c.Status.Conditions
}

// SetConditions method to implement capi conditions setter interface.
func (c *Cluster) SetConditions(conditions clusterv1.Conditions) {
c.Status.Conditions = conditions
}

func init() {
SchemeBuilder.Register(&Cluster{}, &ClusterList{})
}
Loading

0 comments on commit dcea87c

Please sign in to comment.