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

Rework orphaned Templates removal #571

Merged
merged 3 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions internal/controller/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ var _ = BeforeSuite(func() {
})
Expect(err).NotTo(HaveOccurred())
mgrClient = mgr.GetClient()
Expect(mgrClient).NotTo(BeNil())

err = hmcmirantiscomv1alpha1.SetupIndexers(ctx, mgr)
Expect(err).NotTo(HaveOccurred())
Expand Down
117 changes: 54 additions & 63 deletions internal/controller/templatechain_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

hmc "github.com/Mirantis/hmc/api/v1alpha1"
"github.com/Mirantis/hmc/internal/utils"
)

const HMCManagedByChainLabelKey = "hmc.mirantis.com/managed-by-chain"

// TemplateChainReconciler reconciles a TemplateChain object
type TemplateChainReconciler struct {
client.Client
SystemNamespace string

templateKind string
}

type ClusterTemplateChainReconciler struct {
Expand All @@ -46,8 +48,6 @@ type ServiceTemplateChainReconciler struct {
// templateChain is the interface defining a list of methods to interact with *templatechains
type templateChain interface {
client.Object
Kind() string
TemplateKind() string
GetSpec() *hmc.TemplateChainSpec
}

Expand All @@ -65,6 +65,7 @@ func (r *ClusterTemplateChainReconciler) Reconcile(ctx context.Context, req ctrl
l.Error(err, "Failed to get ClusterTemplateChain")
return ctrl.Result{}, err
}

return r.ReconcileTemplateChain(ctx, clusterTemplateChain)
}

Expand All @@ -82,39 +83,39 @@ func (r *ServiceTemplateChainReconciler) Reconcile(ctx context.Context, req ctrl
l.Error(err, "Failed to get ServiceTemplateChain")
return ctrl.Result{}, err
}

return r.ReconcileTemplateChain(ctx, serviceTemplateChain)
}

func (r *TemplateChainReconciler) ReconcileTemplateChain(ctx context.Context, templateChain templateChain) (ctrl.Result, error) {
l := ctrl.LoggerFrom(ctx)

systemTemplates, managedTemplates, err := getCurrentTemplates(ctx, r.Client, templateChain.TemplateKind(), r.SystemNamespace, templateChain.GetNamespace(), templateChain.GetName())
if templateChain.GetNamespace() == r.SystemNamespace {
return ctrl.Result{}, nil
}

systemTemplates, err := r.getSystemTemplates(ctx)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to get current templates: %w", err)
return ctrl.Result{}, fmt.Errorf("failed to get system templates: %w", err)
}

