diff --git a/README.md b/README.md index 0c5660eaa..537b0e84e 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ or install using `helm` helm install hmc oci://ghcr.io/mirantis/hmc/charts/hmc --version 0.0.5 -n hmc-system --create-namespace ``` -Then follow the [Deploy a managed cluster](#deploy-a-managed-cluster) guide to -create a managed cluster. +Then follow the [Deploy a cluster deployment](#deploy-a-cluster-deployment) guide to +create a cluster deployment. > [!NOTE] > The HMC installation using Kubernetes manifests does not allow @@ -51,7 +51,7 @@ Mirantis Hybrid Container Cloud requires the following: Optionally, the following CLIs may be helpful: 1. `helm` (required only when installing HMC using `helm`). -2. `clusterctl` (to handle the lifecycle of the managed clusters). +2. `clusterctl` (to handle the lifecycle of the cluster deployments). ### Providers configuration @@ -109,9 +109,9 @@ own `Management` configuration: `kubectl --kubeconfig create -f management.yaml` -## Deploy a managed cluster +## Create a ClusterDeployment -To deploy a managed cluster: +To create a ClusterDeployment: 1. Create `Credential` object with all credentials required. @@ -173,7 +173,7 @@ kubectl -n get cluster -o > cluster -n --show-conditions > all ``` -6. Retrieve the `kubeconfig` of your managed cluster: +6. Retrieve the `kubeconfig` of your cluster deployment: ``` kubectl get secret -n hmc-system -kubeconfig -o=jsonpath={.data.value} | base64 -d > kubeconfig diff --git a/api/v1alpha1/managedcluster_types.go b/api/v1alpha1/clusterdeployment_types.go similarity index 99% rename from api/v1alpha1/managedcluster_types.go rename to api/v1alpha1/clusterdeployment_types.go index 9e1434f03..e811b9801 100644 --- a/api/v1alpha1/managedcluster_types.go +++ b/api/v1alpha1/clusterdeployment_types.go @@ -106,7 +106,7 @@ type ClusterDeploymentStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status -// +kubebuilder:resource:shortName=mcluster;mcl +// +kubebuilder:resource:shortName=clusterd;cld // +kubebuilder:printcolumn:name="ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="Ready",priority=0 // +kubebuilder:printcolumn:name="status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description="Status",priority=0 // +kubebuilder:printcolumn:name="dryRun",type="string",JSONPath=".spec.dryRun",description="Dry Run",priority=1 diff --git a/api/v1alpha1/indexers.go b/api/v1alpha1/indexers.go index f1d87733e..222ce8b69 100644 --- a/api/v1alpha1/indexers.go +++ b/api/v1alpha1/indexers.go @@ -42,7 +42,7 @@ func SetupIndexers(ctx context.Context, mgr ctrl.Manager) error { return merr } -// managed cluster +// cluster deployment // ClusterDeploymentTemplateIndexKey indexer field name to extract ClusterTemplate name reference from a ClusterDeployment object. const ClusterDeploymentTemplateIndexKey = ".spec.template" @@ -89,12 +89,12 @@ func ExtractServiceTemplateNamesFromClusterDeployment(rawObj client.Object) []st const ClusterDeploymentCredentialIndexKey = ".spec.credential" func setupClusterDeploymentCredentialIndexer(ctx context.Context, mgr ctrl.Manager) error { - return mgr.GetFieldIndexer().IndexField(ctx, &ClusterDeployment{}, ClusterDeploymentCredentialIndexKey, ExtractCredentialNameFromClusterDeployment) + return mgr.GetFieldIndexer().IndexField(ctx, &ClusterDeployment{}, ClusterDeploymentCredentialIndexKey, extractCredentialNameFromClusterDeployment) } -// ExtractCredentialNameFromClusterDeployment returns referenced Credential name +// extractCredentialNameFromClusterDeployment returns referenced Credential name // declared in a ClusterDeployment object. -func ExtractCredentialNameFromClusterDeployment(rawObj client.Object) []string { +func extractCredentialNameFromClusterDeployment(rawObj client.Object) []string { cluster, ok := rawObj.(*ClusterDeployment) if !ok { return nil diff --git a/config/dev/adopted-clusterdeployment.yaml b/config/dev/adopted-clusterdeployment.yaml new file mode 100644 index 000000000..c20e819df --- /dev/null +++ b/config/dev/adopted-clusterdeployment.yaml @@ -0,0 +1,16 @@ +apiVersion: hmc.mirantis.com/v1alpha1 +kind: ClusterDeployment +metadata: + name: adopted-dev + namespace: ${NAMESPACE} +spec: + template: adopted-cluster-0-0-1 + credential: adopted-cluster-cred + config: {} + services: + - template: kyverno-3-2-6 + name: kyverno + namespace: kyverno + - template: ingress-nginx-4-11-0 + name: ingress-nginx + namespace: ingress-nginx \ No newline at end of file diff --git a/config/dev/adopted-credentials.yaml b/config/dev/adopted-credentials.yaml new file mode 100644 index 000000000..bdd68f0bf --- /dev/null +++ b/config/dev/adopted-credentials.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +data: + value: ${KUBECONFIG_DATA} +kind: Secret +metadata: + name: adopted-cluster-kubeconf + namespace: ${NAMESPACE} +type: Opaque +--- +apiVersion: hmc.mirantis.com/v1alpha1 +kind: Credential +metadata: + name: adopted-cluster-cred + namespace: ${NAMESPACE} +spec: + description: Adopted Credentials + identityRef: + apiVersion: v1 + kind: Secret + name: adopted-cluster-kubeconf + namespace: ${NAMESPACE} \ No newline at end of file diff --git a/docs/dev.md b/docs/dev.md index e96b629ae..0823cc27a 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -96,7 +96,7 @@ another provider change `DEV_PROVIDER` variable with the name of provider before running make (e.g. `export DEV_PROVIDER=azure`). 1. Configure your cluster parameters in provider specific file - (for example `config/dev/aws-clusterDeployment.yaml` in case of AWS): + (for example `config/dev/aws-clusterdeployment.yaml` in case of AWS): * Configure the `name` of the ClusterDeployment * Change instance type or size for control plane and worker machines diff --git a/internal/controller/clusterdeployment_controller.go b/internal/controller/clusterdeployment_controller.go index 1cc7b3c08..4d2c3f938 100644 --- a/internal/controller/clusterdeployment_controller.go +++ b/internal/controller/clusterdeployment_controller.go @@ -104,9 +104,7 @@ func (r *ClusterDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Re return r.reconcileUpdate(ctx, clusterDeployment) } -func (r *ClusterDeploymentReconciler) setStatusFromChildObjects( - ctx context.Context, clusterDeployment *hmc.ClusterDeployment, gvr schema.GroupVersionResource, conditions []string, -) (requeue bool, _ error) { +func (r *ClusterDeploymentReconciler) setStatusFromChildObjects(ctx context.Context, clusterDeployment *hmc.ClusterDeployment, gvr schema.GroupVersionResource, conditions []string) (requeue bool, _ error) { l := ctrl.LoggerFrom(ctx) resourceConditions, err := status.GetResourceConditions(ctx, clusterDeployment.Namespace, r.DynamicClient, gvr, diff --git a/internal/controller/management_controller.go b/internal/controller/management_controller.go index 89be1667c..102cad860 100644 --- a/internal/controller/management_controller.go +++ b/internal/controller/management_controller.go @@ -116,7 +116,7 @@ func (r *ManagementReconciler) Update(ctx context.Context, management *hmc.Manag errs error statusAccumulator = &mgmtStatusAccumulator{ - providers: hmc.Providers{}, + providers: hmc.Providers{"infrastructure-internal"}, components: make(map[string]hmc.ComponentStatus), compatibilityContracts: make(map[string]hmc.CompatibilityContracts), } @@ -124,8 +124,6 @@ func (r *ManagementReconciler) Update(ctx context.Context, management *hmc.Manag requeue bool ) - statusAccumulator.providers = append(statusAccumulator.providers, "infrastructure-internal") - for _, component := range components { l.V(1).Info("reconciling components", "component", component) template := new(hmc.ProviderTemplate) diff --git a/templates/cluster/adopted-cluster/.helmignore b/templates/cluster/adopted-cluster/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/templates/cluster/adopted-cluster/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/templates/cluster/adopted-cluster/Chart.yaml b/templates/cluster/adopted-cluster/Chart.yaml new file mode 100644 index 000000000..71b73c4fc --- /dev/null +++ b/templates/cluster/adopted-cluster/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: adopted-cluster +description: | + An HMC template to adopt an already existing kubernetes cluster +type: application +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.0.1 +annotations: + cluster.x-k8s.io/provider: infrastructure-internal diff --git a/templates/cluster/adopted-cluster/templates/_helpers.tpl b/templates/cluster/adopted-cluster/templates/_helpers.tpl new file mode 100644 index 000000000..50922cc27 --- /dev/null +++ b/templates/cluster/adopted-cluster/templates/_helpers.tpl @@ -0,0 +1,4 @@ +{{- define "cluster.name" -}} + {{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- end }} + diff --git a/templates/cluster/adopted-cluster/templates/sveltoscluster.yaml b/templates/cluster/adopted-cluster/templates/sveltoscluster.yaml new file mode 100644 index 000000000..3c3c2aea1 --- /dev/null +++ b/templates/cluster/adopted-cluster/templates/sveltoscluster.yaml @@ -0,0 +1,7 @@ +apiVersion: lib.projectsveltos.io/v1beta1 +kind: SveltosCluster +metadata: + name: {{ include "cluster.name" . }} +spec: + consecutiveFailureThreshold: {{ .Values.consecutiveFailureThreshold }} + kubeconfigName: {{ .Values.clusterIdentity.name }} diff --git a/templates/cluster/adopted-cluster/values.schema.json b/templates/cluster/adopted-cluster/values.schema.json new file mode 100644 index 000000000..aaccae33d --- /dev/null +++ b/templates/cluster/adopted-cluster/values.schema.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "An HMC template to adopt an existing k8s cluster.", + "type": "object", + "required": [ + "clusterIdentity" + ], + "properties": { + "consecutiveFailureThreshold": { + "description": "The number of the failures prior to setting the status condition", + "type": "integer", + "minimum": 1 + } + } +} diff --git a/templates/cluster/adopted-cluster/values.yaml b/templates/cluster/adopted-cluster/values.yaml new file mode 100644 index 000000000..a0e9d4d82 --- /dev/null +++ b/templates/cluster/adopted-cluster/values.yaml @@ -0,0 +1,6 @@ +# Cluster parameters + +clusterIdentity: + name: "adopted-cluster-identity" + +consecutiveFailureThreshold: 3 diff --git a/templates/provider/hmc-templates/files/templates/adopted-cluster-0-0-1.yaml b/templates/provider/hmc-templates/files/templates/adopted-cluster-0-0-1.yaml new file mode 100644 index 000000000..d7a88a27b --- /dev/null +++ b/templates/provider/hmc-templates/files/templates/adopted-cluster-0-0-1.yaml @@ -0,0 +1,15 @@ +apiVersion: hmc.mirantis.com/v1alpha1 +kind: ClusterTemplate +metadata: + name: adopted-cluster-0-0-1 + annotations: + helm.sh/resource-policy: keep +spec: + helm: + chartSpec: + chart: adopted-cluster + version: 0.0.1 + interval: 10m0s + sourceRef: + kind: HelmRepository + name: hmc-templates diff --git a/templates/provider/hmc/templates/crds/hmc.mirantis.com_clusterdeployments.yaml b/templates/provider/hmc/templates/crds/hmc.mirantis.com_clusterdeployments.yaml new file mode 100644 index 000000000..44b90107f --- /dev/null +++ b/templates/provider/hmc/templates/crds/hmc.mirantis.com_clusterdeployments.yaml @@ -0,0 +1,295 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: clusterdeployments.hmc.mirantis.com +spec: + group: hmc.mirantis.com + names: + kind: ClusterDeployment + listKind: ClusterDeploymentList + plural: clusterdeployments + shortNames: + - clusterd + - cld + singular: clusterdeployment + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Ready + jsonPath: .status.conditions[?(@.type=="Ready")].status + name: ready + type: string + - description: Status + jsonPath: .status.conditions[?(@.type=="Ready")].message + name: status + type: string + - description: Dry Run + jsonPath: .spec.dryRun + name: dryRun + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterDeployment is the Schema for the ClusterDeployments API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ClusterDeploymentSpec defines the desired state of ClusterDeployment + properties: + config: + description: |- + Config allows to provide parameters for template customization. + If no Config provided, the field will be populated with the default values for + the template and DryRun will be enabled. + x-kubernetes-preserve-unknown-fields: true + credential: + description: Name reference to the related Credentials object. + type: string + dryRun: + description: DryRun specifies whether the template should be applied + after validation or only validated. + type: boolean + services: + description: |- + Services is a list of services created via ServiceTemplates + that could be installed on the target cluster. + items: + description: ServiceSpec represents a Service to be managed + properties: + disable: + description: Disable can be set to disable handling of this + service. + type: boolean + name: + description: Name is the chart release. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace the release will be installed in. + It will default to Name if not provided. + type: string + template: + description: Template is a reference to a Template object located + in the same namespace. + maxLength: 253 + minLength: 1 + type: string + values: + description: |- + Values is the helm values to be passed to the chart used by the template. + The string type is used in order to allow for templating. + type: string + required: + - name + - template + type: object + type: array + servicesPriority: + default: 100 + description: |- + ServicesPriority sets the priority for the services defined in this spec. + Higher value means higher priority and lower means lower. + In case of conflict with another object managing the service, + the one with higher priority will get to deploy its services. + format: int32 + maximum: 2147483646 + minimum: 1 + type: integer + stopOnConflict: + default: false + description: |- + StopOnConflict specifies what to do in case of a conflict. + E.g. If another object is already managing a service. + By default the remaining services will be deployed even if conflict is detected. + If set to true, the deployment will stop after encountering the first conflict. + type: boolean + template: + description: Template is a reference to a Template object located + in the same namespace. + maxLength: 253 + minLength: 1 + type: string + required: + - template + type: object + status: + description: ClusterDeploymentStatus defines the observed state of ClusterDeployment + properties: + availableUpgrades: + description: |- + AvailableUpgrades is the list of ClusterTemplate names to which + this cluster can be upgraded. It can be an empty array, which means no upgrades are + available. + items: + type: string + type: array + conditions: + description: Conditions contains details for the current state of + the ClusterDeployment. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + k8sVersion: + description: |- + Currently compatible exact Kubernetes version of the cluster. Being set only if + provided by the corresponding ClusterTemplate. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + services: + description: Services contains details for the state of services. + items: + description: ServiceStatus contains details for the state of services. + properties: + clusterName: + description: ClusterName is the name of the associated cluster. + type: string + clusterNamespace: + description: ClusterNamespace is the namespace of the associated + cluster. + type: string + conditions: + description: Conditions contains details for the current state + of managed services. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + required: + - clusterName + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/templates/provider/hmc/templates/deployment.yaml b/templates/provider/hmc/templates/deployment.yaml index e95b1cd95..31ae1004a 100644 --- a/templates/provider/hmc/templates/deployment.yaml +++ b/templates/provider/hmc/templates/deployment.yaml @@ -54,7 +54,6 @@ spec: port: 8081 initialDelaySeconds: 15 periodSeconds: 20 - timeoutSeconds: 6000 name: manager readinessProbe: httpGet: @@ -62,7 +61,6 @@ spec: port: 8081 initialDelaySeconds: 5 periodSeconds: 10 - timeoutSeconds: 6000 resources: {{- toYaml .Values.resources | nindent 10 }} securityContext: {{- toYaml .Values.containerSecurityContext diff --git a/test/objects/clusterdeployment/clusterdeployment.go b/test/objects/clusterdeployment/clusterdeployment.go index 2fcfb134f..777572d86 100644 --- a/test/objects/clusterdeployment/clusterdeployment.go +++ b/test/objects/clusterdeployment/clusterdeployment.go @@ -26,7 +26,7 @@ const ( DefaultNamespace = metav1.NamespaceDefault ) -type Opt func(ClusterDeployment *v1alpha1.ClusterDeployment) +type Opt func(clusterDeployment *v1alpha1.ClusterDeployment) func NewClusterDeployment(opts ...Opt) *v1alpha1.ClusterDeployment { p := &v1alpha1.ClusterDeployment{