diff --git a/Makefile b/Makefile index 960b0d755..4c0def6cc 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,7 @@ KIND_CLUSTER_NAME ?= hmc-dev KIND_NETWORK ?= kind LOCAL_REGISTRY_NAME ?= hmc-local-registry LOCAL_REGISTRY_PORT ?= 5001 -LOCAL_REGISTRY_REPO ?= oci://127.0.0.1:$(LOCAL_REGISTRY_PORT)/chart +LOCAL_REGISTRY_REPO ?= oci://127.0.0.1:$(LOCAL_REGISTRY_PORT)/charts ifndef ignore-not-found ignore-not-found = false diff --git a/api/v1alpha1/management_types.go b/api/v1alpha1/management_types.go index f8fb17c97..60796f4a8 100644 --- a/api/v1alpha1/management_types.go +++ b/api/v1alpha1/management_types.go @@ -17,7 +17,14 @@ limitations under the License. package v1alpha1 import ( + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/yaml" +) + +const ( + ManagementName = "hmc" + ManagementNamespace = "hmc-system" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -28,14 +35,72 @@ type ManagementSpec struct { // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster // Important: Run "make" to regenerate code after modifying this file - // Foo is an example field of Management. Edit management_types.go to remove/update - Foo string `json:"foo,omitempty"` + // Components is the list of supported management components + Components []Component `json:"components,omitempty"` +} + +// Component represents HMC management component +type Component struct { + // Template is the name of the Template associated with this component + Template string `json:"template"` + // Config allows to provide parameters for management component customization. + // If no Config provided, the field will be populated with the default + // values for the template. + // +optional + Config *apiextensionsv1.JSON `json:"config,omitempty"` +} + +func (in *Component) HelmValues() (values map[string]interface{}, err error) { + if in.Config != nil { + err = yaml.Unmarshal(in.Config.Raw, &values) + } + return values, err +} + +func (m ManagementSpec) SetDefaults() { + // TODO: Uncomment when Templates will be ready + /* + m.Components = []Component{ + { + Template: "cluster-api", + }, + { + Template: "k0smotron", + }, + { + Template: "cluster-api-provider-aws", + }, + } + */ } // ManagementStatus defines the observed state of Management type ManagementStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // ObservedGeneration is the last observed generation. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + // Providers is the list of discovered supported providers + Providers ProvidersStatus `json:"providers,omitempty"` + // Components contains the map with the status of Management components installation + Components map[string]ComponentStatus `json:"components,omitempty"` +} + +// ComponentStatus is the status of Management component installation +type ComponentStatus struct { + // Success represents if a component installation was successful + Success bool `json:"success,omitempty"` + // Error stores as error message in case of failed installation + Error string `json:"error,omitempty"` +} + +// ProvidersStatus is the list of discovered supported providers +type ProvidersStatus struct { + // InfrastructureProviders is the list of discovered infrastructure providers + InfrastructureProviders []string `json:"infrastructure,omitempty"` + // BootstrapProviders is the list of discovered bootstrap providers + BootstrapProviders []string `json:"bootstrap,omitempty"` + // ControlPlaneProviders is the list of discovered control plane providers + ControlPlaneProviders []string `json:"controlPlane,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/template_types.go b/api/v1alpha1/template_types.go index 482999cdc..57a6c7c82 100644 --- a/api/v1alpha1/template_types.go +++ b/api/v1alpha1/template_types.go @@ -24,6 +24,11 @@ import ( ) const ( + // TemplatesNamespace is the namespace where all Templates are located + TemplatesNamespace = "hmc-system" + + // ManagementKind is the string representation of a Management. + ManagementKind = "Management" // TemplateKind is the string representation of a Template. TemplateKind = "Template" // DeploymentKind is the string representation of a Deployment. @@ -90,7 +95,7 @@ type TemplateStatus struct { // +optional ChartRef *helmcontrollerv2.CrossNamespaceSourceReference `json:"chartRef,omitempty"` // Type specifies the type of the provided template, as discovered from the Helm chart metadata. - // +kubebuilder:validation:Enum=deployment;provider;management + // +kubebuilder:validation:Enum=deployment;provider;core Type string `json:"type,omitempty"` // InfrastructureProviders specifies CAPI infrastructure providers associated with the template. // +optional diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 8e7ea7745..ca1001810 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -115,6 +115,41 @@ func (in *AWSProviderStatus) DeepCopy() *AWSProviderStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Component) DeepCopyInto(out *Component) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(v1.JSON) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Component. +func (in *Component) DeepCopy() *Component { + if in == nil { + return nil + } + out := new(Component) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComponentStatus) DeepCopyInto(out *ComponentStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentStatus. +func (in *ComponentStatus) DeepCopy() *ComponentStatus { + if in == nil { + return nil + } + out := new(ComponentStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Deployment) DeepCopyInto(out *Deployment) { *out = *in @@ -235,8 +270,8 @@ func (in *Management) DeepCopyInto(out *Management) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Management. @@ -292,6 +327,13 @@ func (in *ManagementList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ManagementSpec) DeepCopyInto(out *ManagementSpec) { *out = *in + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]Component, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementSpec. @@ -307,6 +349,14 @@ func (in *ManagementSpec) DeepCopy() *ManagementSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ManagementStatus) DeepCopyInto(out *ManagementStatus) { *out = *in + in.Providers.DeepCopyInto(&out.Providers) + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make(map[string]ComponentStatus, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementStatus. @@ -319,6 +369,36 @@ func (in *ManagementStatus) DeepCopy() *ManagementStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProvidersStatus) DeepCopyInto(out *ProvidersStatus) { + *out = *in + if in.InfrastructureProviders != nil { + in, out := &in.InfrastructureProviders, &out.InfrastructureProviders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.BootstrapProviders != nil { + in, out := &in.BootstrapProviders, &out.BootstrapProviders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ControlPlaneProviders != nil { + in, out := &in.ControlPlaneProviders, &out.ControlPlaneProviders + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvidersStatus. +func (in *ProvidersStatus) DeepCopy() *ProvidersStatus { + if in == nil { + return nil + } + out := new(ProvidersStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Template) DeepCopyInto(out *Template) { *out = *in diff --git a/cmd/main.go b/cmd/main.go index c86f63947..746c32926 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -152,6 +152,7 @@ func main() { if err = (&controller.ManagementReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), + Config: mgr.GetConfig(), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Management") os.Exit(1) diff --git a/config/crd/bases/hmc.mirantis.com_managements.yaml b/config/crd/bases/hmc.mirantis.com_managements.yaml index 11fdbc665..5f8a3e1b6 100644 --- a/config/crd/bases/hmc.mirantis.com_managements.yaml +++ b/config/crd/bases/hmc.mirantis.com_managements.yaml @@ -39,13 +39,72 @@ spec: spec: description: ManagementSpec defines the desired state of Management properties: - foo: - description: Foo is an example field of Management. Edit management_types.go - to remove/update - type: string + components: + description: Components is the list of supported management components + items: + description: Component represents HMC management component + properties: + config: + description: |- + Config allows to provide parameters for management component customization. + If no Config provided, the field will be populated with the default + values for the template. + x-kubernetes-preserve-unknown-fields: true + template: + description: Template is the name of the Template associated + with this component + type: string + required: + - template + type: object + type: array type: object status: description: ManagementStatus defines the observed state of Management + properties: + components: + additionalProperties: + description: ComponentStatus is the status of Management component + installation + properties: + error: + description: Error stores as error message in case of failed + installation + type: string + success: + description: Success represents if a component installation + was successful + type: boolean + type: object + description: Components contains the map with the status of Management + components installation + type: object + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + providers: + description: Providers is the list of discovered supported providers + properties: + bootstrap: + description: BootstrapProviders is the list of discovered bootstrap + providers + items: + type: string + type: array + controlPlane: + description: ControlPlaneProviders is the list of discovered control + plane providers + items: + type: string + type: array + infrastructure: + description: InfrastructureProviders is the list of discovered + infrastructure providers + items: + type: string + type: array + type: object type: object type: object served: true diff --git a/config/crd/bases/hmc.mirantis.com_templates.yaml b/config/crd/bases/hmc.mirantis.com_templates.yaml index 1b8e21016..eb222cb7f 100644 --- a/config/crd/bases/hmc.mirantis.com_templates.yaml +++ b/config/crd/bases/hmc.mirantis.com_templates.yaml @@ -158,7 +158,7 @@ spec: enum: - deployment - provider - - management + - core type: string valid: description: Valid indicates whether the template passed validation diff --git a/config/rbac/awsprovider_editor_role.yaml b/config/rbac/awsprovider_editor_role.yaml index d952a3f21..445732837 100644 --- a/config/rbac/awsprovider_editor_role.yaml +++ b/config/rbac/awsprovider_editor_role.yaml @@ -8,7 +8,7 @@ metadata: name: awsprovider-editor-role rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - awsproviders verbs: @@ -20,7 +20,7 @@ rules: - update - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - awsproviders/status verbs: diff --git a/config/rbac/awsprovider_viewer_role.yaml b/config/rbac/awsprovider_viewer_role.yaml index 89eb7f7ba..6ac793eb3 100644 --- a/config/rbac/awsprovider_viewer_role.yaml +++ b/config/rbac/awsprovider_viewer_role.yaml @@ -8,7 +8,7 @@ metadata: name: awsprovider-viewer-role rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - awsproviders verbs: @@ -16,7 +16,7 @@ rules: - list - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - awsproviders/status verbs: diff --git a/config/rbac/deployment_editor_role.yaml b/config/rbac/deployment_editor_role.yaml index 2d144e4ee..63b45cf4d 100644 --- a/config/rbac/deployment_editor_role.yaml +++ b/config/rbac/deployment_editor_role.yaml @@ -8,7 +8,7 @@ metadata: name: deployment-editor-role rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - deployments verbs: @@ -20,7 +20,7 @@ rules: - update - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - deployments/status verbs: diff --git a/config/rbac/deployment_viewer_role.yaml b/config/rbac/deployment_viewer_role.yaml index 288f1bdc1..73bde70b3 100644 --- a/config/rbac/deployment_viewer_role.yaml +++ b/config/rbac/deployment_viewer_role.yaml @@ -8,7 +8,7 @@ metadata: name: deployment-viewer-role rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - deployments verbs: @@ -16,7 +16,7 @@ rules: - list - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - deployments/status verbs: diff --git a/config/rbac/management_editor_role.yaml b/config/rbac/management_editor_role.yaml index 7ead123ab..90d81ee03 100644 --- a/config/rbac/management_editor_role.yaml +++ b/config/rbac/management_editor_role.yaml @@ -8,7 +8,7 @@ metadata: name: management-editor-role rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - managements verbs: @@ -20,7 +20,7 @@ rules: - update - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - managements/status verbs: diff --git a/config/rbac/management_viewer_role.yaml b/config/rbac/management_viewer_role.yaml index fcf9a131c..044531ef2 100644 --- a/config/rbac/management_viewer_role.yaml +++ b/config/rbac/management_viewer_role.yaml @@ -8,7 +8,7 @@ metadata: name: management-viewer-role rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - managements verbs: @@ -16,7 +16,7 @@ rules: - list - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - managements/status verbs: diff --git a/config/rbac/template_editor_role.yaml b/config/rbac/template_editor_role.yaml index 9d5fef4fe..197896482 100644 --- a/config/rbac/template_editor_role.yaml +++ b/config/rbac/template_editor_role.yaml @@ -8,7 +8,7 @@ metadata: name: template-editor-role rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - templates verbs: @@ -20,7 +20,7 @@ rules: - update - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - templates/status verbs: diff --git a/config/rbac/template_viewer_role.yaml b/config/rbac/template_viewer_role.yaml index 627bd2a80..1c78288e6 100644 --- a/config/rbac/template_viewer_role.yaml +++ b/config/rbac/template_viewer_role.yaml @@ -8,7 +8,7 @@ metadata: name: template-viewer-role rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - templates verbs: @@ -16,7 +16,7 @@ rules: - list - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - templates/status verbs: diff --git a/config/samples/hmc.mirantis.com_v1alpha1_template.yaml b/config/samples/hmc.mirantis.com_v1alpha1_template.yaml deleted file mode 100644 index 58d952820..000000000 --- a/config/samples/hmc.mirantis.com_v1alpha1_template.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: hmc.mirantis.com/v1alpha1 -kind: Template -metadata: - labels: - app.kubernetes.io/name: hmc - app.kubernetes.io/managed-by: kustomize - name: template-sample -spec: - provider: aws - helm: - chartName: aws-standalone-cp - chartVersion: 0.1.0 diff --git a/internal/controller/deployment_controller.go b/internal/controller/deployment_controller.go index 7202baf82..31d6d9ac2 100644 --- a/internal/controller/deployment_controller.go +++ b/internal/controller/deployment_controller.go @@ -121,7 +121,14 @@ func (r *DeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } if !deployment.Spec.DryRun { - _, err := r.reconcileHelmRelease(ctx, deployment, template.Status.ChartRef) + ownerRef := metav1.OwnerReference{ + APIVersion: hmc.GroupVersion.String(), + Kind: hmc.DeploymentKind, + Name: deployment.Name, + UID: deployment.UID, + } + _, err := helm.ReconcileHelmRelease(ctx, r.Client, deployment.Name, deployment.Namespace, deployment.Spec.Config, + ownerRef, template.Status.ChartRef, defaultReconcileInterval) if err != nil { return ctrl.Result{}, err } @@ -129,37 +136,6 @@ func (r *DeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, r.updateStatus(ctx, deployment, "") } -func (r *DeploymentReconciler) reconcileHelmRelease(ctx context.Context, deployment *hmc.Deployment, chartRef *hcv2.CrossNamespaceSourceReference) (*hcv2.HelmRelease, error) { - helmRelease := &hcv2.HelmRelease{ - ObjectMeta: metav1.ObjectMeta{ - Name: deployment.Name, - Namespace: deployment.Namespace, - }, - } - - _, err := ctrl.CreateOrUpdate(ctx, r.Client, helmRelease, func() error { - helmRelease.OwnerReferences = []metav1.OwnerReference{ - { - APIVersion: hmc.GroupVersion.String(), - Kind: hmc.DeploymentKind, - Name: deployment.Name, - UID: deployment.UID, - }, - } - helmRelease.Spec = hcv2.HelmReleaseSpec{ - ChartRef: chartRef, - Interval: metav1.Duration{Duration: defaultReconcileInterval}, - ReleaseName: deployment.Name, - Values: deployment.Spec.Config, - } - return nil - }) - if err != nil { - return nil, err - } - return helmRelease, nil -} - func (r *DeploymentReconciler) validateReleaseWithValues(ctx context.Context, actionConfig *action.Configuration, deployment *hmc.Deployment, hcChart *chart.Chart) error { install := action.NewInstall(actionConfig) install.DryRun = true diff --git a/internal/controller/management_controller.go b/internal/controller/management_controller.go index a01922648..c5c47f191 100644 --- a/internal/controller/management_controller.go +++ b/internal/controller/management_controller.go @@ -18,45 +18,127 @@ package controller import ( "context" + "errors" + "fmt" + "github.com/Mirantis/hmc/internal/helm" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - hmcmirantiscomv1alpha1 "github.com/Mirantis/hmc/api/v1alpha1" + hmc "github.com/Mirantis/hmc/api/v1alpha1" ) // ManagementReconciler reconciles a Management object type ManagementReconciler struct { client.Client Scheme *runtime.Scheme + Config *rest.Config } //+kubebuilder:rbac:groups=hmc.mirantis.com,resources=managements,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=hmc.mirantis.com,resources=managements/status,verbs=get;update;patch //+kubebuilder:rbac:groups=hmc.mirantis.com,resources=managements/finalizers,verbs=update -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the Management object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.17.3/pkg/reconcile func (r *ManagementReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) + l := log.FromContext(ctx).WithValues("ManagementController", req.NamespacedName) + l.Info("Reconciling Management") - // TODO(user): your logic here + management := &hmc.Management{} + if err := r.Get(ctx, req.NamespacedName, management); err != nil { + if apierrors.IsNotFound(err) { + l.Info("Management config not found, ignoring since object must be deleted") + return ctrl.Result{}, nil + } + l.Error(err, "Failed to get Management") + return ctrl.Result{}, err + } + var errs error + detectedProviders := hmc.ProvidersStatus{} + detectedComponents := make(map[string]hmc.ComponentStatus) + + for _, component := range management.Spec.Components { + template := &hmc.Template{} + err := r.Get(ctx, types.NamespacedName{ + Namespace: hmc.TemplatesNamespace, + Name: component.Template, + }, template) + if err != nil { + errMsg := fmt.Sprintf("Failed to get Template %s/%s: %s", hmc.TemplatesNamespace, component.Template, err) + updateComponentsStatus(detectedComponents, &detectedProviders, component.Template, template.Status, errMsg) + errs = errors.Join(fmt.Errorf(errMsg)) + continue + } + if !template.Status.Valid { + errMsg := fmt.Sprintf("Template %s/%s is not marked as valid", hmc.TemplatesNamespace, component.Template) + updateComponentsStatus(detectedComponents, &detectedProviders, component.Template, template.Status, errMsg) + errs = errors.Join(fmt.Errorf(errMsg)) + continue + } + + // Applying defaults + if component.Config == nil && template.Status.Config != nil { + component.Config = &apiextensionsv1.JSON{Raw: template.Status.Config.Raw} + } + + ownerRef := metav1.OwnerReference{ + APIVersion: hmc.GroupVersion.String(), + Kind: hmc.ManagementKind, + Name: management.Name, + UID: management.UID, + } + _, err = helm.ReconcileHelmRelease(ctx, r.Client, component.Template, management.Namespace, component.Config, + ownerRef, template.Status.ChartRef, defaultReconcileInterval) + if err != nil { + errMsg := fmt.Sprintf("error reconciling HelmRelease %s/%s: %s", management.Namespace, component.Template, err) + updateComponentsStatus(detectedComponents, &detectedProviders, component.Template, template.Status, errMsg) + errs = errors.Join(fmt.Errorf(errMsg)) + continue + } + updateComponentsStatus(detectedComponents, &detectedProviders, component.Template, template.Status, "") + } + if errs != nil { + l.Error(errs, "Multiple errors during Management reconciliation") + } + + management.Status.ObservedGeneration = management.Generation + management.Status.Providers = detectedProviders + management.Status.Components = detectedComponents + if err := r.Status().Update(ctx, management); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to update status for Management %s/%s: %w", management.Namespace, management.Name, err) + } return ctrl.Result{}, nil } +func updateComponentsStatus( + components map[string]hmc.ComponentStatus, + providers *hmc.ProvidersStatus, + componentName string, + templateStatus hmc.TemplateStatus, + err string) { + + components[componentName] = hmc.ComponentStatus{ + Error: err, + Success: err == "", + } + + if err == "" { + providers.InfrastructureProviders = append(providers.InfrastructureProviders, templateStatus.InfrastructureProviders...) + providers.BootstrapProviders = append(providers.BootstrapProviders, templateStatus.BootstrapProviders...) + providers.ControlPlaneProviders = append(providers.ControlPlaneProviders, templateStatus.ControlPlaneProviders...) + } +} + // SetupWithManager sets up the controller with the Manager. func (r *ManagementReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&hmcmirantiscomv1alpha1.Management{}). + For(&hmc.Management{}). Complete(r) } diff --git a/internal/helm/release.go b/internal/helm/release.go new file mode 100644 index 000000000..e7fb1e1dc --- /dev/null +++ b/internal/helm/release.go @@ -0,0 +1,45 @@ +package helm + +import ( + "context" + "time" + + hcv2 "github.com/fluxcd/helm-controller/api/v2" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func ReconcileHelmRelease( + ctx context.Context, + cl client.Client, + name string, + namespace string, + values *apiextensionsv1.JSON, + ownerReference metav1.OwnerReference, + chartRef *hcv2.CrossNamespaceSourceReference, + reconcileInterval time.Duration, +) (*hcv2.HelmRelease, error) { + helmRelease := &hcv2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + + _, err := ctrl.CreateOrUpdate(ctx, cl, helmRelease, func() error { + helmRelease.OwnerReferences = []metav1.OwnerReference{ownerReference} + helmRelease.Spec = hcv2.HelmReleaseSpec{ + ChartRef: chartRef, + Interval: metav1.Duration{Duration: reconcileInterval}, + ReleaseName: name, + Values: values, + } + return nil + }) + if err != nil { + return nil, err + } + return helmRelease, nil +} diff --git a/templates/hmc/templates/awsprovider-editor-rbac.yaml b/templates/hmc/templates/awsprovider-editor-rbac.yaml index 426bc30ec..4e44ac1df 100644 --- a/templates/hmc/templates/awsprovider-editor-rbac.yaml +++ b/templates/hmc/templates/awsprovider-editor-rbac.yaml @@ -6,7 +6,7 @@ metadata: {{- include "hmc.labels" . | nindent 4 }} rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - awsproviders verbs: @@ -18,7 +18,7 @@ rules: - update - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - awsproviders/status verbs: diff --git a/templates/hmc/templates/awsprovider-viewer-rbac.yaml b/templates/hmc/templates/awsprovider-viewer-rbac.yaml index 5c71a39fd..9c80267bc 100644 --- a/templates/hmc/templates/awsprovider-viewer-rbac.yaml +++ b/templates/hmc/templates/awsprovider-viewer-rbac.yaml @@ -6,7 +6,7 @@ metadata: {{- include "hmc.labels" . | nindent 4 }} rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - awsproviders verbs: @@ -14,7 +14,7 @@ rules: - list - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - awsproviders/status verbs: diff --git a/templates/hmc/templates/deployment-editor-rbac.yaml b/templates/hmc/templates/deployment-editor-rbac.yaml index 88b8919e7..ac9b60b2a 100644 --- a/templates/hmc/templates/deployment-editor-rbac.yaml +++ b/templates/hmc/templates/deployment-editor-rbac.yaml @@ -6,7 +6,7 @@ metadata: {{- include "hmc.labels" . | nindent 4 }} rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - deployments verbs: @@ -18,7 +18,7 @@ rules: - update - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - deployments/status verbs: diff --git a/templates/hmc/templates/deployment-viewer-rbac.yaml b/templates/hmc/templates/deployment-viewer-rbac.yaml index 7af1a91b6..3c28a89cc 100644 --- a/templates/hmc/templates/deployment-viewer-rbac.yaml +++ b/templates/hmc/templates/deployment-viewer-rbac.yaml @@ -6,7 +6,7 @@ metadata: {{- include "hmc.labels" . | nindent 4 }} rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - deployments verbs: @@ -14,7 +14,7 @@ rules: - list - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - deployments/status verbs: diff --git a/templates/hmc/templates/management-crd.yaml b/templates/hmc/templates/management-crd.yaml index 28b3017ec..a5deadc0a 100644 --- a/templates/hmc/templates/management-crd.yaml +++ b/templates/hmc/templates/management-crd.yaml @@ -40,13 +40,71 @@ spec: spec: description: ManagementSpec defines the desired state of Management properties: - foo: - description: Foo is an example field of Management. Edit management_types.go - to remove/update - type: string + components: + description: Components is the list of supported management components + items: + description: Component represents HMC management component + properties: + config: + description: |- + Config allows to provide parameters for management component customization. + If no Config provided, the field will be populated with the default + values for the template. + x-kubernetes-preserve-unknown-fields: true + template: + description: Template is the name of the Template associated with + this component + type: string + required: + - template + type: object + type: array type: object status: description: ManagementStatus defines the observed state of Management + properties: + components: + additionalProperties: + description: ComponentStatus is the status of Management component + installation + properties: + error: + description: Error stores as error message in case of failed installation + type: string + success: + description: Success represents if a component installation was + successful + type: boolean + type: object + description: Components contains the map with the status of Management + components installation + type: object + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + providers: + description: Providers is the list of discovered supported providers + properties: + bootstrap: + description: BootstrapProviders is the list of discovered bootstrap + providers + items: + type: string + type: array + controlPlane: + description: ControlPlaneProviders is the list of discovered control + plane providers + items: + type: string + type: array + infrastructure: + description: InfrastructureProviders is the list of discovered infrastructure + providers + items: + type: string + type: array + type: object type: object type: object served: true diff --git a/templates/hmc/templates/management-editor-rbac.yaml b/templates/hmc/templates/management-editor-rbac.yaml index 243f40fd1..bd04c7da6 100644 --- a/templates/hmc/templates/management-editor-rbac.yaml +++ b/templates/hmc/templates/management-editor-rbac.yaml @@ -6,7 +6,7 @@ metadata: {{- include "hmc.labels" . | nindent 4 }} rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - managements verbs: @@ -18,7 +18,7 @@ rules: - update - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - managements/status verbs: diff --git a/templates/hmc/templates/management-viewer-rbac.yaml b/templates/hmc/templates/management-viewer-rbac.yaml index 90c102bbb..9ea69d43a 100644 --- a/templates/hmc/templates/management-viewer-rbac.yaml +++ b/templates/hmc/templates/management-viewer-rbac.yaml @@ -6,7 +6,7 @@ metadata: {{- include "hmc.labels" . | nindent 4 }} rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - managements verbs: @@ -14,7 +14,7 @@ rules: - list - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - managements/status verbs: diff --git a/templates/hmc/templates/template-crd.yaml b/templates/hmc/templates/template-crd.yaml index 05872e0b9..4a8acc1be 100644 --- a/templates/hmc/templates/template-crd.yaml +++ b/templates/hmc/templates/template-crd.yaml @@ -159,7 +159,7 @@ spec: enum: - deployment - provider - - management + - core type: string valid: description: Valid indicates whether the template passed validation diff --git a/templates/hmc/templates/template-editor-rbac.yaml b/templates/hmc/templates/template-editor-rbac.yaml index 398363f19..45065be28 100644 --- a/templates/hmc/templates/template-editor-rbac.yaml +++ b/templates/hmc/templates/template-editor-rbac.yaml @@ -6,7 +6,7 @@ metadata: {{- include "hmc.labels" . | nindent 4 }} rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - templates verbs: @@ -18,7 +18,7 @@ rules: - update - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - templates/status verbs: diff --git a/templates/hmc/templates/template-viewer-rbac.yaml b/templates/hmc/templates/template-viewer-rbac.yaml index 127ca4b29..f609cd9a8 100644 --- a/templates/hmc/templates/template-viewer-rbac.yaml +++ b/templates/hmc/templates/template-viewer-rbac.yaml @@ -6,7 +6,7 @@ metadata: {{- include "hmc.labels" . | nindent 4 }} rules: - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - templates verbs: @@ -14,7 +14,7 @@ rules: - list - watch - apiGroups: - - hmc.mirantis.com.hmc.mirantis.com + - hmc.mirantis.com resources: - templates/status verbs: