diff --git a/charts/cluster-addons/templates/ccm-openstack/job.yaml b/charts/cluster-addons/templates/ccm-openstack/job.yaml index 236b126..29cba9b 100644 --- a/charts/cluster-addons/templates/ccm-openstack/job.yaml +++ b/charts/cluster-addons/templates/ccm-openstack/job.yaml @@ -13,6 +13,9 @@ spec: metadata: labels: {{ include "cluster-addons.componentSelectorLabels" (list . "ccm-openstack") | nindent 8 }} spec: + # Ensure that we run as a non-root user + securityContext: + runAsUser: 1001 serviceAccountName: {{ include "cluster-addons.componentName" (list . "deployer") }} restartPolicy: OnFailure containers: diff --git a/charts/cluster-addons/templates/cni-calico/job.yaml b/charts/cluster-addons/templates/cni-calico/job.yaml index 7913242..8439c49 100644 --- a/charts/cluster-addons/templates/cni-calico/job.yaml +++ b/charts/cluster-addons/templates/cni-calico/job.yaml @@ -13,6 +13,9 @@ spec: metadata: labels: {{ include "cluster-addons.componentSelectorLabels" (list . "cni-calico") | nindent 8 }} spec: + # Ensure that we run as a non-root user + securityContext: + runAsUser: 1001 serviceAccountName: {{ include "cluster-addons.componentName" (list . "deployer") }} restartPolicy: OnFailure containers: diff --git a/charts/cluster-addons/templates/cni-weave/job.yaml b/charts/cluster-addons/templates/cni-weave/job.yaml index 34c7bce..aeda31a 100644 --- a/charts/cluster-addons/templates/cni-weave/job.yaml +++ b/charts/cluster-addons/templates/cni-weave/job.yaml @@ -13,6 +13,9 @@ spec: metadata: labels: {{ include "cluster-addons.componentSelectorLabels" (list . "cni-weave") | nindent 8 }} spec: + # Ensure that we run as a non-root user + securityContext: + runAsUser: 1001 serviceAccountName: {{ include "cluster-addons.componentName" (list . "deployer") }} restartPolicy: OnFailure containers: diff --git a/charts/cluster-addons/templates/nvidia-gpu-operator/job.yaml b/charts/cluster-addons/templates/nvidia-gpu-operator/job.yaml index 11e263e..63a94a3 100644 --- a/charts/cluster-addons/templates/nvidia-gpu-operator/job.yaml +++ b/charts/cluster-addons/templates/nvidia-gpu-operator/job.yaml @@ -13,6 +13,9 @@ spec: metadata: labels: {{ include "cluster-addons.componentSelectorLabels" (list . "nvidia-gpu-operator") | nindent 8 }} spec: + # Ensure that we run as a non-root user + securityContext: + runAsUser: 1001 serviceAccountName: {{ include "cluster-addons.componentName" (list . "deployer") }} restartPolicy: OnFailure containers: diff --git a/charts/openstack-cluster/templates/addons/job.yaml b/charts/openstack-cluster/templates/addons/job.yaml new file mode 100644 index 0000000..c716969 --- /dev/null +++ b/charts/openstack-cluster/templates/addons/job.yaml @@ -0,0 +1,81 @@ +{{- if .Values.addons.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "openstack-cluster.componentName" (list . "addons") }}-{{ .Release.Revision }} + labels: {{ include "openstack-cluster.componentLabels" (list . "addons") | nindent 4 }} +spec: + # Keep trying for a decent amount of time before failing + backoffLimit: 1000 + # Keep succeeded jobs for 5m after finishing + ttlSecondsAfterFinished: 300 + template: + metadata: + labels: + capi.stackhpc.com/cluster: {{ include "openstack-cluster.clusterName" . }} + capi.stackhpc.com/component: addons + spec: + # Ensure that we run as a non-root user + securityContext: + runAsUser: 1001 + # Specifically request not to have the token for the default service account mounted + # in case it interferes with the kubeconfig for the target cluster + automountServiceAccountToken: false + restartPolicy: OnFailure + containers: + - name: addons + image: {{ printf "%s:%s" .Values.addons.jobImage.repository (default .Chart.AppVersion .Values.addons.jobImage.tag) }} + imagePullPolicy: {{ .Values.addons.jobImage.pullPolicy }} + env: + - name: KUBECONFIG + value: /kubeconfig/value + args: + - helm + - upgrade + - {{ .Values.addons.release.name }} + - {{ .Values.addons.chart.name }} + - --atomic + - --install + - --namespace + - {{ .Values.addons.release.namespace }} + - --create-namespace + - --repo + - {{ .Values.addons.chart.repo }} + - --version + - {{ default .Chart.Version .Values.addons.chart.version }} + # Include the clouds.yaml first + - --values + - /cloud-config/clouds.yaml + # Then add the cloud certificate if defined + {{- if .Values.cloudCACert }} + - --set-file + - cloudCACert=/cloud-config/cacert + {{- end }} + # Lastly include the user-specified values + - --values + - /config/values.yaml + - --wait + - --wait-for-jobs + - --timeout + - {{ .Values.addons.release.timeout }} + volumeMounts: + - name: kubeconfig + mountPath: /kubeconfig + readOnly: true + - name: cloud-config + mountPath: /cloud-config + readOnly: true + - name: helm-values + mountPath: /config + readOnly: true + volumes: + - name: kubeconfig + secret: + secretName: {{ include "openstack-cluster.componentName" (list . "kubeconfig") }} + - name: cloud-config + secret: + secretName: {{ include "openstack-cluster.componentName" (list . "cloud-config") }} + - name: helm-values + secret: + secretName: {{ include "openstack-cluster.componentName" (list . "addons-values") }} +{{- end }} diff --git a/charts/openstack-cluster/templates/addons/secret.yaml b/charts/openstack-cluster/templates/addons/secret.yaml new file mode 100644 index 0000000..eafa37d --- /dev/null +++ b/charts/openstack-cluster/templates/addons/secret.yaml @@ -0,0 +1,10 @@ +{{- if .Values.addons.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "openstack-cluster.componentName" (list . "addons-values") }} + labels: {{ include "openstack-cluster.componentLabels" (list . "addons") | nindent 4 }} +stringData: + values.yaml: | + {{- toYaml .Values.addons.values | nindent 4 }} +{{- end }} diff --git a/charts/openstack-cluster/templates/control-plane/kubeadm-control-plane.yaml b/charts/openstack-cluster/templates/control-plane/kubeadm-control-plane.yaml new file mode 100644 index 0000000..a2bc501 --- /dev/null +++ b/charts/openstack-cluster/templates/control-plane/kubeadm-control-plane.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: controlplane.cluster.x-k8s.io/v1alpha4 +kind: KubeadmControlPlane +metadata: + name: {{ include "openstack-cluster.componentName" (list . "control-plane") }} + labels: {{ include "openstack-cluster.componentLabels" (list . "control-plane") | nindent 4 }} +spec: + version: {{ .Values.kubernetesVersion | required ".Values.kubernetesVersion is required" }} + replicas: {{ .Values.controlPlane.machineCount }} + machineTemplate: + infrastructureRef: + kind: OpenStackMachineTemplate + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha4 + name: {{ include "openstack-cluster.componentName" (list . "control-plane") }} + kubeadmConfigSpec: + initConfiguration: + nodeRegistration: + name: '{{ "{{" }} local_hostname {{ "}}" }}' + kubeletExtraArgs: + cloud-provider: external + clusterConfiguration: + imageRepository: {{ .Values.kubeImageRepository }} + apiServer: + extraArgs: + cloud-provider: external + controllerManager: + extraArgs: + cloud-provider: external + joinConfiguration: + nodeRegistration: + name: '{{ "{{" }} local_hostname {{ "}}" }}' + kubeletExtraArgs: + cloud-provider: external diff --git a/charts/openstack-cluster/templates/control-plane/openstack-machine-template.yaml b/charts/openstack-cluster/templates/control-plane/openstack-machine-template.yaml new file mode 100644 index 0000000..bc8bbda --- /dev/null +++ b/charts/openstack-cluster/templates/control-plane/openstack-machine-template.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha4 +kind: OpenStackMachineTemplate +metadata: + name: {{ include "openstack-cluster.componentName" (list . "control-plane") }} + labels: {{ include "openstack-cluster.componentLabels" (list . "control-plane") | nindent 4 }} +spec: + template: + spec: + identityRef: + kind: Secret + name: {{ include "openstack-cluster.componentName" (list . "cloud-config") }} + cloudName: openstack + image: {{ .Values.machineImage | required ".Values.machineImage is required" }} + flavor: {{ .Values.controlPlane.machineFlavor | required ".Values.controlPlane.machineFlavor is required" }} + sshKeyName: {{ .Values.machineSSHKeyName | required ".Values.machineSSHKeyName is required" }} diff --git a/charts/openstack-cluster/templates/node-group/kubeadm-config-template.yaml b/charts/openstack-cluster/templates/node-group/kubeadm-config-template.yaml new file mode 100644 index 0000000..2f9a94f --- /dev/null +++ b/charts/openstack-cluster/templates/node-group/kubeadm-config-template.yaml @@ -0,0 +1,16 @@ +{{- range $nodeGroup := .Values.nodeGroups }} +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1alpha4 +kind: KubeadmConfigTemplate +metadata: + name: {{ include "openstack-cluster.componentName" (list $ $nodeGroup.name) }} + labels: {{ include "openstack-cluster.nodeGroupLabels" (list $ $nodeGroup.name) | nindent 4 }} +spec: + template: + spec: + joinConfiguration: + nodeRegistration: + name: '{{ "{{" }} local_hostname {{ "}}" }}' + kubeletExtraArgs: + cloud-provider: external +{{- end }} diff --git a/charts/openstack-cluster/templates/node-group/machine-deployment.yaml b/charts/openstack-cluster/templates/node-group/machine-deployment.yaml new file mode 100644 index 0000000..f94811c --- /dev/null +++ b/charts/openstack-cluster/templates/node-group/machine-deployment.yaml @@ -0,0 +1,29 @@ +{{- range $nodeGroup := .Values.nodeGroups }} +--- +apiVersion: cluster.x-k8s.io/v1alpha4 +kind: MachineDeployment +metadata: + name: {{ include "openstack-cluster.componentName" (list $ $nodeGroup.name) }} + labels: {{ include "openstack-cluster.nodeGroupLabels" (list $ $nodeGroup.name) | nindent 4 }} +spec: + clusterName: {{ include "openstack-cluster.clusterName" $ }} + replicas: {{ $nodeGroup.machineCount | required (printf "no machine count specified for node group '%s'" $nodeGroup.name) }} + selector: + matchLabels: {{ include "openstack-cluster.nodeGroupSelectorLabels" (list $ $nodeGroup.name) | nindent 6 }} + template: + metadata: + labels: {{ include "openstack-cluster.nodeGroupSelectorLabels" (list $ $nodeGroup.name) | nindent 8 }} + spec: + clusterName: {{ include "openstack-cluster.clusterName" $ }} + version: {{ $.Values.kubernetesVersion }} + failureDomain: {{ pluck "failureDomain" $nodeGroup $.Values.nodeGroupDefaults | first }} + bootstrap: + configRef: + apiVersion: bootstrap.cluster.x-k8s.io/v1alpha4 + kind: KubeadmConfigTemplate + name: {{ include "openstack-cluster.componentName" (list $ $nodeGroup.name) }} + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha4 + kind: OpenStackMachineTemplate + name: {{ include "openstack-cluster.componentName" (list $ $nodeGroup.name) }} +{{- end }} diff --git a/charts/openstack-cluster/templates/node-group/openstack-machine-template.yaml b/charts/openstack-cluster/templates/node-group/openstack-machine-template.yaml new file mode 100644 index 0000000..0b9d503 --- /dev/null +++ b/charts/openstack-cluster/templates/node-group/openstack-machine-template.yaml @@ -0,0 +1,18 @@ +{{- range $nodeGroup := .Values.nodeGroups }} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha4 +kind: OpenStackMachineTemplate +metadata: + name: {{ include "openstack-cluster.componentName" (list $ $nodeGroup.name) }} + labels: {{ include "openstack-cluster.nodeGroupLabels" (list $ $nodeGroup.name) | nindent 4 }} +spec: + template: + spec: + identityRef: + kind: Secret + name: {{ include "openstack-cluster.componentName" (list $ "cloud-config") }} + cloudName: openstack + image: {{ $.Values.machineImage | required ".Values.machineImage is required" }} + flavor: {{ pluck "machineFlavor" $nodeGroup $.Values.nodeGroupDefaults | first | required (printf "no flavor specified for node group '%s'" $nodeGroup.name) }} + sshKeyName: {{ $.Values.machineSSHKeyName | required ".Values.machineSSHKeyName is required" }} +{{- end }} diff --git a/charts/openstack-cluster/values.yaml b/charts/openstack-cluster/values.yaml index 70d423c..808900a 100644 --- a/charts/openstack-cluster/values.yaml +++ b/charts/openstack-cluster/values.yaml @@ -95,7 +95,7 @@ addons: # Indicates if cluster addons should be deployed enabled: true # The image to use for the job that deploys the addons - image: + jobImage: repository: ghcr.io/stackhpc/k8s-utils tag: # defaults to chart appVersion pullPolicy: IfNotPresent @@ -103,7 +103,7 @@ addons: chart: repo: https://stackhpc.github.io/capi-helm-charts name: cluster-addons - version: 0.1.0-dev.0.main.32 + version: # This defaults to the version that matches this chart # Release details for the addons deployment release: namespace: capi-system @@ -111,5 +111,5 @@ addons: timeout: 30m # Values for the addons # See https://github.com/stackhpc/capi-helm-charts/blob/main/charts/cluster-addons for details - # The clouds.yaml used for cluster deployment will be given in addition + # The clouds.yaml used for cluster deployment will be given in addition to these values: {} diff --git a/scripts/publish-charts.py b/scripts/publish-charts.py index a6f9113..f1aaf1e 100755 --- a/scripts/publish-charts.py +++ b/scripts/publish-charts.py @@ -190,28 +190,27 @@ def main(): commit_files = cmd(["git", "show", "--pretty=", "--name-only", commit]) changed_paths = [pathlib.Path(filename).resolve() for filename in commit_files.splitlines()] - # Get the charts in the repository - charts = [path.parent for path in chart_directory.glob('**/Chart.yaml')] - - # Decide which charts we are actually going to publish + # Determine whether to publish charts or not + # Because there are dependencies that are not actual Helm dependencies, charts are + # either all published together or not at all if is_tag: # If the commit is a tag, publish all the charts regardless of changes # so that they get the version bump - print("[INFO] Detected tagged commit - publishing all charts") + print("[INFO] Detected tagged commit - publishing charts") elif is_changed(image_directory, changed_paths): # If the image was changed, publish all the charts regardless of changes # so that they pick up the new image for any deployment jobs - print("[INFO] Image for deploy jobs has changed - publishing all charts") + print("[INFO] Image for deploy jobs has changed - publishing charts") + elif is_changed(chart_directory, changed_paths): + # If any of the charts changed, publish all the charts + print("[INFO] At least one chart has changed - publishing charts") else: - # In all other cases, just publish the charts that changed - print("[INFO] Publishing changed charts only") - charts = [chart for chart in charts if is_changed(chart, changed_paths)] - if charts: - print(f"[INFO] Publishing {len(charts)} charts") - else: - print("[INFO] No charts to publish - exiting") + print("[INFO] Nothing has changed - exiting without publishing charts") return + # Get the charts in the repository + charts = [path.parent for path in chart_directory.glob('**/Chart.yaml')] + # Publish the charts and re-generate the repository index publish_branch = os.environ.get('PUBLISH_BRANCH', 'gh-pages') print(f"[INFO] Charts will be published to branch '{publish_branch}'")