diff --git a/.github/workflows/docker-release.yaml b/.github/workflows/docker-release.yaml new file mode 100644 index 00000000..e85a3c3f --- /dev/null +++ b/.github/workflows/docker-release.yaml @@ -0,0 +1,13 @@ +name: Release Docker Image + +on: + workflow_dispatch: # for manual testing + push: + # Publish semver tags as releases: + tags: ["v*.*.*"] + +jobs: + call-docker-release: + permissions: + packages: write + uses: thealtoclef/modular-automations/.github/workflows/docker-release-forked-repo.yaml@main diff --git a/.github/workflows/helm-release.yaml b/.github/workflows/helm-release.yaml new file mode 100644 index 00000000..16274079 --- /dev/null +++ b/.github/workflows/helm-release.yaml @@ -0,0 +1,14 @@ +name: Release Helm Chart + +on: + workflow_dispatch: # for manual testing + push: + # Publish semver tags as releases: + tags: ["helm-v*.*.*"] + +jobs: + call-helm-release: + permissions: + contents: write + packages: write + uses: thealtoclef/modular-automations/.github/workflows/helm-release.yaml@main diff --git a/Makefile b/Makefile index 7d5aa7e7..29c0e133 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,7 @@ help: ## Display this help. .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 + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=helm-charts/doris-resource-operator/crds .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. diff --git a/helm-charts/doris-resource-operator/.helmignore b/helm-charts/doris-resource-operator/.helmignore new file mode 100755 index 00000000..0e8a0eb3 --- /dev/null +++ b/helm-charts/doris-resource-operator/.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/helm-charts/doris-resource-operator/Chart.yaml b/helm-charts/doris-resource-operator/Chart.yaml new file mode 100755 index 00000000..fd3d0380 --- /dev/null +++ b/helm-charts/doris-resource-operator/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: doris-manager +description: A Helm chart for Kubernetes +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: "0.0.1" +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.0.1" diff --git a/helm-charts/doris-resource-operator/README.md b/helm-charts/doris-resource-operator/README.md new file mode 100644 index 00000000..bcacee72 --- /dev/null +++ b/helm-charts/doris-resource-operator/README.md @@ -0,0 +1,40 @@ +# [mysql-operator](https://github.com/nakamasato/mysql-operator) + +## Values + +1. **adminUserSecretType**: `raw`, `gcp` or `k8s` . With `raw`, you need to give root user password in `MySQL` custom resource. With `gcp`, you can securely store root user password in GCP SecretManager. This root user password is used to manage (create/edit/update) MySQL users, databases, etc. With k8s you need to create (in the same namespace where this operator is installed) two kubernetes secrets one for the root username and another one for root password. +1. **gcpServiceAccount**: Only for `adminUserSecretType=gcp`. GCP service account for Pod `SA_NAME@PROJECT.iam.gserviceaccount.com` + 1. This service account needs the following roles: + 1. `roles/secretmanager.secretAccessor` to allow to get root password from SecretManager +1. **gcpProjectId**: Only for `adminUserSecretType=gcp` +1. **adminUserSecretNamespace**: Only for `adminUserSecretType=k8s`. Kubernetes Namespace of Secret for MySQL admin user credentials. +1. **cloudSQL.instanceConnectionName**: `InstanceConnectionName` for [Google Cloud SQL](https://cloud.google.com/sql/) if you use Cloud SQL to manage with mysql-operator. `::` + + + +## Usage + +### Dryrun + +You can check the final yaml with `--dry-run`: + +``` +helm install mysql-operator ./charts/mysql-operator \ + --dry-run \ + --set adminUserSecretType=gcp \ + --set gcpServiceAccount=${SA_NAME}@${PROJECT}.iam.gserviceaccount.com \ + --set gcpProjectId=$PROJECT \ + --set cloudSQL.instanceConnectionName=$PROJECT:$REGION:$INSTANCE_NAME \ + --namespace mysql-operator +``` + +### Install + +``` +helm install mysql-operator ./charts/mysql-operator \ + --set adminUserSecretType=gcp \ + --set gcpServiceAccount=${SA_NAME}@${PROJECT}.iam.gserviceaccount.com \ + --set gcpProjectId=$PROJECT \ + --set cloudSQL.instanceConnectionName=$PROJECT:$REGION:$INSTANCE_NAME \ + --namespace mysql-operator +``` diff --git a/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqldbs.yaml b/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqldbs.yaml new file mode 100644 index 00000000..6b4ad1ee --- /dev/null +++ b/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqldbs.yaml @@ -0,0 +1,109 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: mysqldbs.mysql.nakamasato.com +spec: + group: mysql.nakamasato.com + names: + kind: MySQLDB + listKind: MySQLDBList + plural: mysqldbs + singular: mysqldb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The phase of MySQLDB + jsonPath: .status.phase + name: Phase + type: string + - description: The reason for the current phase of this MySQLDB + jsonPath: .status.reason + name: Reason + type: string + - description: schema_migration table if schema migration is enabled. + jsonPath: .status.schemaMigration + name: SchemaMigration + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: MySQLDB is the Schema for the mysqldbs 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: MySQLDBSpec defines the desired state of MySQLDB + properties: + clusterName: + description: Cluster name to reference to, which decides the destination + type: string + x-kubernetes-validations: + - message: Cluster name is immutable + rule: self == oldSelf + dbName: + description: MySQL Database name + type: string + schemaMigrationFromGitHub: + description: MySQL Database Schema Migrations from GitHub + properties: + owner: + type: string + path: + type: string + ref: + type: string + repo: + type: string + required: + - owner + - path + - repo + type: object + required: + - clusterName + - dbName + type: object + status: + description: MySQLDBStatus defines the observed state of MySQLDB + properties: + phase: + description: The phase of database creation + type: string + reason: + description: The reason for the current phase + type: string + schemaMigration: + description: Schema Migration status + properties: + dirty: + type: boolean + version: + type: integer + required: + - dirty + - version + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqls.yaml b/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqls.yaml new file mode 100644 index 00000000..38355228 --- /dev/null +++ b/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqls.yaml @@ -0,0 +1,138 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: mysqls.mysql.nakamasato.com +spec: + group: mysql.nakamasato.com + names: + kind: MySQL + listKind: MySQLList + plural: mysqls + singular: mysql + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.host + name: Host + type: string + - jsonPath: .spec.adminUser.name + name: AdminUser + type: string + - jsonPath: .status.connected + name: Connected + type: boolean + - description: The number of MySQLUsers that belongs to the MySQL + jsonPath: .status.userCount + name: UserCount + type: integer + - description: The number of MySQLDBs that belongs to the MySQL + jsonPath: .status.dbCount + name: DBCount + type: integer + - jsonPath: .status.reason + name: Reason + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: MySQL is the Schema for the mysqls 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: MySQLSpec holds the connection information for the target + MySQL cluster. + properties: + adminPassword: + description: AdminPassword is MySQL password to connect target MySQL + cluster. + properties: + name: + description: Secret Name + type: string + type: + description: Secret Type (e.g. gcp, raw, k8s) + enum: + - raw + - gcp + - k8s + type: string + required: + - name + - type + type: object + adminUser: + description: AdminUser is MySQL user to connect target MySQL cluster. + properties: + name: + description: Secret Name + type: string + type: + description: Secret Type (e.g. gcp, raw, k8s) + enum: + - raw + - gcp + - k8s + type: string + required: + - name + - type + type: object + host: + description: Host is MySQL host of target MySQL cluster. + type: string + port: + default: 3306 + description: Port is MySQL port of target MySQL cluster. + type: integer + required: + - adminPassword + - adminUser + - host + type: object + status: + description: MySQLStatus defines the observed state of MySQL + properties: + connected: + description: true if successfully connected to the MySQL cluster + type: boolean + dbCount: + default: 0 + description: The number of database in this MySQL + format: int32 + type: integer + reason: + description: Reason for connection failure + type: string + userCount: + default: 0 + description: The number of users in this MySQL + format: int32 + type: integer + required: + - dbCount + - userCount + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqlusers.yaml b/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqlusers.yaml new file mode 100644 index 00000000..1955632e --- /dev/null +++ b/helm-charts/doris-resource-operator/crds/mysql.nakamasato.com_mysqlusers.yaml @@ -0,0 +1,219 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: mysqlusers.mysql.nakamasato.com +spec: + group: mysql.nakamasato.com + names: + kind: MySQLUser + listKind: MySQLUserList + plural: mysqlusers + singular: mysqluser + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: true if MySQL user is created + jsonPath: .status.userCreated + name: MySQLUser + type: boolean + - description: true if Secret is created + jsonPath: .status.secretCreated + name: Secret + type: boolean + - description: true if Grants are updated + jsonPath: .status.grantsUpdated + name: Grants + type: boolean + - description: The phase of this MySQLUser + jsonPath: .status.phase + name: Phase + type: string + - description: The reason for the current phase of this MySQLUser + jsonPath: .status.reason + name: Reason + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: MySQLUser is the Schema for the mysqlusers 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: MySQLUserSpec defines the desired state of MySQLUser + properties: + clusterName: + description: Cluster name to reference to, which decides the destination + type: string + x-kubernetes-validations: + - message: Cluster name is immutable + rule: self == oldSelf + grants: + description: Grants of database user + items: + description: Grant defines the privileges and the resource for a + MySQL user + properties: + privileges: + description: Privileges to grant to the user + items: + type: string + type: array + target: + description: Target on which the privileges are applied + type: string + required: + - privileges + - target + type: object + type: array + host: + default: '%' + description: Host address where the client connects, default to '%' + pattern: ^(\*|%|[a-zA-Z0-9._-]+|\d{1,3}(\.\d{1,3}){3})$ + type: string + x-kubernetes-validations: + - message: Host is immutable + rule: self == oldSelf + secretRef: + description: SecretRef is a reference to a Secret containing the password + properties: + key: + description: Key in the Secret that contains the password + type: string + name: + description: Name of the Secret containing the password + type: string + required: + - key + - name + type: object + username: + description: Username + maxLength: 64 + pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ + type: string + x-kubernetes-validations: + - message: Username is immutable + rule: self == oldSelf + required: + - clusterName + - host + - secretRef + - username + type: object + status: + description: MySQLUserStatus defines the observed state of MySQLUser + properties: + conditions: + 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 + grantsUpdated: + default: false + description: true if Grants are updated + type: boolean + passwordUpdated: + default: false + description: true if User's password is updated + type: boolean + phase: + type: string + reason: + type: string + userCreated: + default: false + description: true if User is created + type: boolean + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm-charts/doris-resource-operator/templates/_helpers.tpl b/helm-charts/doris-resource-operator/templates/_helpers.tpl new file mode 100755 index 00000000..0a1dd23d --- /dev/null +++ b/helm-charts/doris-resource-operator/templates/_helpers.tpl @@ -0,0 +1,57 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "chart.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 "chart.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 chart name and version as used by the chart label. +*/}} +{{- define "chart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} +{{/* +Common labels +*/}} +{{- define "chart.labels" -}} +helm.sh/chart: {{ include "chart.chart" . }} +{{ include "chart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} +{{/* +Selector labels +*/}} +{{- define "chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} +{{/* +Create the name of the service account to use +*/}} +{{- define "chart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "chart.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm-charts/doris-resource-operator/templates/deployment.yaml b/helm-charts/doris-resource-operator/templates/deployment.yaml new file mode 100644 index 00000000..050f2885 --- /dev/null +++ b/helm-charts/doris-resource-operator/templates/deployment.yaml @@ -0,0 +1,124 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "chart.fullname" . }}-controller-manager + labels: + {{- include "chart.labels" . | nindent 4 }} + {{- if .Values.gcpServiceAccount }} + annotations: + iam.gke.io/gcp-service-account: {{ .Values.gcpServiceAccount }} + {{- end }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "chart.fullname" . }}-controller-manager + labels: + control-plane: controller-manager + {{- include "chart.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.controllerManager.replicas }} + selector: + matchLabels: + control-plane: controller-manager + {{- include "chart.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + control-plane: controller-manager + {{- include "chart.selectorLabels" . | nindent 8 }} + spec: + containers: + - args: + - --leader-elect + - --admin-user-secret-type={{ .Values.adminUserSecretType | default "raw" }} + {{- if eq .Values.adminUserSecretType "k8s" }} + - --k8s-secret-namespace={{ .Values.k8sSecretNamespace | default "default" }} + {{- end }} + command: + - /manager + image: {{ .Values.controllerManager.manager.image.repository }}:{{ .Values.controllerManager.manager.image.tag | default .Chart.AppVersion }} + imagePullPolicy: {{ .Values.controllerManager.manager.image.pullPolicy }} + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + {{- if .Values.controllerManager.manager.resources }} + resources: + {{- toYaml .Values.controllerManager.manager.resources | nindent 10 }} + {{- end }} + {{- if .Values.controllerManager.manager.securityContext }} + securityContext: + {{- toYaml .Values.controllerManager.manager.securityContext | nindent 10 }} + {{- end }} + {{- if eq .Values.adminUserSecretType "gcp" }} + {{- if .Values.gcpServiceAccount }} + env: + - name: PROJECT_ID + value: {{ .Values.gcpProjectId }} + {{- else }} + volumeMounts: + - name: gcp-sa-private-key + mountPath: /var/secrets/google + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /var/secrets/google/sa-private-key.json + - name: PROJECT_ID + value: {{ .Values.gcpProjectId }} + {{- end }} + {{- end }} + + # https://cloud.google.com/sql/docs/mysql/connect-instance-kubernetes#deploy_the_sample_app + {{- with .Values.cloudSQL }} + # If you are using the Go Connector (recommended), you can + # remove cloud-sql-proxy (everything below this line) + - name: cloud-sql-proxy + # This uses the latest version of the Cloud SQL Proxy + # It is recommended to use a specific version for production environments. + # See: https://github.com/GoogleCloudPlatform/cloudsql-proxy + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:latest + args: + # If connecting from a VPC-native GKE cluster, you can use the + # following flag to have the proxy connect over private IP + {{- if .usePrivateIp }} + # - "--private-ip" + {{- end }} + + # If you are not connecting with Automatic IAM, you can delete + # the following flag. + {{- if .enableIamAuth }} + - "--auto-iam-authn" + {{- end }} + + # tcp should be set to the port the proxy should listen on + # and should match the DB_PORT value set above. + # Defaults: MySQL: 3306, Postgres: 5432, SQLServer: 1433 + - "--port=3306" + - {{ .instanceConnectionName }} + securityContext: + # The default Cloud SQL proxy image runs as the + # "nonroot" user and group (uid: 65532) by default. + runAsNonRoot: true + {{- end }} + + {{- if .Values.controllerManager.podSecurityContext }} + securityContext: + {{- toYaml .Values.controllerManager.podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "chart.fullname" . }}-controller-manager + terminationGracePeriodSeconds: 10 + {{- if and (eq .Values.adminUserSecretType "gcp") (empty .Values.gcpServiceAccount) }} + volumes: + - name: gcp-sa-private-key + secret: + secretName: gcp-sa-private-key + {{- end }} diff --git a/helm-charts/doris-resource-operator/templates/leader-election-rbac.yaml b/helm-charts/doris-resource-operator/templates/leader-election-rbac.yaml new file mode 100644 index 00000000..d0e738e4 --- /dev/null +++ b/helm-charts/doris-resource-operator/templates/leader-election-rbac.yaml @@ -0,0 +1,53 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "chart.fullname" . }}-leader-election-role + labels: + {{- include "chart.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 +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "chart.fullname" . }}-leader-election-rolebinding + labels: + {{- include "chart.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: '{{ include "chart.fullname" . }}-leader-election-role' +subjects: +- kind: ServiceAccount + name: '{{ include "chart.fullname" . }}-controller-manager' + namespace: '{{ .Release.Namespace }}' diff --git a/helm-charts/doris-resource-operator/templates/manager-config.yaml b/helm-charts/doris-resource-operator/templates/manager-config.yaml new file mode 100644 index 00000000..af5eaf38 --- /dev/null +++ b/helm-charts/doris-resource-operator/templates/manager-config.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "chart.fullname" . }}-manager-config + labels: + {{- include "chart.labels" . | nindent 4 }} +data: + controller_manager_config.yaml: | + apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 + health: + healthProbeBindAddress: {{ .Values.managerConfig.controllerManagerConfigYaml.health.healthProbeBindAddress + | quote }} + kind: ControllerManagerConfig + leaderElection: + leaderElect: {{ .Values.managerConfig.controllerManagerConfigYaml.leaderElection.leaderElect + }} + resourceName: {{ .Values.managerConfig.controllerManagerConfigYaml.leaderElection.resourceName + | quote }} + metrics: + bindAddress: {{ .Values.managerConfig.controllerManagerConfigYaml.metrics.bindAddress + | quote }} + webhook: + port: {{ .Values.managerConfig.controllerManagerConfigYaml.webhook.port }} diff --git a/helm-charts/doris-resource-operator/templates/manager-rbac.yaml b/helm-charts/doris-resource-operator/templates/manager-rbac.yaml new file mode 100644 index 00000000..919f157b --- /dev/null +++ b/helm-charts/doris-resource-operator/templates/manager-rbac.yaml @@ -0,0 +1,120 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "chart.fullname" . }}-manager-role + labels: + {{- include "chart.labels" . | nindent 4 }} +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - update +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqldbs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqldbs/finalizers + verbs: + - update +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqldbs/status + verbs: + - get + - patch + - update +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqls + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqls/finalizers + verbs: + - update +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqls/status + verbs: + - get + - patch + - update +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqlusers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqlusers/finalizers + verbs: + - update +- apiGroups: + - mysql.nakamasato.com + resources: + - mysqlusers/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "chart.fullname" . }}-manager-rolebinding + labels: + {{- include "chart.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: '{{ include "chart.fullname" . }}-manager-role' +subjects: +- kind: ServiceAccount + name: '{{ include "chart.fullname" . }}-controller-manager' + namespace: '{{ .Release.Namespace }}' diff --git a/helm-charts/doris-resource-operator/templates/metrics-reader-rbac.yaml b/helm-charts/doris-resource-operator/templates/metrics-reader-rbac.yaml new file mode 100644 index 00000000..ce5d8b5a --- /dev/null +++ b/helm-charts/doris-resource-operator/templates/metrics-reader-rbac.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "chart.fullname" . }}-metrics-reader + labels: + {{- include "chart.labels" . | nindent 4 }} +rules: +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/helm-charts/doris-resource-operator/templates/metrics-service.yaml b/helm-charts/doris-resource-operator/templates/metrics-service.yaml new file mode 100644 index 00000000..2fbef6b7 --- /dev/null +++ b/helm-charts/doris-resource-operator/templates/metrics-service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "chart.fullname" . }}-controller-manager-metrics-service + labels: + control-plane: controller-manager + {{- include "chart.labels" . | nindent 4 }} +spec: + type: {{ .Values.metricsService.type }} + selector: + control-plane: controller-manager + {{- include "chart.selectorLabels" . | nindent 4 }} + ports: + {{- .Values.metricsService.ports | toYaml | nindent 2 -}} diff --git a/helm-charts/doris-resource-operator/templates/proxy-rbac.yaml b/helm-charts/doris-resource-operator/templates/proxy-rbac.yaml new file mode 100644 index 00000000..d40fc07d --- /dev/null +++ b/helm-charts/doris-resource-operator/templates/proxy-rbac.yaml @@ -0,0 +1,34 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "chart.fullname" . }}-proxy-role + labels: + {{- include "chart.labels" . | nindent 4 }} +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "chart.fullname" . }}-proxy-rolebinding + labels: + {{- include "chart.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: '{{ include "chart.fullname" . }}-proxy-role' +subjects: +- kind: ServiceAccount + name: '{{ include "chart.fullname" . }}-controller-manager' + namespace: '{{ .Release.Namespace }}' diff --git a/helm-charts/doris-resource-operator/values.yaml b/helm-charts/doris-resource-operator/values.yaml new file mode 100644 index 00000000..5aa85eb3 --- /dev/null +++ b/helm-charts/doris-resource-operator/values.yaml @@ -0,0 +1,45 @@ +# set gcp if you use GCP SecretManager +# set k8s if you use Kubernetes secrets +adminUserSecretType: raw # set gcp if you use GCP SecretManager +# gcpServiceAccount: GSA_NAME@GSA_PROJECT.iam.gserviceaccount.com +# gcpProjectId: +# cloudSQL: +# instanceConnectionName: +# enableIamAuth: false +# usePrivateIp: false +k8sSecretNamespace: default +controllerManager: + manager: + image: + repository: ghcr.io/nakamasato/mysql-operator + pullPolicy: IfNotPresent + resources: + limits: + cpu: 200m + memory: 100Mi + requests: + cpu: 100m + memory: 20Mi + securityContext: + allowPrivilegeEscalation: false + podSecurityContext: + runAsNonRoot: true + replicas: 1 +managerConfig: + controllerManagerConfigYaml: + health: + healthProbeBindAddress: :8081 + leaderElection: + leaderElect: true + resourceName: dfc6d3c2.nakamasato.com + metrics: + bindAddress: 127.0.0.1:8080 + webhook: + port: 9443 +metricsService: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + type: ClusterIP