diff --git a/charts/ocean-right-sizing-controller/.helmignore b/charts/ocean-right-sizing-controller/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/ocean-right-sizing-controller/.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/charts/ocean-right-sizing-controller/Chart.lock b/charts/ocean-right-sizing-controller/Chart.lock new file mode 100644 index 0000000..85cfb59 --- /dev/null +++ b/charts/ocean-right-sizing-controller/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: metrics-server + repository: https://kubernetes-sigs.github.io/metrics-server + version: 3.12.2 +- name: ocean-vpa + repository: https://charts.spot.io + version: 1.0.4 +digest: sha256:bc126e327103cb3506c42e0d91a4771cb1824160669c3579604e06f5c0760f6a +generated: "2025-06-18T11:39:19.608047+03:00" diff --git a/charts/ocean-right-sizing-controller/Chart.yaml b/charts/ocean-right-sizing-controller/Chart.yaml new file mode 100644 index 0000000..bb1ccf7 --- /dev/null +++ b/charts/ocean-right-sizing-controller/Chart.yaml @@ -0,0 +1,25 @@ +apiVersion: v2 +name: ocean-right-sizing-controller +description: A Helm chart for Ocean Right-Sizing Controller +type: application +version: 0.1.62 +appVersion: 2.0.70 +kubeVersion: ">=1.20.0-0" +maintainers: + - name: spotinst + email: ng-spot-info@netapp.com +icon: https://docs.spot.io/_media/images/spot_mark.png +keywords: + - spot + - ocean + - right-sizing + - controller +dependencies: + - name: metrics-server + version: 3.12.2 + repository: https://kubernetes-sigs.github.io/metrics-server + condition: metrics-server.deployChart + - name: ocean-vpa + version: 1.0.4 + repository: https://charts.spot.io + condition: ocean-vpa.deployChart diff --git a/charts/ocean-right-sizing-controller/README.md b/charts/ocean-right-sizing-controller/README.md new file mode 100644 index 0000000..f3eef27 --- /dev/null +++ b/charts/ocean-right-sizing-controller/README.md @@ -0,0 +1,161 @@ +# ocean-right-sizing-controller + +![Version: 0.1.62](https://img.shields.io/badge/Version-0.1.62-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2.0.70](https://img.shields.io/badge/AppVersion-2.0.70-informational?style=flat-square) + +A Helm chart for Ocean Right-Sizing Controller. + +## Installation + +1. Add the Spot Helm chart repository: + +```sh +helm repo add spot https://charts.spot.io +``` + +2. Update your local Helm chart repository cache: + +```sh +helm repo update +``` + +3. Install `ocean-right-sizing-controller`: + +```sh +helm install spot spot/ocean-right-sizing-controller \ + --set spotinst.account=$SPOTINST_ACCOUNT \ + --set spotinst.clusterIdentifier=$SPOTINST_CLUSTER_IDENTIFIER \ + --set spotinst.token=$SPOTINST_TOKEN +``` + +> NOTE: Please configure all required chart values using the `set` command line argument or a `values.yaml` file. + +## Installation With HTTPS Proxy + +In case you need to configure a proxy with a custom CA bundle you should use the following: + +```sh +helm install spot spot/ocean-right-sizing-controller \ + --set spotinst.account=$SPOTINST_ACCOUNT \ + --set spotinst.clusterIdentifier=$SPOTINST_CLUSTER_IDENTIFIER \ + --set spotinst.token=$SPOTINST_TOKEN \ + --set spotinst.proxyUrl=$SPOTINST_PROXY_URL \ + --set caBundleSecret.create=true \ + --set caBundleSecret.data="$(cat ./path/to/ca.pem)" +``` + +If you already have a CA bundle secret you can instead use: + +```sh +helm install spot spot/ocean-right-sizing-controller \ + --set spotinst.account=$SPOTINST_ACCOUNT \ + --set spotinst.clusterIdentifier=$SPOTINST_CLUSTER_IDENTIFIER \ + --set spotinst.token=$SPOTINST_TOKEN \ + --set spotinst.proxyUrl=$SPOTINST_PROXY_URL \ + --set caBundleSecret.name=my-ca-bundle-secret \ + --set caBundleSecret.key=bundle.pem +``` + +## Requirements + +Kubernetes: `>=1.20.0-0` + +| Repository | Name | Version | +|------------|------|---------| +| https://charts.spot.io | ocean-vpa | 1.0.4 | +| https://kubernetes-sigs.github.io/metrics-server | metrics-server | 3.12.2 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | string | `nil` | | +| args | list | `[]` | | +| autoUpdate.image | object | `{"pullPolicy":"Always","repository":"us-docker.pkg.dev/spotit-today/container-labs/auto-updater","tag":"latest"}` | Configures the image for the auto-updater job. (Optional) | +| autoUpdate.image.pullPolicy | string | `"Always"` | Image pull policy. (Optional) | +| autoUpdate.image.repository | string | `"us-docker.pkg.dev/spotit-today/container-labs/auto-updater"` | Image repository. (Optional) | +| autoUpdate.image.tag | string | `"latest"` | Overrides the image tag. (Optional) | +| autoUpdate.imagePullSecrets | list | `[]` | Image pull secrets. (Optional) | +| autoUpdate.podSecurityContext | object | `{"fsGroup":1000690000,"runAsGroup":1000690000,"runAsNonRoot":true,"runAsUser":1000690000}` | Pod Security Context for the auto-updater job. (Optional) Ref: https://kubernetes.io/docs/concepts/security/pod-security-standards/ | +| autoUpdate.priorityClassName | string | `"system-cluster-critical"` | Priority class name for the auto-updater job. Defaults to the same priority class as the controller to prevent eviction. (Optional) | +| autoUpdate.resources | object | `{"limits":{"cpu":"100m","memory":"256Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | Resource requests and limits for the auto-updater job. Defaults to 100m CPU and 256Mi memory to make the job run with 'Guranteed' QoS. (Optional) | +| autoUpdate.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true}` | Security Context for the auto-updater container. (Optional) | +| autoUpdate.serviceAccount.annotations | object | `{}` | | +| autoUpdate.serviceAccount.create | bool | `true` | | +| autoUpdate.serviceAccount.name | string | `""` | | +| caBundleSecret.create | bool | `false` | Controls whether a CA bundle secret should be created. | +| caBundleSecret.data | string | `""` | Must contain the CA bundle data in case `caBundleSecret.create` is true. For example by using `--set caBundleSecret.data="$(cat ./ca.pem)"` | +| caBundleSecret.key | string | `"userEnvCertificates.pem"` | Key inside the secret to inject the CA bundle from | +| caBundleSecret.name | string | `""` | CA bundle Secret name. (Optional) | +| command | list | `[]` | | +| commonLabels | object | `{}` | | +| configMap.create | bool | `true` | | +| configMap.name | string | `""` | ConfigMap name. (Optional) | +| deploymentAnnotations | object | `{}` | | +| extraEnv | list | `[]` | | +| extraVolumeMounts | list | `[]` | | +| extraVolumes | list | `[]` | | +| fullnameOverride | string | `""` | | +| image.fips | bool | `false` | Set to `true` to use an FIPS-140 compliant image. This flag adds `-fips` suffix to the image tag, therefore it should not be used together with the `--image.tag` flag. Ref: https://go.dev/doc/security/fips140 | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"us-docker.pkg.dev/spotit-today/container-labs/spotinst-kubernetes-controller"` | | +| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | +| imagePullSecrets | list | `[]` | | +| initContainers | list | `[]` | | +| livenessProbe.httpGet.path | string | `"/healthz"` | | +| livenessProbe.httpGet.port | string | `"readiness"` | | +| livenessProbe.initialDelaySeconds | int | `15` | | +| livenessProbe.periodSeconds | int | `20` | | +| logShipping | object | `{"command":["/fluent-bit/bin/fluent-bit","-c","/tmp/fluent-bit.conf","-q"],"destination":{"host":"api.spotinst.io","port":443,"tls":true},"enabled":true,"extraEnv":[],"extraVolumeMounts":[],"image":{"pullPolicy":"IfNotPresent","repository":"ghcr.io/fluent/fluent-bit","tag":"3.1.9"},"securityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true}}` | Log Shipping configuration. | +| logShipping.command | list | `["/fluent-bit/bin/fluent-bit","-c","/tmp/fluent-bit.conf","-q"]` | Log shipping container command. (Optional) | +| logShipping.destination | object | `{"host":"api.spotinst.io","port":443,"tls":true}` | Log shipping destination configuration. | +| logShipping.enabled | bool | `true` | Specifies whether to send the controller logs to Spot for analysis. (Optional) | +| logShipping.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. (Optional) | +| logShipping.image.repository | string | `"ghcr.io/fluent/fluent-bit"` | Image repository. (Optional) | +| logShipping.image.tag | string | `"3.1.9"` | Overrides the image tag. (Optional) | +| logShipping.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsNonRoot":true}` | Log Shipping container security context | +| metrics-server.deployChart | bool | `true` | Specifies whether the metrics-server chart should be deployed. | +| metrics-server.image.pullPolicy | string | `"IfNotPresent"` | | +| metrics-server.image.repository | string | `"registry.k8s.io/metrics-server/metrics-server"` | | +| metrics-server.image.tag | string | `""` | | +| nameOverride | string | `""` | | +| nodeSelector | object | `{}` | | +| ocean-vpa.deployChart | bool | `true` | Specifies whether the ocean-vpa chart should be deployed. | +| podAnnotations | object | `{}` | | +| podLabels | object | `{}` | | +| podSecurityContext.fsGroup | int | `1000690000` | | +| podSecurityContext.runAsGroup | int | `1000690000` | | +| podSecurityContext.runAsNonRoot | bool | `true` | | +| podSecurityContext.runAsUser | int | `1000690000` | | +| priorityClassName | string | `"system-node-critical"` | Priority class name for the controller pod. | +| readinessProbe.httpGet.path | string | `"/readyz"` | | +| readinessProbe.httpGet.port | string | `"readiness"` | | +| readinessProbe.initialDelaySeconds | int | `5` | | +| readinessProbe.periodSeconds | int | `10` | | +| replicas | int | `2` | Configure the amount of replicas for the controller (Optional) | +| resourceQuota | object | `{"enabled":true}` | Resource Quota configuration. Required when running in a namespace other than kube-system in GKE. Ref: https://kubernetes.io/docs/concepts/policy/resource-quotas/ | +| resources | object | `{}` | | +| schedulerName | string | `""` | | +| secret.create | bool | `true` | Controls whether a Secret should be created. (Optional) | +| secret.name | string | `""` | Secret name. (Optional) | +| securityContext.allowPrivilegeEscalation | bool | `false` | | +| securityContext.capabilities.drop[0] | string | `"ALL"` | | +| securityContext.readOnlyRootFilesystem | bool | `true` | | +| securityContext.runAsNonRoot | bool | `true` | | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.name | string | `""` | | +| spotinst.account | string | `""` | Spot Account ID. (Required) Example: `act-123abcd` | +| spotinst.baseUrl | string | `""` | Base URL. (Optional) | +| spotinst.clusterIdentifier | string | `""` | Unique identifier used by the Ocean Controller to connect (Required) between the Ocean backend and the Kubernetes cluster. Ref: https://docs.spot.io/ocean/tutorials/spot-kubernetes-controller/ | +| spotinst.disableAutoUpdate | bool | `false` | Disable auto update. (Optional) | +| spotinst.enableCsrApproval | bool | `true` | Enable CSR approval. (Optional) | +| spotinst.insecureSkipTLSVerify | bool | `false` | Disable TLS certificate validation. (Optional) | +| spotinst.proxyUrl | string | `""` | Proxy URL. (Optional) | +| spotinst.readonly | bool | `true` | Whether this controller needs to be readonly - overrides other permissions. (Optional) | +| spotinst.token | string | `""` | Spot Token. (Required) Ref: https://docs.spot.io/administration/api/create-api-token | +| tolerations | string | `nil` | Tolerations for nodes that have taints on them. (Optional) Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ | +| topologySpreadConstraints | string | `nil` | | +| updateStrategy | object | `{}` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/ocean-right-sizing-controller/README.md.gotmpl b/charts/ocean-right-sizing-controller/README.md.gotmpl new file mode 100644 index 0000000..1ddff9c --- /dev/null +++ b/charts/ocean-right-sizing-controller/README.md.gotmpl @@ -0,0 +1,64 @@ +{{ template "chart.header" . }} + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} + +{{ template "chart.description" . }}. + +## Installation + +1. Add the Spot Helm chart repository: + +```sh +helm repo add spot https://charts.spot.io +``` + +2. Update your local Helm chart repository cache: + +```sh +helm repo update +``` + +3. Install `{{ template "chart.name" . }}`: + +```sh +helm install spot spot/{{ template "chart.name" . }} \ + --set spotinst.account=$SPOTINST_ACCOUNT \ + --set spotinst.clusterIdentifier=$SPOTINST_CLUSTER_IDENTIFIER \ + --set spotinst.token=$SPOTINST_TOKEN +``` + +> NOTE: Please configure all required chart values using the `set` command line argument or a `values.yaml` file. + +## Installation With HTTPS Proxy + +In case you need to configure a proxy with a custom CA bundle you should use the following: + +```sh +helm install spot spot/{{ template "chart.name" . }} \ + --set spotinst.account=$SPOTINST_ACCOUNT \ + --set spotinst.clusterIdentifier=$SPOTINST_CLUSTER_IDENTIFIER \ + --set spotinst.token=$SPOTINST_TOKEN \ + --set spotinst.proxyUrl=$SPOTINST_PROXY_URL \ + --set caBundleSecret.create=true \ + --set caBundleSecret.data="$(cat ./path/to/ca.pem)" +``` + +If you already have a CA bundle secret you can instead use: + +```sh +helm install spot spot/{{ template "chart.name" . }} \ + --set spotinst.account=$SPOTINST_ACCOUNT \ + --set spotinst.clusterIdentifier=$SPOTINST_CLUSTER_IDENTIFIER \ + --set spotinst.token=$SPOTINST_TOKEN \ + --set spotinst.proxyUrl=$SPOTINST_PROXY_URL \ + --set caBundleSecret.name=my-ca-bundle-secret \ + --set caBundleSecret.key=bundle.pem +``` + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/ocean-right-sizing-controller/charts/metrics-server-3.12.2.tgz b/charts/ocean-right-sizing-controller/charts/metrics-server-3.12.2.tgz new file mode 100644 index 0000000..4538e8a Binary files /dev/null and b/charts/ocean-right-sizing-controller/charts/metrics-server-3.12.2.tgz differ diff --git a/charts/ocean-right-sizing-controller/charts/ocean-vpa-1.0.4.tgz b/charts/ocean-right-sizing-controller/charts/ocean-vpa-1.0.4.tgz new file mode 100644 index 0000000..1f13903 Binary files /dev/null and b/charts/ocean-right-sizing-controller/charts/ocean-vpa-1.0.4.tgz differ diff --git a/charts/ocean-right-sizing-controller/templates/_helpers.tpl b/charts/ocean-right-sizing-controller/templates/_helpers.tpl new file mode 100644 index 0000000..4b0a864 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/_helpers.tpl @@ -0,0 +1,351 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "ocean-right-sizing-controller.name" -}} +{{- default .Chart.Name .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 "ocean-right-sizing-controller.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .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 a default fully qualified app name for the auto-updater job. +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 "auto-updater.fullname" -}} +{{- if .Values.autoUpdate.fullnameOverride }} +{{- .Values.autoUpdate.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default "auto-updater" .Values.autoUpdate.nameOverride }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "ocean-right-sizing-controller.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +The image to use +*/}} +{{- define "ocean-right-sizing-controller.image" -}} +{{- $imageSuffix := "" }} +{{- if and .Values.image.fips .Values.image.tag }} +{{- fail "`image.fips` should not be used together with `image.tag`. Either set `--image.fips=false` or `--image.tag=\"\"`" }} +{{- end }} +{{- if .Values.image.fips }} +{{- $imageSuffix = "-fips" }} +{{- end }} +{{- printf "%s:%s%s" .Values.image.repository (default (printf "v%s" .Chart.AppVersion) .Values.image.tag) $imageSuffix }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "ocean-right-sizing-controller.labels" -}} +helm.sh/chart: {{ include "ocean-right-sizing-controller.chart" . }} +{{ include "ocean-right-sizing-controller.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "ocean-right-sizing-controller.selectorLabels" -}} +app.kubernetes.io/name: {{ include "ocean-right-sizing-controller.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Common labels Auto Updater +*/}} +{{- define "auto-updater.labels" -}} +helm.sh/chart: {{ include "ocean-right-sizing-controller.chart" . }} +{{ include "auto-updater.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels Auto Updater +*/}} +{{- define "auto-updater.selectorLabels" -}} +app.kubernetes.io/name: auto-updater +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +ConfigMap name. +*/}} +{{- define "ocean-right-sizing-controller.configMapName" -}} +{{ default (include "ocean-right-sizing-controller.fullname" .) .Values.configMap.name }} +{{- end }} + +{{/* +Secret name. +*/}} +{{- define "ocean-right-sizing-controller.secretName" -}} +{{ default (include "ocean-right-sizing-controller.fullname" .) .Values.secret.name }} +{{- end }} + +{{/* +CA bundle secret name. +*/}} +{{- define "ocean-right-sizing-controller.caBundleSecretName" -}} +{{ default (printf "%s-ca-bundle" (include "ocean-right-sizing-controller.fullname" .)) .Values.caBundleSecret.name }} +{{- end }} + +{{/* +ClusterRole name. +*/}} +{{- define "ocean-right-sizing-controller.clusterRoleName" -}} +{{ include "ocean-right-sizing-controller.fullname" . }} +{{- end }} + +{{/* +ClusterRoleBinding name. +*/}} +{{- define "ocean-right-sizing-controller.clusterRoleBindingName" -}} +{{ include "ocean-right-sizing-controller.fullname" . }} +{{- end }} + +{{/* +Create the name of the service-account to use +*/}} +{{- define "ocean-right-sizing-controller.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "ocean-right-sizing-controller.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Create the name of the service-account to use for the auto-updater +*/}} +{{- define "auto-updater.serviceAccountName" -}} +{{- if .Values.autoUpdate.serviceAccount.create }} +{{- default (include "auto-updater.fullname" .) .Values.autoUpdate.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.autoUpdate.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Controller default affinity +*/}} +{{- define "ocean-right-sizing-controller.defaultAffinity" -}} +nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: NotIn + values: + - windows + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + preference: + matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists +{{- end }} + +{{/* +Controller default tolerations +*/}} +{{- define "ocean-right-sizing-controller.defaultTolerations" -}} +- key: node.kubernetes.io/not-ready + effect: NoExecute + operator: Exists + tolerationSeconds: 150 +- key: node.kubernetes.io/unreachable + effect: NoExecute + operator: Exists + tolerationSeconds: 150 +- key: node-role.kubernetes.io/master + operator: Exists +- key: node-role.kubernetes.io/control-plane + operator: Exists +- key: CriticalAddonsOnly + operator: Exists +{{- end }} + +{{/* +Controller affinity +*/}} +{{- define "ocean-right-sizing-controller.affinity" -}} +{{- if kindIs "invalid" .Values.affinity -}} +{{- include "ocean-right-sizing-controller.defaultAffinity" . -}} +{{- else }} +{{- .Values.affinity | toYaml -}} +{{- end }} +{{- end }} + +{{/* +Controller topology spread constraints +*/}} +{{- define "ocean-right-sizing-controller.topologySpreadConstraints" -}} +{{- if kindIs "invalid" .Values.topologySpreadConstraints -}} +- maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + {{- include "ocean-right-sizing-controller.selectorLabels" . | nindent 6 }} +{{- else }} +{{- .Values.topologySpreadConstraints | toYaml -}} +{{- end }} +{{- end }} + +{{/* +Auto-Updater affinity +*/}} +{{- define "auto-updater.affinity" -}} +{{- if kindIs "invalid" .Values.autoUpdate.affinity -}} +{{- include "ocean-right-sizing-controller.defaultAffinity" . -}} +{{- else }} +{{- .Values.autoUpdate.affinity | toYaml -}} +{{- end }} +{{- end }} + +{{/* +Auto-Updater tolerations +*/}} +{{- define "auto-updater.tolerations" -}} +{{- if kindIs "invalid" .Values.autoUpdate.tolerations -}} +- key: node.kubernetes.io/not-ready + effect: NoExecute + operator: Exists + tolerationSeconds: 150 +- key: node.kubernetes.io/unreachable + effect: NoExecute + operator: Exists + tolerationSeconds: 150 +- key: node-role.kubernetes.io/master + operator: Exists +- key: node-role.kubernetes.io/control-plane + operator: Exists +- key: CriticalAddonsOnly + operator: Exists +{{- else }} +{{- .Values.autoUpdate.tolerations | toYaml -}} +{{- end }} +{{- end }} + +{{/* +NO_PROXY environment variable +*/}} +{{- define "ocean-right-sizing-controller.noProxyEnvVar" -}} +{{- $hasNoProxyEnvVar := false -}} +{{- range .Values.extraEnv }} +{{- if eq .name "NO_PROXY" }} +{{- $hasNoProxyEnvVar = true }} +{{- end }} +{{- end }} +{{- if and .Values.spotinst.proxyUrl (not $hasNoProxyEnvVar) -}} +- name: NO_PROXY + value: '$(KUBERNETES_SERVICE_HOST)' # will be replaced to $(KUBERNETES_SERVICE_HOST) in cluster +{{ end -}} +{{- end }} + +{{/* +Metrics-server serviceaccount name +*/}} +{{- define "metrics-server.serviceAccountName" -}} +{{- $name := "metrics-server" }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Metrics-server fullname +*/}} +{{- define "metrics-server.fullname" -}} +{{- $name := "metrics-server" }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} + + +{{/* +Metrics-server name +*/}} +{{- define "metrics-server.name" -}} +metrics-server +{{- end }} + +{{/* +Figure out if we should deploy metrics server. We are checking: +- if 'metrics-server.deployChart' is true: + - try to fetch the 'v1beta1.metrics.k8s.io' APIService + - if it exists: + - check for it's helm annotations to see if it was installed as part of the + same release we are installing now (release name and namespace annotations). + - if it's not the same release -> fail +*/}} +{{- define "ocean-right-sizing-controller.deployMetricsServer" }} +{{- if (index .Values "metrics-server" "deployChart") }} +{{- $apiService := lookup "apiregistration.k8s.io/v1" "APIService" "" "v1beta1.metrics.k8s.io" }} +{{- $releaseName := .Release.Name }} +{{- $releaseNamespace := .Release.Namespace }} +{{- if $apiService -}} +{{- with $apiService }} +{{- if (or + (not .metadata.annotations) + (or + (ne + $releaseName + (index .metadata.annotations "meta.helm.sh/release-name") + ) + (ne + $releaseNamespace + (index .metadata.annotations "meta.helm.sh/release-namespace") + ) + )) +}} +{{- fail "\nThe value: 'metrics-server.deployChart' was set to 'true' but we found another installation of metrics-server in your cluster.\nYou must use:\n --set metrics-server.deployChart=false" }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Log Shipping - Should Enable + +Should only enabled log shipping if controller is installed against prod SaaS environment +or if another log shipping destination host is specified. +*/}} +{{- define "ocean-right-sizing-controller.logShipping.enabled" -}} +{{- if and .Values.logShipping .Values.logShipping.enabled -}} +{{- if (or + (or (eq .Values.spotinst.baseUrl "") (eq .Values.spotinst.baseUrl "api.spotinst.io:443")) + (ne .Values.logShipping.destination.host "api.spotinst.io") +) -}} +true +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/ocean-right-sizing-controller/templates/auto-update/_job.yaml b/charts/ocean-right-sizing-controller/templates/auto-update/_job.yaml new file mode 100644 index 0000000..771f5af --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/auto-update/_job.yaml @@ -0,0 +1,85 @@ +{{- define "auto-updater.job" -}} +{{- if not .Values.spotinst.disableAutoUpdate -}} +apiVersion: batch/v1 +kind: Job +metadata: + generateName: {{ include "auto-updater.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "auto-updater.labels" . | nindent 4 }} +spec: + backoffLimit: 0 + ttlSecondsAfterFinished: 600 + template: + metadata: + labels: + {{- include "auto-updater.selectorLabels" . | nindent 8 }} + spec: + serviceAccountName: {{ include "auto-updater.serviceAccountName" . }} + restartPolicy: Never + {{- with .Values.autoUpdate.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.autoUpdate.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.autoUpdate.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.priorityClassName }} + priorityClassName: {{ . | quote }} + {{- end }} + containers: + - name: auto-updater + image: {{ .Values.autoUpdate.image.repository }}:{{ .Values.autoUpdate.image.tag }} + imagePullPolicy: {{ .Values.autoUpdate.image.pullPolicy }} + command: + - /bin/bash + args: + - -c + - | + set -o pipefail + trap 'echo "SIGTERM caught"' INT TERM + + if [ -z "$CHART_VERSION" ]; then + echo "CHART_VERSION is not set" + exit 1 + fi + if [ -z "$WAIT_TIMEOUT" ]; then + echo "WAIT_TIMEOUT is not set" + exit 1 + fi + + export HELM_CACHE_HOME=/tmp/.helm + export HELM_CONFIG_HOME=/tmp/.helm + export HELM_DATA_HOME=/tmp/.helm + + echo "Upgrading release $(RELEASE_NAME) to version $(CHART_VERSION)" && \ + helm repo add spot https://charts.spot.io && \ + helm get values $(RELEASE_NAME) > /tmp/values.yaml && \ + helm upgrade $(RELEASE_NAME) spot/ocean-right-sizing-controller --version $(CHART_VERSION) \ + --atomic --timeout=$(WAIT_TIMEOUT) --values /tmp/values.yaml + securityContext: + {{- toYaml .Values.autoUpdate.securityContext | nindent 10 }} + env: + - name: RELEASE_NAME + value: {{ .Release.Name }} + resources: + {{- with .Values.autoUpdate.resources }} + {{- toYaml . | nindent 10 }} + {{- end }} + volumeMounts: + - name: helm-data + mountPath: /tmp + affinity: + {{- include "auto-updater.affinity" . | nindent 8 }} + tolerations: + {{- include "auto-updater.tolerations" . | nindent 6 }} + volumes: + - name: helm-data + emptyDir: {} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/auto-update/clusterrole.yaml b/charts/ocean-right-sizing-controller/templates/auto-update/clusterrole.yaml new file mode 100644 index 0000000..1133246 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/auto-update/clusterrole.yaml @@ -0,0 +1,72 @@ +{{- if not .Values.spotinst.disableAutoUpdate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "auto-updater.fullname" . }} + labels: + {{- include "auto-updater.labels" . | nindent 4 }} +rules: +# Auto Updater requires +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "clusterrolebindings" ] + resourceNames: [ {{ include "auto-updater.fullname" . }} ] + verbs: [ "get", "patch" ] +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "clusterroles" ] + resourceNames: [ {{ include "auto-updater.fullname" . }} ] + verbs: [ "get", "patch", "escalate", "bind" ] + +# Controller requires +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "clusterroles" ] + verbs: [ "get", "patch", "escalate", "bind" ] + resourceNames: [ {{ include "ocean-right-sizing-controller.fullname" . }} ] +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "clusterrolebindings" ] + verbs: [ "get", "patch" ] + resourceNames: [ {{ include "ocean-right-sizing-controller.fullname" . }} ] + +# Metrics Server requires +{{- if (index .Values "metrics-server" "deployChart") }} +- apiGroups: [ "apiregistration.k8s.io" ] + resources: [ "apiservices" ] + resourceNames: [ "v1beta1.metrics.k8s.io" ] + verbs: [ "get", "patch" ] +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "clusterroles" ] + verbs: [ "get", "patch", "escalate", "bind" ] + resourceNames: + - {{ printf "system:%s" (include "metrics-server.fullname" .) }} + - {{ printf "system:%s-aggregated-reader" (include "metrics-server.name" .) }} +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "clusterrolebindings" ] + verbs: [ "get", "patch" ] + resourceNames: + - {{ printf "system:%s" (include "metrics-server.fullname" .) }} + - {{ printf "%s:system:auth-delegator" (include "metrics-server.fullname" .) }} +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "rolebindings" ] + verbs: [ "get", "patch" ] + resourceNames: + - {{ printf "%s-auth-reader" (include "metrics-server.fullname" .) }} +{{- end }} + +# Ocean VPA requires +{{- if (index .Values "ocean-vpa" "deployChart") }} +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "clusterrolebindings" ] + verbs: [ "get", "patch" ] + resourceNames: + - system:vpa-updater-in-place-binding + - system:vpa-actor + - system:vpa-target-reader-binding + - system:vpa-evictioner-binding + - system:vpa-admission-controller + - vpa-status-reader-binding +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "rolebindings" ] + verbs: [ "get", "patch" ] + resourceNames: + - system:leader-locking-vpa-updater +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/auto-update/clusterrolebinding.yaml b/charts/ocean-right-sizing-controller/templates/auto-update/clusterrolebinding.yaml new file mode 100644 index 0000000..6811aea --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/auto-update/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if not .Values.spotinst.disableAutoUpdate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "auto-updater.fullname" . }} + labels: + {{- include "auto-updater.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "auto-updater.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "auto-updater.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/auto-update/role.yaml b/charts/ocean-right-sizing-controller/templates/auto-update/role.yaml new file mode 100644 index 0000000..a40222d --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/auto-update/role.yaml @@ -0,0 +1,98 @@ +{{- if not .Values.spotinst.disableAutoUpdate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "auto-updater.fullname" . }} + labels: + {{- include "auto-updater.labels" . | nindent 4 }} +rules: +# Auto Updater requires +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "rolebindings" ] + resourceNames: [ {{ include "auto-updater.fullname" . }} ] + verbs: [ "get", "patch" ] +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "roles" ] + resourceNames: [ {{ include "auto-updater.fullname" . }} ] + verbs: [ "get", "patch", "escalate", "bind" ] +- apiGroups: [ "" ] + resources: [ "serviceaccounts" ] + resourceNames: [ {{ include "auto-updater.fullname" . }} ] + verbs: [ "get", "patch" ] +# Helm Management (resource name not required because the name is dynamic) +- apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: [ "create", "get", "update", "list", "delete" ] +# Required to monitor the deployments rollout after the upgrade +# (resource name not required because the name is dynamic) +- apiGroups: [ "apps" ] + resources: [ "replicasets" ] + verbs: [ "list", "watch" ] + +# Controller requires +- apiGroups: [ "apps" ] + resources: [ "deployments" ] + resourceNames: [ {{ include "ocean-right-sizing-controller.fullname" . }} ] + verbs: [ "get", "patch" ] +- apiGroups: [ "" ] + resources: [ "configmaps" ] + resourceNames: [ {{ include "ocean-right-sizing-controller.configMapName" . }} ] + verbs: [ "get", "patch" ] +- apiGroups: [ "" ] + resources: [ "secrets" ] + resourceNames: [ {{ include "ocean-right-sizing-controller.secretName" . }} ] + verbs: [ "get", "patch" ] +- apiGroups: [ "" ] + resources: [ "serviceaccounts" ] + resourceNames: [ {{ include "ocean-right-sizing-controller.serviceAccountName" . }} ] + verbs: [ "get", "patch" ] +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "roles" ] + resourceNames: [ {{ include "ocean-right-sizing-controller.fullname" . }} ] + verbs: [ "get", "patch", "escalate", "bind" ] +- apiGroups: [ "rbac.authorization.k8s.io" ] + resources: [ "rolebindings" ] + resourceNames: [ {{ include "ocean-right-sizing-controller.fullname" . }} ] + verbs: [ "get", "patch" ] +{{- if and (ne .Release.Namespace "kube-system") .Values.resourceQuota.enabled }} +- apiGroups: [ "" ] + # Cannot limit to specific resource name due to limitation with K8s RBAC. + # We need to create this resource with the auto-updater and it will not allow + # 'create' verb if we limit to specific resource name. + # resourceNames: [ {{ include "ocean-right-sizing-controller.fullname" . }} ] + resources: [ "resourcequotas" ] + verbs: [ "get", "patch", "create" ] +{{- end }} + +# Metrics Server requires +{{- if (index .Values "metrics-server" "deployChart") }} +- apiGroups: [ "" ] + resources: [ "serviceaccounts" ] + resourceNames: [ {{ include "metrics-server.serviceAccountName" . }} ] + verbs: [ "get", "patch" ] +- apiGroups: [ "" ] + resources: [ "services" ] + resourceNames: [ {{ include "metrics-server.fullname" . }} ] + verbs: [ "get", "patch" ] +- apiGroups: [ "apps" ] + resources: [ "deployments" ] + resourceNames: [ {{ include "metrics-server.fullname" . }} ] + verbs: [ "get", "patch" ] +{{- end }} + +# Ocean VPA requires +{{- if (index .Values "ocean-vpa" "deployChart") }} +- apiGroups: [ "" ] + resources: [ "serviceaccounts" ] + resourceNames: [ {{ include "ocean-vpa.serviceAccountName" . }}-admission-controller, {{ include "ocean-vpa.serviceAccountName" . }}-updater ] + verbs: [ "get", "patch" ] +- apiGroups: [ "" ] + resources: [ "services" ] + resourceNames: [ {{ include "ocean-vpa.fullname" . }}-webhook ] + verbs: [ "get", "patch" ] +- apiGroups: [ "apps" ] + resources: [ "deployments" ] + resourceNames: [ {{ include "ocean-vpa.fullname" . }}-updater, {{ include "ocean-vpa.fullname" . }}-admission-controller ] + verbs: [ "get", "patch" ] +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/auto-update/rolebinding.yaml b/charts/ocean-right-sizing-controller/templates/auto-update/rolebinding.yaml new file mode 100644 index 0000000..f8ff593 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/auto-update/rolebinding.yaml @@ -0,0 +1,15 @@ +{{- if not .Values.spotinst.disableAutoUpdate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "auto-updater.fullname" . }} + labels: + {{- include "auto-updater.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "auto-updater.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "auto-updater.serviceAccountName" . }} +{{- end }} \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/auto-update/serviceaccount.yaml b/charts/ocean-right-sizing-controller/templates/auto-update/serviceaccount.yaml new file mode 100644 index 0000000..8ca23a1 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/auto-update/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if not .Values.spotinst.disableAutoUpdate }} +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "auto-updater.serviceAccountName" . }} + labels: + {{- include "auto-updater.labels" . | nindent 4 }} + {{- with .Values.autoUpdate.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/caBundle.secret.yaml b/charts/ocean-right-sizing-controller/templates/caBundle.secret.yaml new file mode 100644 index 0000000..5a4a49b --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/caBundle.secret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.caBundleSecret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "ocean-right-sizing-controller.caBundleSecretName" . }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +type: Opaque +data: + {{- .Values.caBundleSecret.key | nindent 2 -}}: {{ required "`caBundleSecret.data` must be specified if `caBundleSecret.create` is `true`" .Values.caBundleSecret.data | b64enc }} +{{- end }} diff --git a/charts/ocean-right-sizing-controller/templates/clusterrole.yaml b/charts/ocean-right-sizing-controller/templates/clusterrole.yaml new file mode 100644 index 0000000..cce7860 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/clusterrole.yaml @@ -0,0 +1,65 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "ocean-right-sizing-controller.fullname" . }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +rules: + # --------------------------------------------------------------------------- + # feature: ocean/readonly + # --------------------------------------------------------------------------- +- apiGroups: [ "" ] + resources: [ "pods", "nodes", "services", "namespaces", "replicationcontrollers", "limitranges", "events", "persistentvolumes", "persistentvolumeclaims" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "apps" ] + resources: [ "deployments", "daemonsets", "statefulsets", "replicasets" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "storage.k8s.io" ] + resources: [ "storageclasses" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "batch" ] + resources: [ "jobs", "cronjobs" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "policy" ] + resources: [ "poddisruptionbudgets" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "metrics.k8s.io" ] + resources: [ "pods" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "autoscaling" ] + resources: [ "horizontalpodautoscalers" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "autoscaling.k8s.io" ] + resources: [ "verticalpodautoscalers" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "apiextensions.k8s.io" ] + resources: [ "customresourcedefinitions" ] + verbs: [ "get", "list", "watch" ] +- apiGroups: [ "node.k8s.io" ] + resources: [ "runtimeclasses" ] + verbs: [ "get", "list", "watch" ] +- nonResourceURLs: [ "/version/", "/version" ] + verbs: [ "get" ] + + # --------------------------------------------------------------------------- + # feature: controller/leader-election (high-availability) + # --------------------------------------------------------------------------- +- apiGroups: [ "coordination.k8s.io" ] + resources: [ "leases" ] + verbs: [ "get","list","patch","update","create","delete" ] + + # --------------------------------------------------------------------------- + # feature: automatic right-sizing + # --------------------------------------------------------------------------- +- apiGroups: ["autoscaling.k8s.io"] + resources: ["verticalpodautoscalers", "verticalpodautoscalers/status"] + verbs: ["get", "list", "watch", "patch", "update", "create", "delete"] +- apiGroups: ["autoscaling"] + resources: ["horizontalpodautoscalers"] + verbs: ["patch", "update"] +- apiGroups: [ "" ] + resources: [ "pods"] + verbs: [ "get", "list", "patch", "update", "create", "delete" ] +- apiGroups: ["apps"] + resources: ["deployments", "daemonsets", "statefulsets", "replicasets"] + verbs: [ "get", "list", "watch", "patch", "update" ] \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/clusterrolebinding.yaml b/charts/ocean-right-sizing-controller/templates/clusterrolebinding.yaml new file mode 100644 index 0000000..1c6f180 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/clusterrolebinding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "ocean-right-sizing-controller.fullname" . }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "ocean-right-sizing-controller.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "ocean-right-sizing-controller.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} diff --git a/charts/ocean-right-sizing-controller/templates/configmap.yaml b/charts/ocean-right-sizing-controller/templates/configmap.yaml new file mode 100644 index 0000000..398b223 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/configmap.yaml @@ -0,0 +1,109 @@ +{{- if .Values.configMap.create }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +data: + spotinst.cluster-identifier: {{ required "`spotinst.clusterIdentifier` must be specified" .Values.spotinst.clusterIdentifier }} + base-url: {{ default "" .Values.spotinst.baseUrl | quote }} + proxy-url: {{ default "" .Values.spotinst.proxyUrl | quote }} + leader-election: {{ gt (int .Values.replicas) 1 | quote }} + disable-auto-update: {{ default "false" .Values.spotinst.disableAutoUpdate | quote }} + enable-csr-approval: {{ default "false" .Values.spotinst.enableCsrApproval | quote }} + {{- if not .Values.spotinst.disableAutoUpdate }} + auto-update.job: | + {{- include "auto-updater.job" . | nindent 4 }} + {{- end }} + {{- if eq (include "ocean-right-sizing-controller.logShipping.enabled" .) "true" }} + fluent-bit.conf: | + [SERVICE] + Parsers_File parsers.conf + Flush 60 + Daemon Off + + [INPUT] + Name tail + Path /var/log/controller.logs + Parser json + Buffer_Max_Size 2MB + Skip_Long_Lines On + Skip_Empty_Lines On + Refresh_Interval 10 + + [FILTER] + Name modify + Match * + Add ctrlPod ${POD_NAMESPACE}/${POD_NAME} + + # rename msg -> message , level -> l + [FILTER] + Name modify + Match * + Rename msg message + Rename level l + + # info -> INFO + [FILTER] + Name modify + Match * + + Condition Key_Value_Equals l info + SET l INFO + + # debug -> INFO + [FILTER] + Name modify + Match * + + Condition Key_Value_Equals l debug + SET l INFO + + # trace -> TRACE + [FILTER] + Name modify + Match * + + Condition Key_Value_Equals l trace + SET l TRACE + + # error -> ERROR + [FILTER] + Name modify + Match * + + Condition Key_Value_Equals l error + SET l ERROR + + # nest all fields under log key + [FILTER] + Name nest + Match * + Operation nest + Wildcard * + Nest_Under log + + # stringify log field + [FILTER] + Name Lua + Match * + call parse + code function stringify(obj) result = {} for key, value in pairs(obj) do table.insert(result, string.format("\"%s\":%q", key, value)) end result = "{" .. table.concat(result, ",") .. "}" return result end function parse(tag, timestamp, record) new_record = record new_record["log"] = stringify(record["log"]) return 1, timestamp, new_record end + + [OUTPUT] + Name http + Match * + Format json + Host {{ .Values.logShipping.destination.host }} + Port {{ .Values.logShipping.destination.port }} + TLS {{ .Values.logShipping.destination.tls }} + URI /logs/${CLUSTER_IDENTIFIER}?accountId=${SPOTINST_ACCOUNT} + Header Authorization Bearer ${SPOTINST_TOKEN} + Retry_Limit no_retries + parsers.conf: | + [PARSER] + Name json + Format json + {{- end }} +{{- end }} diff --git a/charts/ocean-right-sizing-controller/templates/deployment.yaml b/charts/ocean-right-sizing-controller/templates/deployment.yaml new file mode 100644 index 0000000..bd43e1d --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/deployment.yaml @@ -0,0 +1,263 @@ +{{ include "ocean-right-sizing-controller.deployMetricsServer" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "ocean-right-sizing-controller.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicas }} + {{- with .Values.updateStrategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "ocean-right-sizing-controller.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + # This will restart the deployment in case of configmap/secret/cluster-role changes + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + checksum/cluster-role: {{ include (print $.Template.BasePath "/clusterrole.yaml") . | sha256sum }} + kubectl.kubernetes.io/default-container: {{ .Chart.Name }} + + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "ocean-right-sizing-controller.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "ocean-right-sizing-controller.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.priorityClassName }} + priorityClassName: {{ . | quote }} + {{- end }} + {{- with .Values.initContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: {{ include "ocean-right-sizing-controller.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.command }} + command: + {{- toYaml . | nindent 12 }} + {{- end }} + args: + {{- range .Values.args }} + - {{ . }} + {{- end }} + {{- if eq (include "ocean-right-sizing-controller.logShipping.enabled" .) "true" }} + - --log_to_file + - --log_file=/var/log/controller.logs + - --log_file_max_size=1 + {{- end }} + {{- if .Values.spotinst.insecureSkipTLSVerify }} + - --insecure + {{- end }} + env: + - name: SPOTINST_TOKEN + valueFrom: + secretKeyRef: + name: {{ include "ocean-right-sizing-controller.secretName" . }} + key: token + - name: SPOTINST_ACCOUNT + valueFrom: + secretKeyRef: + name: {{ include "ocean-right-sizing-controller.secretName" . }} + key: account + - name: SPOTINST_LEADER_ELECTION_ENABLED + valueFrom: + configMapKeyRef: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + key: leader-election + optional: true + - name: CLUSTER_IDENTIFIER + valueFrom: + configMapKeyRef: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + key: spotinst.cluster-identifier + - name: BASE_SPOTINST_URL + valueFrom: + configMapKeyRef: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + key: base-url + optional: true + - name: HTTPS_PROXY + valueFrom: + configMapKeyRef: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + key: proxy-url + optional: true + - name: DISABLE_AUTO_UPDATE + valueFrom: + configMapKeyRef: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + key: disable-auto-update + optional: true + {{- if .Values.spotinst.readonly}} + - name: SPOTINST_READONLY_MODE + value: "true" + {{- end }} + {{- if not .Values.spotinst.disableAutoUpdate }} + - name: AUTO_UPDATE_JOB_SPEC + valueFrom: + configMapKeyRef: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + key: auto-update.job + optional: true + {{- end }} + - name: ENABLE_CSR_APPROVAL + valueFrom: + configMapKeyRef: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + key: enable-csr-approval + optional: true + - name: USER_ENV_CERTIFICATES + valueFrom: + secretKeyRef: + name: {{ include "ocean-right-sizing-controller.caBundleSecretName" . }} + key: {{ .Values.caBundleSecret.key }} + optional: true + - name: POD_ID + valueFrom: + fieldRef: + fieldPath: metadata.uid + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- include "ocean-right-sizing-controller.noProxyEnvVar" . | nindent 10 -}} + ports: + - name: metrics + containerPort: 9080 + - name: readiness + containerPort: 9081 + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- if eq (include "ocean-right-sizing-controller.logShipping.enabled" .) "true" }} + - name: logs + mountPath: /var/log + {{- end }} + resources: + {{- with .Values.resources }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if eq (include "ocean-right-sizing-controller.logShipping.enabled" .) "true" }} + - name: log-shipper + image: {{ .Values.logShipping.image.repository }}:{{ .Values.logShipping.image.tag }} + imagePullPolicy: {{ .Values.logShipping.image.pullPolicy }} + {{- with .Values.logShipping.command }} + command: + {{- toYaml . | nindent 12 }} + {{- end }} + env: + - name: SPOTINST_TOKEN + valueFrom: + secretKeyRef: + name: {{ include "ocean-right-sizing-controller.secretName" . }} + key: token + optional: true + - name: SPOTINST_ACCOUNT + valueFrom: + secretKeyRef: + name: {{ include "ocean-right-sizing-controller.secretName" . }} + key: account + optional: true + - name: CLUSTER_IDENTIFIER + valueFrom: + configMapKeyRef: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + key: spotinst.cluster-identifier + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- with .Values.logShipping.extraEnv }} + {{- toYaml . | nindent 10 }} + {{- end }} + volumeMounts: + - name: logs + mountPath: /var/log + - name: fluentbit-config + mountPath: /tmp/fluent-bit.conf + subPath: fluent-bit.conf + - name: fluentbit-config + mountPath: /tmp/parsers.conf + subPath: parsers.conf + {{- with .Values.logShipping.extraVolumeMounts }} + {{- toYaml . | nindent 10 }} + {{- end }} + securityContext: + {{- toYaml .Values.logShipping.securityContext | nindent 12 }} + {{- end }} + volumes: + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 6 }} + {{- end }} + {{- if eq (include "ocean-right-sizing-controller.logShipping.enabled" .) "true" }} + - name: logs + emptyDir: {} + - name: fluentbit-config + configMap: + name: {{ include "ocean-right-sizing-controller.configMapName" . }} + items: + - key: fluent-bit.conf + path: fluent-bit.conf + - key: parsers.conf + path: parsers.conf + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + affinity: + {{- include "ocean-right-sizing-controller.affinity" . | nindent 8 }} + {{- if kindIs "invalid" .Values.tolerations }} + tolerations: + {{- include "ocean-right-sizing-controller.defaultTolerations" . | nindent 6 }} + {{- else }} + {{- with .Values.tolerations }} + tolerations: + {{- . | toYaml | nindent 6 }} + {{- end }} + {{- end }} + topologySpreadConstraints: + {{- include "ocean-right-sizing-controller.topologySpreadConstraints" . | nindent 6 }} diff --git a/charts/ocean-right-sizing-controller/templates/resourcequota.yaml b/charts/ocean-right-sizing-controller/templates/resourcequota.yaml new file mode 100644 index 0000000..ae5026f --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/resourcequota.yaml @@ -0,0 +1,19 @@ +{{- if and (ne .Release.Namespace "kube-system") .Values.resourceQuota.enabled }} +apiVersion: v1 +kind: ResourceQuota +metadata: + name: {{ include "ocean-right-sizing-controller.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +spec: + hard: + pods: "1000" + scopeSelector: + matchExpressions: + - operator: In + scopeName: PriorityClass + values: + - system-node-critical + - system-cluster-critical +{{- end }} \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/role.yaml b/charts/ocean-right-sizing-controller/templates/role.yaml new file mode 100644 index 0000000..20b0bde --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/role.yaml @@ -0,0 +1,14 @@ +{{- if not .Values.spotinst.disableAutoUpdate }} +# This role is required only to fetch the logs of the auto-updater job. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "ocean-right-sizing-controller.fullname" . }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +rules: + # Get logs of the auto-updater job + - apiGroups: [ "" ] + resources: [ "pods/log" ] + verbs: [ "get" ] +{{- end }} \ No newline at end of file diff --git a/charts/ocean-right-sizing-controller/templates/rolebinding.yaml b/charts/ocean-right-sizing-controller/templates/rolebinding.yaml new file mode 100644 index 0000000..35261a1 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/rolebinding.yaml @@ -0,0 +1,15 @@ +{{- if not .Values.spotinst.disableAutoUpdate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "ocean-right-sizing-controller.fullname" . }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "ocean-right-sizing-controller.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "ocean-right-sizing-controller.serviceAccountName" . }} +{{- end }} diff --git a/charts/ocean-right-sizing-controller/templates/secret.yaml b/charts/ocean-right-sizing-controller/templates/secret.yaml new file mode 100644 index 0000000..d01b1b3 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/secret.yaml @@ -0,0 +1,12 @@ +{{- if .Values.secret.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "ocean-right-sizing-controller.secretName" . }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} +type: Opaque +data: + token: {{ required "`spotinst.token` must be specified" .Values.spotinst.token | b64enc }} + account: {{ required "`spotinst.account` must be specified" .Values.spotinst.account | b64enc }} +{{- end }} diff --git a/charts/ocean-right-sizing-controller/templates/serviceaccount.yaml b/charts/ocean-right-sizing-controller/templates/serviceaccount.yaml new file mode 100644 index 0000000..42d4b00 --- /dev/null +++ b/charts/ocean-right-sizing-controller/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "ocean-right-sizing-controller.serviceAccountName" . }} + labels: + {{- include "ocean-right-sizing-controller.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/ocean-right-sizing-controller/values.yaml b/charts/ocean-right-sizing-controller/values.yaml new file mode 100644 index 0000000..84081f0 --- /dev/null +++ b/charts/ocean-right-sizing-controller/values.yaml @@ -0,0 +1,301 @@ +# Default values for ocean-right-sizing-controller. + +nameOverride: "" +fullnameOverride: "" + +# Spot Configuration. +spotinst: + # -- Spot Token. (Required) + # Ref: https://docs.spot.io/administration/api/create-api-token + token: "" + # -- Spot Account ID. (Required) + # Example: `act-123abcd` + account: "" + # -- Unique identifier used by the Ocean Controller to connect (Required) + # between the Ocean backend and the Kubernetes cluster. + # Ref: https://docs.spot.io/ocean/tutorials/spot-kubernetes-controller/ + clusterIdentifier: "" + # -- Base URL. (Optional) + baseUrl: "" + # -- Proxy URL. (Optional) + proxyUrl: "" + # -- Disable auto update. (Optional) + disableAutoUpdate: false + # -- Enable CSR approval. (Optional) + enableCsrApproval: true + # -- Disable TLS certificate validation. (Optional) + insecureSkipTLSVerify: false + # -- Whether this controller needs to be readonly - overrides other permissions. (Optional) + readonly: true +# -- Configure the amount of replicas for the controller (Optional) +replicas: 2 + +image: + repository: us-docker.pkg.dev/spotit-today/container-labs/spotinst-kubernetes-controller + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion. + tag: "" + # -- Set to `true` to use an FIPS-140 compliant image. This flag adds `-fips` suffix to the image tag, + # therefore it should not be used together with the `--image.tag` flag. + # Ref: https://go.dev/doc/security/fips140 + fips: false + +initContainers: [] + +imagePullSecrets: [] + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. (Optional) + name: "" + +secret: + # -- Controls whether a Secret should be created. (Optional) + create: true + # -- Secret name. (Optional) + name: "" + +# CA bundle. +# Ref: https://kubernetes.io/docs/concepts/configuration/secret/ +caBundleSecret: + # -- CA bundle Secret name. (Optional) + name: "" + # -- Key inside the secret to inject the CA bundle from + key: "userEnvCertificates.pem" + # -- Controls whether a CA bundle secret should be created. + create: false + # -- Must contain the CA bundle data in case `caBundleSecret.create` is true. + # For example by using `--set caBundleSecret.data="$(cat ./ca.pem)"` + data: "" + +# Config Map. +# Ref: https://kubernetes.io/docs/concepts/configuration/configmap/ +configMap: + create: true + # -- ConfigMap name. (Optional) + name: "" + +podAnnotations: {} +podLabels: {} +commonLabels: {} + +# Pod Security Context +# Ref: https://kubernetes.io/docs/concepts/security/pod-security-standards/ +podSecurityContext: + runAsNonRoot: true + runAsUser: 1000690000 + runAsGroup: 1000690000 + fsGroup: 1000690000 + +# -- Priority class name for the controller pod. +priorityClassName: system-node-critical + +# -- Resource Quota configuration. Required when running in a namespace other than kube-system in GKE. +# Ref: https://kubernetes.io/docs/concepts/policy/resource-quotas/ +resourceQuota: + enabled: true + +# Container Security Context +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + +command: [] + +args: [] +# - --test + +extraEnv: [] +# - name: KEY +# value: VALUE + +livenessProbe: + httpGet: + path: /healthz + port: readiness + initialDelaySeconds: 15 + periodSeconds: 20 + +readinessProbe: + httpGet: + path: /readyz + port: readiness + initialDelaySeconds: 5 + periodSeconds: 10 + +# Controller pod resources. (Optional) +resources: {} + # requests: + # cpu: 100m + # memory: 128Mi + # limits: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +# -- Tolerations for nodes that have taints on them. (Optional) +# Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +tolerations: +# - key: node.kubernetes.io/not-ready +# effect: NoExecute +# operator: Exists +# tolerationSeconds: 150 + +# Pod scheduling preferences. +# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +affinity: +# podAntiAffinity: +# preferredDuringSchedulingIgnoredDuringExecution: +# - weight: 50 +# podAffinityTerm: +# labelSelector: +# matchExpressions: +# - key: app.kubernetes.io/name +# operator: In +# values: +# - spotinst-kubernetes-cluster-controller +# topologyKey: kubernetes.io/hostname + +topologySpreadConstraints: +# - maxSkew: 1 +# topologyKey: kubernetes.io/hostname +# whenUnsatisfiable: ScheduleAnyway +# labelSelector: +# app: test + +extraVolumeMounts: [] + +extraVolumes: [] + +schedulerName: "" + +# Annotations to add to the deployment +deploymentAnnotations: {} + +# Deployment update strategy +updateStrategy: {} +# type: RollingUpdate +# rollingUpdate: +# maxSurge: 0 +# maxUnavailable: 1 + +# Metrics Server configuration. +metrics-server: + # -- Specifies whether the metrics-server chart should be deployed. + deployChart: true + + # Overrides the image + image: + repository: registry.k8s.io/metrics-server/metrics-server + tag: "" + pullPolicy: IfNotPresent + + # -- Arguments to pass to metrics-server on start up. (Optional) + # args: + # enable this if you have self-signed certificates, see: https://github.com/kubernetes-incubator/metrics-server + # - --kubelet-insecure-tls + +# Ocean VPA configuration. +ocean-vpa: + # -- Specifies whether the ocean-vpa chart should be deployed. + deployChart: true + +# -- Log Shipping configuration. +logShipping: + # -- Specifies whether to send the controller logs to Spot for analysis. (Optional) + enabled: true + + image: + # -- Image repository. (Optional) + repository: ghcr.io/fluent/fluent-bit + # -- Overrides the image tag. (Optional) + tag: "3.1.9" + # -- Image pull policy. (Optional) + pullPolicy: IfNotPresent + + # -- Log shipping destination configuration. + destination: + host: api.spotinst.io + port: 443 + tls: true + + extraVolumeMounts: [] + + extraEnv: [] + + # -- Log shipping container command. (Optional) + command: + - /fluent-bit/bin/fluent-bit + - -c + - /tmp/fluent-bit.conf + - -q + + # -- Log Shipping container security context + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + +# Auto Update process configuration. +autoUpdate: + # -- Configures the image for the auto-updater job. (Optional) + image: + # -- Image repository. (Optional) + repository: us-docker.pkg.dev/spotit-today/container-labs/auto-updater + # -- Overrides the image tag. (Optional) + tag: "latest" + # -- Image pull policy. (Optional) + pullPolicy: Always + + # -- Image pull secrets. (Optional) + imagePullSecrets: [] + + # -- Pod Security Context for the auto-updater job. (Optional) + # Ref: https://kubernetes.io/docs/concepts/security/pod-security-standards/ + podSecurityContext: + runAsNonRoot: true + runAsUser: 1000690000 + runAsGroup: 1000690000 + fsGroup: 1000690000 + + # -- Security Context for the auto-updater container. (Optional) + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + + # -- Resource requests and limits for the auto-updater job. + # Defaults to 100m CPU and 256Mi memory to make the job run with 'Guranteed' QoS. (Optional) + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 100m + memory: 256Mi + + # -- Priority class name for the auto-updater job. + # Defaults to the same priority class as the controller to prevent eviction. (Optional) + priorityClassName: system-cluster-critical + + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. (Optional) + name: ""