var (
errs error
keepTemplate = make(map[string]struct{}, len(templateChain.GetSpec().SupportedTemplates))
)
var errs error
for _, supportedTemplate := range templateChain.GetSpec().SupportedTemplates {
meta := metav1.ObjectMeta{
Name: supportedTemplate.Name,
Namespace: templateChain.GetNamespace(),
Labels: map[string]string{
hmc.HMCManagedLabelKey: hmc.HMCManagedLabelValue,
HMCManagedByChainLabelKey: templateChain.GetName(),
hmc.HMCManagedLabelKey: hmc.HMCManagedLabelValue,
},
}
keepTemplate[supportedTemplate.Name] = struct{}{}

source, found := systemTemplates[supportedTemplate.Name]
if !found {
errs = errors.Join(errs, fmt.Errorf("source %s %s/%s is not found", templateChain.TemplateKind(), r.SystemNamespace, supportedTemplate.Name))
errs = errors.Join(errs, fmt.Errorf("source %s %s/%s is not found", r.templateKind, r.SystemNamespace, supportedTemplate.Name))
continue
}
if source.GetCommonStatus().ChartRef == nil {
errs = errors.Join(errs, fmt.Errorf("source %s %s/%s does not have chart reference yet", templateChain.TemplateKind(), r.SystemNamespace, supportedTemplate.Name))
errs = errors.Join(errs, fmt.Errorf("source %s %s/%s does not have chart reference yet", r.templateKind, r.SystemNamespace, supportedTemplate.Name))
continue
}

Expand All @@ -123,111 +124,101 @@ func (r *TemplateChainReconciler) ReconcileTemplateChain(ctx context.Context, te
}

var target client.Object
switch templateChain.Kind() {
case hmc.ClusterTemplateChainKind:
switch r.templateKind {
case hmc.ClusterTemplateKind:
target = &hmc.ClusterTemplate{ObjectMeta: meta, Spec: hmc.ClusterTemplateSpec{
Helm: helmSpec,
}}
case hmc.ServiceTemplateChainKind:
case hmc.ServiceTemplateKind:
target = &hmc.ServiceTemplate{ObjectMeta: meta, Spec: hmc.ServiceTemplateSpec{
Helm: helmSpec,
}}
default:
return ctrl.Result{}, fmt.Errorf("invalid TemplateChain kind. Supported kinds are %s and %s", hmc.ClusterTemplateChainKind, hmc.ServiceTemplateChainKind)
}

if err := r.Create(ctx, target); err == nil {
l.Info(templateChain.TemplateKind()+" was successfully created", "template namespace", templateChain.GetNamespace(), "template name", supportedTemplate.Name)
operation, err := ctrl.CreateOrUpdate(ctx, r.Client, target, func() error {
utils.AddOwnerReference(target, templateChain)
return nil
})
if err != nil {
errs = errors.Join(errs, err)
continue
}

if !apierrors.IsAlreadyExists(err) {
errs = errors.Join(errs, err)
if operation == controllerutil.OperationResultCreated {
l.Info(r.templateKind+" was successfully created", "template namespace", templateChain.GetNamespace(), "template name", supportedTemplate.Name)
}
}

for _, template := range managedTemplates {
templateName := template.GetName()
if _, keep := keepTemplate[templateName]; keep {
continue
if operation == controllerutil.OperationResultUpdated {
l.Info("Successfully updated OwnerReference on "+r.templateKind, "template namespace", templateChain.GetNamespace(), "template name", supportedTemplate.Name)
}
}

ll := l.WithValues("template kind", templateChain.TemplateKind(), "template namespace", templateChain.GetNamespace(), "template name", templateName)
ll.Info("Deleting Template")

if err := r.Delete(ctx, template); client.IgnoreNotFound(err) != nil {
errs = errors.Join(errs, err)
continue
}
return ctrl.Result{}, errs
}

ll.Info("Template has been deleted")
func (r *TemplateChainReconciler) getSystemTemplates(ctx context.Context) (systemTemplates map[string]templateCommon, _ error) {
templates, err := r.getTemplates(ctx, r.templateKind, &client.ListOptions{Namespace: r.SystemNamespace})
if err != nil {
return nil, err
}

return ctrl.Result{}, errs
systemTemplates = make(map[string]templateCommon, len(templates))
for _, tmpl := range templates {
systemTemplates[tmpl.GetName()] = tmpl
}
return systemTemplates, nil
}

func getCurrentTemplates(ctx context.Context, cl client.Client, templateKind, systemNamespace, targetNamespace, templateChainName string) (systemTemplates map[string]templateCommon, managedTemplates []templateCommon, _ error) {
func (r *TemplateChainReconciler) getTemplates(ctx context.Context, templateKind string, opts *client.ListOptions) ([]templateCommon, error) {
var templates []templateCommon

switch templateKind {
case hmc.ClusterTemplateKind:
ctList := &hmc.ClusterTemplateList{}
err := cl.List(ctx, ctList)
err := r.Client.List(ctx, ctList, opts)
if err != nil {
return nil, nil, err
return nil, err
}
for _, template := range ctList.Items {
templates = append(templates, &template)
}
case hmc.ServiceTemplateKind:
stList := &hmc.ServiceTemplateList{}
err := cl.List(ctx, stList)
err := r.Client.List(ctx, stList, opts)
if err != nil {
return nil, nil, err
return nil, err
}
for _, template := range stList.Items {
templates = append(templates, &template)
}
default:
return nil, nil, fmt.Errorf("invalid Template kind. Supported kinds are %s and %s", hmc.ClusterTemplateKind, hmc.ServiceTemplateKind)
}

systemTemplates = make(map[string]templateCommon, len(templates))
managedTemplates = make([]templateCommon, 0, len(templates))
for _, template := range templates {
if template.GetNamespace() == systemNamespace {
systemTemplates[template.GetName()] = template
continue
}

labels := template.GetLabels()
if template.GetNamespace() == targetNamespace &&
labels[hmc.HMCManagedLabelKey] == hmc.HMCManagedLabelValue &&
labels[HMCManagedByChainLabelKey] == templateChainName {
managedTemplates = append(managedTemplates, template)
}
return nil, fmt.Errorf("invalid Template kind. Supported kinds are %s and %s", hmc.ClusterTemplateKind, hmc.ServiceTemplateKind)
}

return systemTemplates, managedTemplates, nil
return templates, nil
}

func getTemplateNamesManagedByChain(chain templateChain) []string {
result := make([]string, 0, len(chain.GetSpec().SupportedTemplates))
for _, template := range chain.GetSpec().SupportedTemplates {
result = append(result, template.Name)
for _, tmpl := range chain.GetSpec().SupportedTemplates {
result = append(result, tmpl.Name)
}
return result
}

// SetupWithManager sets up the controller with the Manager.
func (r *ClusterTemplateChainReconciler) SetupWithManager(mgr ctrl.Manager) error {
r.templateKind = hmc.ClusterTemplateKind

return ctrl.NewControllerManagedBy(mgr).
For(&hmc.ClusterTemplateChain{}).
Complete(r)
}

// SetupWithManager sets up the controller with the Manager.
func (r *ServiceTemplateChainReconciler) SetupWithManager(mgr ctrl.Manager) error {
r.templateKind = hmc.ServiceTemplateKind

return ctrl.NewControllerManagedBy(mgr).
For(&hmc.ServiceTemplateChain{}).
Complete(r)
Expand Down
Loading