From 4c2c1570d809871cce170a0cb8d065c3b624cec5 Mon Sep 17 00:00:00 2001 From: adrianc Date: Sun, 1 Sep 2024 16:11:42 +0300 Subject: [PATCH 1/2] Add helm chart for maintenance-operator Signed-off-by: adrianc --- Makefile | 1 + .../maintenance-operator-chart/.helmignore | 23 ++ .../maintenance-operator-chart/Chart.yaml | 6 + ...nvidia.com_maintenanceoperatorconfigs.yaml | 89 ++++++ ...intenance.nvidia.com_nodemaintenances.yaml | 284 ++++++++++++++++++ .../templates/_helpers.tpl | 62 ++++ .../templates/certificates.yaml | 43 +++ .../templates/deployment.yaml | 84 ++++++ .../templates/metrics-service.yaml | 18 ++ .../templates/operatorconfig.yaml | 21 ++ .../templates/role.yaml | 158 ++++++++++ .../templates/role_binding.yaml | 36 +++ .../templates/serviceaccount.yaml | 12 + .../templates/webhook.yaml | 48 +++ .../maintenance-operator-chart/values.yaml | 82 +++++ 15 files changed, 967 insertions(+) create mode 100644 deployment/maintenance-operator-chart/.helmignore create mode 100644 deployment/maintenance-operator-chart/Chart.yaml create mode 100644 deployment/maintenance-operator-chart/crds/maintenance.nvidia.com_maintenanceoperatorconfigs.yaml create mode 100644 deployment/maintenance-operator-chart/crds/maintenance.nvidia.com_nodemaintenances.yaml create mode 100644 deployment/maintenance-operator-chart/templates/_helpers.tpl create mode 100644 deployment/maintenance-operator-chart/templates/certificates.yaml create mode 100644 deployment/maintenance-operator-chart/templates/deployment.yaml create mode 100644 deployment/maintenance-operator-chart/templates/metrics-service.yaml create mode 100644 deployment/maintenance-operator-chart/templates/operatorconfig.yaml create mode 100644 deployment/maintenance-operator-chart/templates/role.yaml create mode 100644 deployment/maintenance-operator-chart/templates/role_binding.yaml create mode 100644 deployment/maintenance-operator-chart/templates/serviceaccount.yaml create mode 100644 deployment/maintenance-operator-chart/templates/webhook.yaml create mode 100644 deployment/maintenance-operator-chart/values.yaml diff --git a/Makefile b/Makefile index 3197654..678d1ed 100644 --- a/Makefile +++ b/Makefile @@ -121,6 +121,7 @@ clean: ## clean files .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + cp -f config/crd/bases/* deployment/maintenance-operator-chart/crds .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. diff --git a/deployment/maintenance-operator-chart/.helmignore b/deployment/maintenance-operator-chart/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/deployment/maintenance-operator-chart/.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/deployment/maintenance-operator-chart/Chart.yaml b/deployment/maintenance-operator-chart/Chart.yaml new file mode 100644 index 0000000..8ebbc68 --- /dev/null +++ b/deployment/maintenance-operator-chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: maintenance-operator-chart +description: Maintenance Operator Helm Chart +type: application +version: 0.0.1 +appVersion: "v0.0.1-main" diff --git a/deployment/maintenance-operator-chart/crds/maintenance.nvidia.com_maintenanceoperatorconfigs.yaml b/deployment/maintenance-operator-chart/crds/maintenance.nvidia.com_maintenanceoperatorconfigs.yaml new file mode 100644 index 0000000..24a29d6 --- /dev/null +++ b/deployment/maintenance-operator-chart/crds/maintenance.nvidia.com_maintenanceoperatorconfigs.yaml @@ -0,0 +1,89 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: maintenanceoperatorconfigs.maintenance.nvidia.com +spec: + group: maintenance.nvidia.com + names: + kind: MaintenanceOperatorConfig + listKind: MaintenanceOperatorConfigList + plural: maintenanceoperatorconfigs + singular: maintenanceoperatorconfig + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: MaintenanceOperatorConfig is the Schema for the maintenanceoperatorconfigs + 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: MaintenanceOperatorConfigSpec defines the desired state of + MaintenanceOperatorConfig + properties: + logLevel: + default: info + description: LogLevel is the operator logging level + enum: + - debug + - info + - error + type: string + maxNodeMaintenanceTimeSeconds: + default: 1600 + description: |- + MaxNodeMaintenanceTimeSeconds is the time from when a NodeMaintenance is marked as ready (phase: Ready) + until the NodeMaintenance is considered stale and removed by the operator. + should be less than idle time for any autoscaler that is running. + default to 30m (1600 seconds) + format: int32 + minimum: 0 + type: integer + maxParallelOperations: + anyOf: + - type: integer + - type: string + default: 1 + description: |- + MaxParallelOperations indicates the maximal number nodes that can undergo maintenance + at a given time. 0 means no limit + value can be an absolute number (ex: 5) or a percentage of total nodes in the cluster (ex: 10%). + absolute number is calculated from percentage by rounding up. + defaults to 1. The actual number of nodes that can undergo maintenance may be lower depending + on the value of MaintenanceOperatorConfigSpec.MaxUnavailable. + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + MaxUnavailable is the maximum number of nodes that can become unavailable in the cluster. + value can be an absolute number (ex: 5) or a percentage of total nodes in the cluster (ex: 10%). + absolute number is calculated from percentage by rounding up. + by default, unset. + new nodes will not be processed if the number of unavailable node will exceed this value + x-kubernetes-int-or-string: true + type: object + type: object + served: true + storage: true diff --git a/deployment/maintenance-operator-chart/crds/maintenance.nvidia.com_nodemaintenances.yaml b/deployment/maintenance-operator-chart/crds/maintenance.nvidia.com_nodemaintenances.yaml new file mode 100644 index 0000000..892289c --- /dev/null +++ b/deployment/maintenance-operator-chart/crds/maintenance.nvidia.com_nodemaintenances.yaml @@ -0,0 +1,284 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: nodemaintenances.maintenance.nvidia.com +spec: + group: maintenance.nvidia.com + names: + kind: NodeMaintenance + listKind: NodeMaintenanceList + plural: nodemaintenances + singular: nodemaintenance + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.nodeName + name: Node + type: string + - jsonPath: .spec.requestorID + name: Requestor + type: string + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=='Ready')].reason + name: Phase + type: string + - jsonPath: .status.conditions[?(@.type=='Failed')].reason + name: Failed + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: NodeMaintenance is the Schema for the nodemaintenances 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: NodeMaintenanceSpec defines the desired state of NodeMaintenance + properties: + additionalRequestors: + description: |- + AdditionalRequestors is a set of additional requestor IDs which are using the same NodeMaintenance + request. addition or removal of requiestor IDs to this list MUST be made with update operation (and retry on failure) + which will replace the entire list. + items: + type: string + type: array + x-kubernetes-list-type: set + cordon: + default: true + description: Cordon if set, marks node as unschedulable during maintenance + operation + type: boolean + drainSpec: + description: DrainSpec specifies how a node will be drained. if not + provided, no draining will be performed. + properties: + deleteEmptyDir: + default: false + description: |- + DeleteEmptyDir indicates if should continue even if there are pods using emptyDir + (local data that will be deleted when the node is drained) + type: boolean + force: + default: false + description: Force indicates if force draining is allowed + type: boolean + podEvictionFilters: + description: |- + PodEvictionFilters specifies filters for pods that need to undergo eviction during drain. + if specified. only pods that match PodEvictionFilters will be evicted during drain operation. + if unspecified. all non-daemonset pods will be evicted. + logical OR is performed between filter entires. logical AND is performed within different filters + in a filter entry. + items: + description: PodEvictionFiterEntry defines filters for Pod evictions + during drain operation + properties: + byResourceNameRegex: + description: ByResourceNameRegex filters pods by the name + of the resources they consume using regex. + type: string + type: object + type: array + podSelector: + description: |- + PodSelector specifies a label selector to filter pods on the node that need to be drained + For more details on label selectors, see: + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors + type: string + timeoutSeconds: + default: 300 + description: TimeoutSecond specifies the length of time in seconds + to wait before giving up drain, zero means infinite + format: int32 + minimum: 0 + type: integer + type: object + nodeName: + description: |- + NodeName is The name of the node that maintenance operation will be performed on + creation fails if node obj does not exist (webhook) + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + requestorID: + description: |- + RequestorID MUST follow domain name notation format (https://tools.ietf.org/html/rfc1035#section-2.3.1) + It MUST be 63 characters or less, beginning and ending with an alphanumeric + character ([a-z0-9A-Z]) with dashes (-), dots (.), and alphanumerics between. + caller SHOULD NOT create multiple objects with same requestorID and nodeName. + This field identifies the requestor of the operation. + maxLength: 63 + minLength: 2 + pattern: ^([a-z0-9A-Z]([-a-z0-9A-Z]*[a-z0-9A-Z])?(\.[a-z0-9A-Z]([-a-z0-9A-Z]*[a-z0-9A-Z])?)*)$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + waitForPodCompletion: + description: |- + WaitForPodCompletion specifies pods via selector to wait for completion before performing drain operation + if not provided, will not wait for pods to complete + properties: + podSelector: + description: |- + PodSelector specifies a label selector for the pods to wait for completion + For more details on label selectors, see: + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors + example: app=my-workloads + type: string + timeoutSeconds: + default: 0 + description: |- + TimeoutSecond specifies the length of time in seconds + to wait before giving up on pod termination, zero means infinite + format: int32 + minimum: 0 + type: integer + type: object + required: + - nodeName + - requestorID + type: object + status: + description: NodeMaintenanceStatus defines the observed state of NodeMaintenance + properties: + conditions: + description: Conditions represents observations of NodeMaintenance + current state + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + 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. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + 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 + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + drain: + description: Drain represents the drain status of the node + properties: + drainProgress: + description: DrainProgress represents the draining progress as + percentage + format: int32 + minimum: 0 + type: integer + evictionPods: + description: EvictionPods is the total number of pods that need + to be evicted at the time NodeMaintenance started draining + format: int32 + minimum: 0 + type: integer + totalPods: + description: TotalPods is the number of pods on the node at the + time NodeMaintenance started draining + format: int32 + minimum: 0 + type: integer + waitForEviction: + description: WaitForEviction is the list of namespaced named pods + that need to be evicted + items: + type: string + type: array + required: + - drainProgress + - evictionPods + - totalPods + type: object + waitForCompletion: + description: WaitForCompletion is the list of namespaced named pods + that we wait to complete + items: + type: string + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/deployment/maintenance-operator-chart/templates/_helpers.tpl b/deployment/maintenance-operator-chart/templates/_helpers.tpl new file mode 100644 index 0000000..ae44554 --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "maintenance-operator.name" -}} +{{- default "maintenance-operator" .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "maintenance-operator.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default "maintenance-operator" .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "maintenance-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "maintenance-operator.labels" -}} +helm.sh/chart: {{ include "maintenance-operator.chart" . }} +{{ include "maintenance-operator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "maintenance-operator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "maintenance-operator.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "maintenance-operator.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "maintenance-operator.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/deployment/maintenance-operator-chart/templates/certificates.yaml b/deployment/maintenance-operator-chart/templates/certificates.yaml new file mode 100644 index 0000000..68383b5 --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/certificates.yaml @@ -0,0 +1,43 @@ +{{- if and .Values.operator.admissionController.enable }} +{{- if and .Values.operator.admissionController.certificates.certManager.enable + .Values.operator.admissionController.certificates.certManager.generateSelfSigned }} +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: {{ include "maintenance-operator.fullname" . }}-selfsigned-issuer + namespace: {{ .Release.Namespace }} + labels: + {{- include "maintenance-operator.labels" . | nindent 4 }} +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ include "maintenance-operator.fullname" . }}-serving-cert + namespace: {{ .Release.Namespace }} + labels: + {{- include "maintenance-operator.labels" . | nindent 4 }} +spec: + dnsNames: + - '{{ include "maintenance-operator.fullname" . }}-webhook-service.{{ .Release.Namespace + }}.svc' + - '{{ include "maintenance-operator.fullname" . }}-webhook-service.{{ .Release.Namespace + }}.svc.{{ .Values.kubernetesClusterDomain }}' + issuerRef: + kind: Issuer + name: '{{ include "maintenance-operator.fullname" . }}-selfsigned-issuer' + secretName: {{ .Values.operator.admissionController.certificates.secretNames.operator }} +{{- else if and (not .Values.operator.admissionController.certManager.enable) .Values.operator.admissionController.custom.enable }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.operator.admissionController.certificates.secretNames.operator }} + namespace: {{ .Release.Namespace }} +type: Opaque +data: + ca.crt: {{ .Values.operator.admissionController.certificates.custom.operator.caCrt | b64enc | b64enc | quote }} + tls.crt: {{ .Values.operator.admissionController.certificates.custom.operator.tlsCrt | b64enc | quote }} + tls.key: {{ .Values.operator.admissionController.certificates.custom.operator.tlsKey | b64enc | quote }} +{{- end }} +{{- end }} diff --git a/deployment/maintenance-operator-chart/templates/deployment.yaml b/deployment/maintenance-operator-chart/templates/deployment.yaml new file mode 100644 index 0000000..83d19f1 --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "maintenance-operator.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: maintenance-operator + app.kubernetes.io/part-of: maintenance-operator + control-plane: {{ .Release.Name }}-controller-manager + {{- include "maintenance-operator.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.operator.replicas }} + selector: + matchLabels: + control-plane: {{ .Release.Name }}-controller-manager + {{- include "maintenance-operator.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + control-plane: {{ .Release.Name }}-controller-manager + {{- include "maintenance-operator.selectorLabels" . | nindent 8 }} + annotations: + kubectl.kubernetes.io/default-container: manager + spec: + tolerations: {{- toYaml .Values.operator.tolerations | nindent 8 }} + nodeSelector: {{- toYaml .Values.operator.nodeSelector | nindent 8 }} + affinity: {{- toYaml .Values.operator.affinity | nindent 8 }} + imagePullSecrets: {{ .Values.imagePullSecrets | default list | toJson }} + securityContext: + runAsNonRoot: true + serviceAccountName: {{ include "maintenance-operator.fullname" . }}-controller-manager + terminationGracePeriodSeconds: 10 + containers: + - name: manager + command: + - /manager + args: + - --leader-elect + env: + - name: OPERATOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: ENABLE_WEBHOOKS + value: {{ quote .Values.operator.admissionController.enable }} + image: {{ .Values.operator.image.repository }}:{{ .Values.operator.image.tag | default .Chart.AppVersion }} + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + {{- if .Values.operator.admissionController.enable }} + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + {{- end }} + resources: {{- toYaml .Values.operator.resources | nindent 10 }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + volumeMounts: + {{- if .Values.operator.admissionController.enable }} + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + {{- end }} + volumes: + {{- if .Values.operator.admissionController.enable }} + - name: cert + secret: + defaultMode: 420 + secretName: {{ .Values.operator.admissionController.certificates.secretNames.operator }} + {{- end }} diff --git a/deployment/maintenance-operator-chart/templates/metrics-service.yaml b/deployment/maintenance-operator-chart/templates/metrics-service.yaml new file mode 100644 index 0000000..780fa80 --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/metrics-service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "maintenance-operator.name" . }}-metrics-service + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: kube-rbac-proxy + app.kubernetes.io/created-by: maintenance-operator + app.kubernetes.io/part-of: maintenance-operator + control-plane: {{ .Release.Name }}-controller-manager + {{- include "maintenance-operator.labels" . | nindent 4 }} +spec: + type: {{ .Values.metricsService.type }} + selector: + control-plane: {{ .Release.Name }}-controller-manager + {{- include "maintenance-operator.selectorLabels" . | nindent 4 }} + ports: + {{- .Values.metricsService.ports | toYaml | nindent 2 }} \ No newline at end of file diff --git a/deployment/maintenance-operator-chart/templates/operatorconfig.yaml b/deployment/maintenance-operator-chart/templates/operatorconfig.yaml new file mode 100644 index 0000000..60f4b49 --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/operatorconfig.yaml @@ -0,0 +1,21 @@ +apiVersion: maintenance.nvidia.com/v1alpha1 +kind: MaintenanceOperatorConfig +metadata: + name: default + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: config + app.kubernetes.io/created-by: maintenance-operator + app.kubernetes.io/part-of: maintenance-operator + {{- include "maintenance-operator.labels" . | nindent 4 }} +spec: + logLevel: {{ .Values.operatorConfig.logLevel }} +{{- if .Values.operatorConfig.maxParallelOperations }} + maxParallelOperations: {{ .Values.operatorConfig.maxParallelOperations }} +{{- end }} +{{- if .Values.operatorConfig.maxUnavailable }} + maxUnavailable: {{ .Values.operatorConfig.maxUnavailable }} +{{- end }} +{{- if .Values.operatorConfig.maxNodeMaintenanceTimeSeconds }} + maxNodeMaintenanceTimeSeconds: {{ .Values.operatorConfig.maxNodeMaintenanceTimeSeconds }} +{{- end }} diff --git a/deployment/maintenance-operator-chart/templates/role.yaml b/deployment/maintenance-operator-chart/templates/role.yaml new file mode 100644 index 0000000..905eb96 --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/role.yaml @@ -0,0 +1,158 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "maintenance-operator.fullname" . }}-manager-role + labels: + {{- include "maintenance-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- nonResourceURLs: + - /metrics + verbs: + - get +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - update +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods + verbs: + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods/eviction + verbs: + - create + - delete + - get + - list + - patch + - update +- apiGroups: + - maintenance.nvidia.com + resources: + - maintenanceoperatorconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - maintenance.nvidia.com + resources: + - maintenanceoperatorconfigs/finalizers + verbs: + - update +- apiGroups: + - maintenance.nvidia.com + resources: + - maintenanceoperatorconfigs/status + verbs: + - get + - patch + - update +- apiGroups: + - maintenance.nvidia.com + resources: + - nodemaintenances + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - maintenance.nvidia.com + resources: + - nodemaintenances/finalizers + verbs: + - update +- apiGroups: + - maintenance.nvidia.com + resources: + - nodemaintenances/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "maintenance-operator.fullname" . }}-manager-role + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: maintenance-operator + app.kubernetes.io/part-of: maintenance-operator + {{- include "maintenance-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/deployment/maintenance-operator-chart/templates/role_binding.yaml b/deployment/maintenance-operator-chart/templates/role_binding.yaml new file mode 100644 index 0000000..6f3491d --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/role_binding.yaml @@ -0,0 +1,36 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "maintenance-operator.fullname" . }}-manager-rolebinding + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: maintenance-operator + app.kubernetes.io/part-of: maintenance-operator + {{- include "maintenance-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: '{{ include "maintenance-operator.fullname" . }}-manager-role' +subjects: +- kind: ServiceAccount + name: '{{ include "maintenance-operator.fullname" . }}-controller-manager' + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "maintenance-operator.fullname" . }}-manager-rolebinding + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: maintenance-operator + app.kubernetes.io/part-of: maintenance-operator + {{- include "maintenance-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: '{{ include "maintenance-operator.fullname" . }}-manager-role' +subjects: +- kind: ServiceAccount + name: '{{ include "maintenance-operator.fullname" . }}-controller-manager' + namespace: {{ .Release.Namespace }} diff --git a/deployment/maintenance-operator-chart/templates/serviceaccount.yaml b/deployment/maintenance-operator-chart/templates/serviceaccount.yaml new file mode 100644 index 0000000..c948f0c --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "maintenance-operator.fullname" . }}-controller-manager + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: maintenance-operator + app.kubernetes.io/part-of: maintenance-operator + {{- include "maintenance-operator.labels" . | nindent 4 }} + annotations: + {{- toYaml .Values.operator.serviceAccount.annotations | nindent 4 }} diff --git a/deployment/maintenance-operator-chart/templates/webhook.yaml b/deployment/maintenance-operator-chart/templates/webhook.yaml new file mode 100644 index 0000000..5e904c6 --- /dev/null +++ b/deployment/maintenance-operator-chart/templates/webhook.yaml @@ -0,0 +1,48 @@ +{{- if .Values.operator.admissionController.enable }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "maintenance-operator.fullname" . }}-webhook-service + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: maintenance-operator + app.kubernetes.io/part-of: maintenance-operator + {{- include "maintenance-operator.labels" . | nindent 4 }} +spec: + type: {{ .Values.webhookService.type }} + selector: + control-plane: {{ .Release.Name }}-controller-manager + {{- include "maintenance-operator.selectorLabels" . | nindent 4 }} + ports: + {{- .Values.webhookService.ports | toYaml | nindent 2 }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: {{ include "maintenance-operator.fullname" . }}-validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "maintenance-operator.fullname" . }}-serving-cert + labels: + {{- include "maintenance-operator.labels" . | nindent 4 }} +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: '{{ include "maintenance-operator.fullname" . }}-webhook-service' + namespace: {{ .Release.Namespace }} + path: /validate-maintenance-nvidia-com-v1alpha1-nodemaintenance + failurePolicy: Fail + name: vnodemaintenance.kb.io + rules: + - apiGroups: + - maintenance.nvidia.com + apiVersions: + - v1alpha1 + operations: + - CREATE + resources: + - nodemaintenances + sideEffects: None +{{- end }} diff --git a/deployment/maintenance-operator-chart/values.yaml b/deployment/maintenance-operator-chart/values.yaml new file mode 100644 index 0000000..1177072 --- /dev/null +++ b/deployment/maintenance-operator-chart/values.yaml @@ -0,0 +1,82 @@ +operator: + image: + repository: ghcr.io/mellanox/maintenance-operator + #tag: latest + tolerations: + - key: "node-role.kubernetes.io/master" + operator: "Exists" + effect: "NoSchedule" + - key: "node-role.kubernetes.io/control-plane" + operator: "Exists" + effect: "NoSchedule" + nodeSelector: {} + affinity: + nodeAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 1 + preference: + matchExpressions: + - key: "node-role.kubernetes.io/master" + operator: Exists + - weight: 1 + preference: + matchExpressions: + - key: "node-role.kubernetes.io/control-plane" + operator: Exists + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + replicas: 1 + serviceAccount: + annotations: {} + admissionController: + enable: true + certificates: + secretNames: + operator: "operator-webhook-cert" + certManager: + enable: true + generateSelfSigned: true + custom: + enable: false + # operator: + # caCrt: | + # -----BEGIN CERTIFICATE----- + # MIIMIICLDCCAdKgAwIBAgIBADAKBggqhkjOPQQDAjB9MQswCQYDVQQGEwJCRTEPMA0G + # ... + # -----END CERTIFICATE----- + # tlsCrt: | + # -----BEGIN CERTIFICATE----- + # MIIMIICLDCCAdKgAwIBAgIBADAKBggqhkjOPQQDAjB9MQswCQYDVQQGEwJCRTEPMA0G + # ... + # -----END CERTIFICATE----- + # tlsKey: | + # -----BEGIN EC PRIVATE KEY----- + # MHcl4wOuDwKQa+upc8GftXE2C//4mKANBC6It01gUaTIpo= + # ... + # -----END EC PRIVATE KEY----- + +operatorConfig: + logLevel: info +# maxParallelOperations: nil +# maxUnavailable: nil +# maxNodeMaintenanceTimeSeconds: 1600 + +imagePullSecrets: [] +metricsService: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + type: ClusterIP +webhookService: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + type: ClusterIP From 68e169aa0866097e6bcdb6e1a9057e5d6aec34ae Mon Sep 17 00:00:00 2001 From: adrianc Date: Sun, 1 Sep 2024 16:19:33 +0300 Subject: [PATCH 2/2] Helm chart release automation push helm chart as oci image to ghcr on release (tag) Signed-off-by: adrianc --- .github/workflows/chart-push-release.yaml | 28 +++ Makefile | 261 ++++++++++++---------- hack/release/chart-push.sh | 34 +++ hack/release/chart-update.sh | 36 +++ 4 files changed, 236 insertions(+), 123 deletions(-) create mode 100644 .github/workflows/chart-push-release.yaml create mode 100755 hack/release/chart-push.sh create mode 100755 hack/release/chart-update.sh diff --git a/.github/workflows/chart-push-release.yaml b/.github/workflows/chart-push-release.yaml new file mode 100644 index 0000000..604109b --- /dev/null +++ b/.github/workflows/chart-push-release.yaml @@ -0,0 +1,28 @@ +name: "Push helm chart on release" + +env: + IMAGE_NAME: ghcr.io/${{ github.repository }} + +on: + push: + tags: + - v* +jobs: + package-and-push-helm-chart: + runs-on: ubuntu-22.04 + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: update chart + env: + GITHUB_TAG: ${{ github.ref_name }} + GITHUB_REPO_OWNER: ${{ github.repository_owner }} + run: make chart-prepare-release + + - name: push chart + env: + GITHUB_TAG: ${{ github.ref_name }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPO_OWNER: ${{ github.repository_owner }} + run: make chart-push-release diff --git a/Makefile b/Makefile index 678d1ed..95b0751 100644 --- a/Makefile +++ b/Makefile @@ -91,8 +91,135 @@ COVER_MODE = atomic COVER_PROFILE = cover.out LCOV_PATH = lcov.info -.PHONY: all -all: build +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Location for build binaries +BUILDDIR ?= $(shell pwd)/build +$(BUILDDIR): + mkdir -p $(BUILDDIR) + +##@ Binary Dependencies download +MOCKERY ?= $(LOCALBIN)/mockery +MOCKERY_VERSION ?= v2.44.2 +.PHONY: mockery +mockery: $(MOCKERY) ## Download mockery locally if necessary. +$(MOCKERY): | $(LOCALBIN) + GOBIN=$(LOCALBIN) go install github.com/vektra/mockery/v2@$(MOCKERY_VERSION) + +.PHONY: kustomize +KUSTOMIZE ?= $(LOCALBIN)/kustomize +KUSTOMIZE_VERSION ?= v5.4.2 +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. +$(KUSTOMIZE): $(LOCALBIN) + @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ + echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ + rm -rf $(LOCALBIN)/kustomize; \ + fi + test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION) + +.PHONY: controller-gen +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +CONTROLLER_TOOLS_VERSION ?= v0.15.0 +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. +$(CONTROLLER_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ + GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + +.PHONY: envtest +ENVTEST ?= $(LOCALBIN)/setup-envtest +ENVTEST_VERSION ?= latest +envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +$(ENVTEST): $(LOCALBIN) + test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@$(ENVTEST_VERSION) + +.PHONY: operator-sdk +OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk +operator-sdk: ## Download operator-sdk locally if necessary. +ifeq (,$(wildcard $(OPERATOR_SDK))) +ifeq (, $(shell which operator-sdk 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPERATOR_SDK)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ + chmod +x $(OPERATOR_SDK) ;\ + } +else +OPERATOR_SDK = $(shell which operator-sdk) +endif +endif + +.PHONY: opm +OPM = $(LOCALBIN)/opm +opm: ## Download opm locally if necessary. +ifeq (,$(wildcard $(OPM))) +ifeq (,$(shell which opm 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPM)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\ + chmod +x $(OPM) ;\ + } +else +OPM = $(shell which opm) +endif +endif + +SKAFFOLD_VER := v2.12.0 +SKAFFOLD := $(abspath $(LOCALBIN)/skaffold-$(SKAFFOLD_VER)) +.PHONY: skaffold +skaffold: $(SKAFFOLD) ## Download skaffold locally if necessary. +$(SKAFFOLD): | $(LOCALBIN) + @{ \ + set -e;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -fsSL https://storage.googleapis.com/skaffold/releases/$(SKAFFOLD_VER)/skaffold-$${OS}-$${ARCH} -o $(SKAFFOLD); \ + chmod +x $(SKAFFOLD);\ + } + +# minikube is used to set-up a local kubernetes cluster for dev work. +MINIKUBE_VER := v1.33.1 +MINIKUBE := $(abspath $(LOCALBIN)/minikube-$(MINIKUBE_VER)) +.PHONY: minikube +minikube: $(MINIKUBE) ## Download minikube locally if necessary. +$(MINIKUBE): | $(LOCALBIN) + @{ \ + set -e;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -fsSL https://storage.googleapis.com/minikube/releases/$(MINIKUBE_VER)/minikube-$${OS}-$${ARCH} -o $(MINIKUBE); \ + chmod +x $(MINIKUBE);\ + } + +HELM := $(abspath $(LOCALBIN)/helm) +.PHONY: helm +helm: $(HELM) ## Download helm (last release) locally if necessary. +$(HELM): | $(LOCALBIN) + @{ \ + curl -fsSL -o $(LOCALBIN)/get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 && \ + chmod 700 $(LOCALBIN)/get_helm.sh && \ + HELM_INSTALL_DIR=$(LOCALBIN) USE_SUDO=false $(LOCALBIN)/get_helm.sh && \ + rm -f $(LOCALBIN)/get_helm.sh; \ + } + +YQ := $(abspath $(LOCALBIN)/yq) +YQ_VERSION=v4.44.1 +.PHONY: yq +yq: $(YQ) ## Download yq locally if necessary. +$(YQ): | $(LOCALBIN) + @curl -fsSL -o $(YQ) https://github.com/mikefarah/yq/releases/download/$(YQ_VERSION)/yq_linux_amd64 && chmod +x $(YQ) + +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint +GOLANGCI_LINT_VERSION ?= v1.59.1 +.PHONY: golangci-lint ## Download golangci-lint locally if necessary. +golangci-lint: + @[ -f $(GOLANGCI_LINT) ] || { \ + set -e ;\ + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell dirname $(GOLANGCI_LINT)) $(GOLANGCI_LINT_VERSION) ;\ + } ##@ General @@ -111,6 +238,9 @@ all: build help: ## Display this help. @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) +.PHONY: all +all: build + .PHONY: clean clean: ## clean files rm -rf $(LOCALBIN) @@ -138,15 +268,6 @@ unit-test: envtest ## Run unit tests. .PHONY: test-e2e # Run the e2e tests against a Kind k8s instance that is spun up. test-e2e: go test ./test/e2e/ -v -ginkgo.v - -GOLANGCI_LINT = $(LOCALBIN)/golangci-lint -GOLANGCI_LINT_VERSION ?= v1.59.1 -golangci-lint: - @[ -f $(GOLANGCI_LINT) ] || { \ - set -e ;\ - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell dirname $(GOLANGCI_LINT)) $(GOLANGCI_LINT_VERSION) ;\ - } - .PHONY: lint lint: golangci-lint ## Run golangci-lint linter & yamllint @@ -225,16 +346,6 @@ undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/confi ##@ Build Dependencies -## Location to install dependencies to -LOCALBIN ?= $(shell pwd)/bin -$(LOCALBIN): - mkdir -p $(LOCALBIN) - -## Location for build binaries -BUILDDIR ?= $(shell pwd)/build -$(BUILDDIR): - mkdir -p $(BUILDDIR) - .PHONY: bundle bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. $(OPERATOR_SDK) generate kustomize manifests -q @@ -274,110 +385,14 @@ catalog-build: opm ## Build a catalog image. catalog-push: ## Push a catalog image. $(MAKE) docker-push IMG=$(CATALOG_IMG) -##@ Binary Dependencies download - -MOCKERY ?= $(LOCALBIN)/mockery -MOCKERY_VERSION ?= v2.44.2 -.PHONY: mockery -mockery: $(MOCKERY) ## Download mockery locally if necessary. -$(MOCKERY): | $(LOCALBIN) - GOBIN=$(LOCALBIN) go install github.com/vektra/mockery/v2@$(MOCKERY_VERSION) - -.PHONY: kustomize -KUSTOMIZE ?= $(LOCALBIN)/kustomize -KUSTOMIZE_VERSION ?= v5.4.2 -kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. -$(KUSTOMIZE): $(LOCALBIN) - @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ - echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ - rm -rf $(LOCALBIN)/kustomize; \ - fi - test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION) - -.PHONY: controller-gen -CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen -CONTROLLER_TOOLS_VERSION ?= v0.15.0 -controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. -$(CONTROLLER_GEN): $(LOCALBIN) - test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ - GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) +.PHONY: chart-prepare-release +chart-prepare-release: | $(YQ) ## prepare helm chart for release + @GITHUB_TAG=$(GITHUB_TAG) GITHUB_REPO_OWNER=$(GITHUB_REPO_OWNER) hack/release/chart-update.sh -.PHONY: envtest -ENVTEST ?= $(LOCALBIN)/setup-envtest -ENVTEST_VERSION ?= latest -envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. -$(ENVTEST): $(LOCALBIN) - test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@$(ENVTEST_VERSION) -.PHONY: operator-sdk -OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk -operator-sdk: ## Download operator-sdk locally if necessary. -ifeq (,$(wildcard $(OPERATOR_SDK))) -ifeq (, $(shell which operator-sdk 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(OPERATOR_SDK)) ;\ - OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$${OS}_$${ARCH} ;\ - chmod +x $(OPERATOR_SDK) ;\ - } -else -OPERATOR_SDK = $(shell which operator-sdk) -endif -endif - -.PHONY: opm -OPM = $(LOCALBIN)/opm -opm: ## Download opm locally if necessary. -ifeq (,$(wildcard $(OPM))) -ifeq (,$(shell which opm 2>/dev/null)) - @{ \ - set -e ;\ - mkdir -p $(dir $(OPM)) ;\ - OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\ - chmod +x $(OPM) ;\ - } -else -OPM = $(shell which opm) -endif -endif - -SKAFFOLD_VER := v2.12.0 -SKAFFOLD := $(abspath $(LOCALBIN)/skaffold-$(SKAFFOLD_VER)) -.PHONY: skaffold -skaffold: $(SKAFFOLD) ## Download skaffold locally if necessary. -$(SKAFFOLD): | $(LOCALBIN) - @{ \ - set -e;\ - OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -fsSL https://storage.googleapis.com/skaffold/releases/$(SKAFFOLD_VER)/skaffold-$${OS}-$${ARCH} -o $(SKAFFOLD); \ - chmod +x $(SKAFFOLD);\ - } - -# minikube is used to set-up a local kubernetes cluster for dev work. -MINIKUBE_VER := v1.33.1 -MINIKUBE := $(abspath $(LOCALBIN)/minikube-$(MINIKUBE_VER)) -.PHONY: minikube -minikube: $(MINIKUBE) ## Download minikube locally if necessary. -$(MINIKUBE): | $(LOCALBIN) - @{ \ - set -e;\ - OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -fsSL https://storage.googleapis.com/minikube/releases/$(MINIKUBE_VER)/minikube-$${OS}-$${ARCH} -o $(MINIKUBE); \ - chmod +x $(MINIKUBE);\ - } - -HELM := $(abspath $(LOCALBIN)/helm) -.PHONY: helm -helm: $(HELM) ## Download helm (last release) locally if necessary. -$(HELM): | $(LOCALBIN) - @{ \ - curl -fsSL -o $(LOCALBIN)/get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 && \ - chmod 700 $(LOCALBIN)/get_helm.sh && \ - HELM_INSTALL_DIR=$(LOCALBIN) USE_SUDO=false $(LOCALBIN)/get_helm.sh && \ - rm -f $(LOCALBIN)/get_helm.sh; \ - } +.PHONY: chart-push-release +chart-push-release: | $(HELM) ## push release helm chart + @GITHUB_TAG=$(GITHUB_TAG) GITHUB_TOKEN=$(GITHUB_TOKEN) GITHUB_REPO_OWNER=$(GITHUB_REPO_OWNER) hack/release/chart-push.sh ##@ Dev diff --git a/hack/release/chart-push.sh b/hack/release/chart-push.sh new file mode 100755 index 0000000..9eb3105 --- /dev/null +++ b/hack/release/chart-push.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -ex + +# github repo owner: e.g mellanox +GITHUB_REPO_OWNER=${GITHUB_REPO_OWNER:-} +# github api token with package:write permissions +GITHUB_TOKEN=${GITHUB_TOKEN:-} +# github tag e.g v1.2.3 +GITHUB_TAG=${GITHUB_TAG:-} + +BASE=${PWD} +HELM_CMD="${BASE}/bin/helm" +HELM_CHART=${BASE}/deployment/maintenance-operator-chart +HELM_CHART_VERSION=${GITHUB_TAG#"v"} +HELM_CHART_TARBALL="maintenance-operator-chart-${HELM_CHART_VERSION}.tgz" + +if [ -z "$GITHUB_REPO_OWNER" ]; then + echo "ERROR: GITHUB_REPO_OWNER must be provided as env var" + exit 1 +fi + +if [ -z "$GITHUB_TOKEN" ]; then + echo "ERROR: GITHUB_TOKEN must be provided as env var" + exit 1 +fi + +if [ -z "$GITHUB_TAG" ]; then + echo "ERROR: GITHUB_TAG must be provided as env var" + exit 1 +fi + +$HELM_CMD package ${HELM_CHART} +$HELM_CMD registry login ghcr.io -u ${GITHUB_REPO_OWNER} -p ${GITHUB_TOKEN} +$HELM_CMD push ${HELM_CHART_TARBALL} oci://ghcr.io/${GITHUB_REPO_OWNER} diff --git a/hack/release/chart-update.sh b/hack/release/chart-update.sh new file mode 100755 index 0000000..a1dad8d --- /dev/null +++ b/hack/release/chart-update.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -ex + +# github tag e.g v1.2.3 +GITHUB_TAG=${GITHUB_TAG:-} +# github repo owner e.g mellanox +GITHUB_REPO_OWNER=${GITHUB_REPO_OWNER:-} + +BASE=${PWD} +YQ_CMD="${BASE}/bin/yq" +HELM_VALUES=${BASE}/deployment/maintenance-operator-chart/values.yaml +HELM_CHART=${BASE}/deployment/maintenance-operator-chart/Chart.yaml + + +if [ -z "$GITHUB_TAG" ]; then + echo "ERROR: GITHUB_TAG must be provided as env var" + exit 1 +fi + +if [ -z "$GITHUB_REPO_OWNER" ]; then + echo "ERROR: GITHUB_REPO_OWNER must be provided as env var" + exit 1 +fi + +# tag provided via env var +OPERATOR_TAG=${GITHUB_TAG} + +# patch values.yaml in-place + +# maintenance-operator image: +OPERATOR_REPO=${GITHUB_REPO_OWNER} # this is used to allow to release maintenance-operator from forks +$YQ_CMD -i ".operator.image.repository = \"ghcr.io/${OPERATOR_REPO}/maintenance-operator\"" ${HELM_VALUES} + +# patch Chart.yaml in-place +$YQ_CMD -i ".version = \"${OPERATOR_TAG#"v"}\"" ${HELM_CHART} +$YQ_CMD -i ".appVersion = \"${OPERATOR_TAG}\"" ${HELM_CHART}