diff --git a/.project b/.project index c07e780..edfa99f 100644 --- a/.project +++ b/.project @@ -13,4 +13,4 @@ operatorName: redis-operator-cop.cs.sap.com owner: SAP SE resource: redisoperators validatingWebhookEnabled: false -version: latest +version: v0.1.4 diff --git a/Makefile b/Makefile index c168228..6611a5b 100644 --- a/Makefile +++ b/Makefile @@ -3,34 +3,15 @@ IMG ?= redis-operator-cop:latest # K8s version used by envtest ENVTEST_K8S_VERSION = 1.26.1 -# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) -ifeq (,$(shell go env GOBIN)) -GOBIN=$(shell go env GOPATH)/bin -else -GOBIN=$(shell go env GOBIN) -endif - -# Setting SHELL to bash allows bash commands to be executed by recipes. -# Options are set to exit when a recipe line exits non-zero or a piped command fails. -SHELL = /usr/bin/env bash -o pipefail -.SHELLFLAGS = -ec +# Set shell to bash +SHELL = /usr/bin/env bash +.SHELLFLAGS = -o pipefail -ec .PHONY: all all: build ##@ General -# The help target prints out all targets with their descriptions organized -# beneath their categories. The categories are represented by '##@' and the -# target descriptions by '##'. The awk command is responsible for reading the -# entire set of makefiles included in this invocation, looking for lines of the -# file as xyz: ## something, and then pretty-format the target and help. Then, -# if there's a line with ##@ something, that gets pretty-printed as a category. -# More info on the usage of ANSI control characters for terminal formatting: -# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters -# More info on the awk command: -# http://linuxcommand.org/lc3_adv_awk.php - .PHONY: help help: ## Display this help. @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) @@ -39,12 +20,17 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate CustomResourceDefinition objects. - $(CONTROLLER_GEN) crd paths="./api/..." output:crd:artifacts:config=crds + $(CONTROLLER_GEN) crd paths="./api/..." output:crd:artifacts:config=crds ;\ + test ! -d chart || test -e chart/crds || ln -s ../crds chart/crds .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./api/..." +.PHONY: generate-client +generate-client: client-gen informer-gen lister-gen ## Generate typed client. + ./hack/genclient.sh + .PHONY: fmt fmt: ## Run go fmt against code. go fmt ./... @@ -83,13 +69,10 @@ docker-push: ## Push docker image with the manager. PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le .PHONY: docker-buildx docker-buildx: ## Build and push docker image for the manager for cross-platform support. - # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile - sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - docker buildx create --name project-v3-builder docker buildx use project-v3-builder - - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} . - docker buildx rm project-v3-builder - rm Dockerfile.cross ##@ Build Dependencies @@ -100,10 +83,14 @@ $(LOCALBIN): ## Tool Binaries CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +CLIENT_GEN ?= $(LOCALBIN)/client-gen +INFORMER_GEN ?= $(LOCALBIN)/informer-gen +LISTER_GEN ?= $(LOCALBIN)/lister-gen SETUP_ENVTEST ?= $(LOCALBIN)/setup-envtest ## Tool Versions CONTROLLER_TOOLS_VERSION ?= v0.9.2 +CODE_GENERATOR_VERSION ?= v0.27.3 SETUP_ENVTEST_VERSION ?= latest .PHONY: controller-gen @@ -111,6 +98,21 @@ controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessar $(CONTROLLER_GEN): $(LOCALBIN) test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) +.PHONY: client-gen +client-gen: $(CLIENT_GEN) ## Download client-gen locally if necessary. +$(CLIENT_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/client-gen || GOBIN=$(LOCALBIN) go install k8s.io/code-generator/cmd/client-gen@$(CODE_GENERATOR_VERSION) + +.PHONY: informer-gen +informer-gen: $(INFORMER_GEN) ## Download informer-gen locally if necessary. +$(INFORMER_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/informer-gen || GOBIN=$(LOCALBIN) go install k8s.io/code-generator/cmd/informer-gen@$(CODE_GENERATOR_VERSION) + +.PHONY: lister-gen +lister-gen: $(LISTER_GEN) ## Download lister-gen locally if necessary. +$(LISTER_GEN): $(LOCALBIN) + test -s $(LOCALBIN)/lister-gen || GOBIN=$(LOCALBIN) go install k8s.io/code-generator/cmd/lister-gen@$(CODE_GENERATOR_VERSION) + .PHONY: setup-envtest setup-envtest: $(SETUP_ENVTEST) ## Download setup-envtest locally if necessary. $(SETUP_ENVTEST): $(LOCALBIN) diff --git a/api/v1alpha1/groupversion_info.go b/api/v1alpha1/groupversion_info.go index 1b57745..2414cf2 100644 --- a/api/v1alpha1/groupversion_info.go +++ b/api/v1alpha1/groupversion_info.go @@ -22,4 +22,12 @@ var ( // AddToScheme adds the types in this group-version to the given scheme. AddToScheme = SchemeBuilder.AddToScheme + + // Needed by kubernetes/code-generator. + SchemeGroupVersion = GroupVersion ) + +// Needed by kubernetes/code-generator. +func Resource(resource string) schema.GroupResource { + return GroupVersion.WithResource(resource).GroupResource() +} diff --git a/api/v1alpha1/types.go b/api/v1alpha1/types.go index 416f381..293a270 100644 --- a/api/v1alpha1/types.go +++ b/api/v1alpha1/types.go @@ -33,6 +33,7 @@ type RedisOperatorStatus struct { // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="State",type=string,JSONPath=`.status.state` // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +genclient // RedisOperator is the Schema for the redisoperators API. type RedisOperator struct { diff --git a/chart/.helmignore b/chart/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 0000000..1744f53 --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: redis-operator-cop +description: A Helm chart for https://github.com/sap/redis-operator-cop +type: application +version: 0.1.0 +appVersion: v0.1.0 diff --git a/chart/crds/operator.kyma-project.io_redisoperators.yaml b/chart/crds/operator.kyma-project.io_redisoperators.yaml new file mode 100644 index 0000000..f68c8b0 --- /dev/null +++ b/chart/crds/operator.kyma-project.io_redisoperators.yaml @@ -0,0 +1,165 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.9.2 + creationTimestamp: null + name: redisoperators.operator.kyma-project.io +spec: + group: operator.kyma-project.io + names: + kind: RedisOperator + listKind: RedisOperatorList + plural: redisoperators + singular: redisoperator + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.state + name: State + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: RedisOperator is the Schema for the redisoperators 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: RedisOperatorSpec defines the desired state of RedisOperator. + type: object + status: + default: + observedGeneration: -1 + description: RedisOperatorStatus defines the observed state of RedisOperator. + properties: + appliedGeneration: + format: int64 + type: integer + conditions: + items: + description: Component status Condition. + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + description: Condition Status. Can be one of 'True', 'False', + 'Unknown'. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: Condition type. Currently, only the 'Ready' type + is used. + type: string + required: + - status + - type + type: object + type: array + inventory: + items: + description: InventoryItem represents a dependent object managed + by this operator. + properties: + digest: + description: Digest of the descriptor of the dependent object. + type: string + group: + description: API group. + type: string + kind: + description: API kind. + type: string + managedTypes: + description: Managed types + items: + description: TypeInfo represents a Kubernetes type. + properties: + group: + description: API group. + type: string + kind: + description: API kind. + type: string + version: + description: API group version. + type: string + required: + - group + - kind + - version + type: object + type: array + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object; empty for non-namespaced + objects + type: string + phase: + description: Phase of the dependent object. + type: string + status: + description: Observed status of the dependent object, as observed + by kstatus. + type: string + version: + description: API group version. + type: string + required: + - digest + - group + - kind + - name + - version + type: object + type: array + lastAppliedAt: + format: date-time + type: string + lastObservedAt: + format: date-time + type: string + observedGeneration: + format: int64 + type: integer + state: + description: Component state. Can be one of 'Ready', 'Processing', + 'Error', 'Deleting'. + enum: + - Processing + - Deleting + - Ready + - Error + type: string + required: + - observedGeneration + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl new file mode 100644 index 0000000..52d7f13 --- /dev/null +++ b/chart/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "redis-operator-cop.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 "redis-operator-cop.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 "redis-operator-cop.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "redis-operator-cop.labels" -}} +helm.sh/chart: {{ include "redis-operator-cop.chart" . }} +{{ include "redis-operator-cop.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "redis-operator-cop.selectorLabels" -}} +app.kubernetes.io/name: {{ include "redis-operator-cop.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml new file mode 100644 index 0000000..29eea48 --- /dev/null +++ b/chart/templates/deployment.yaml @@ -0,0 +1,106 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "redis-operator-cop.fullname" . }} + labels: + {{- include "redis-operator-cop.labels" . | nindent 4 }} + redis-operator-cop.cs.sap.com/ignored: "true" +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "redis-operator-cop.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "redis-operator-cop.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- range . }} + - {{ toYaml . | trim | nindent 8 }} + {{- if not .labelSelector }} + labelSelector: + matchLabels: + {{- include "redis-operator-cop.selectorLabels" $ | nindent 12 }} + {{- end }} + {{- end }} + {{- else }} + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + nodeTaintsPolicy: Honor + whenUnsatisfiable: {{ .Values.defaultHostNameSpreadPolicy }} + labelSelector: + matchLabels: + {{- include "redis-operator-cop.selectorLabels" . | nindent 12 }} + - maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + nodeTaintsPolicy: Honor + whenUnsatisfiable: {{ .Values.defaultZoneSpreadPolicy }} + labelSelector: + matchLabels: + {{- include "redis-operator-cop.selectorLabels" . | nindent 12 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.priorityClassName }} + priorityClassName: {{ . }} + {{- end }} + serviceAccountName: {{ include "redis-operator-cop.fullname" . }} + automountServiceAccountToken: true + containers: + - name: manager + image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - --leader-elect + ports: + - name: metrics + containerPort: 8080 + protocol: TCP + - name: probes + containerPort: 8081 + protocol: TCP + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + livenessProbe: + httpGet: + port: probes + scheme: HTTP + path: /healthz + readinessProbe: + httpGet: + port: probes + scheme: HTTP + path: /readyz diff --git a/chart/templates/pdb.yaml b/chart/templates/pdb.yaml new file mode 100644 index 0000000..b4d2d9a --- /dev/null +++ b/chart/templates/pdb.yaml @@ -0,0 +1,13 @@ +{{- if ge (int .Values.replicaCount) 2 }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "redis-operator-cop.fullname" . }} + labels: + {{- include "redis-operator-cop.labels" . | nindent 4 }} +spec: + minAvailable: 1 + selector: + matchLabels: + {{- include "redis-operator-cop.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/chart/templates/rbac.yaml b/chart/templates/rbac.yaml new file mode 100644 index 0000000..c6d06bd --- /dev/null +++ b/chart/templates/rbac.yaml @@ -0,0 +1,69 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "redis-operator-cop.fullname" . }} + labels: + {{- include "redis-operator-cop.labels" . | nindent 4 }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "redis-operator-cop.fullname" . }} + labels: + {{- include "redis-operator-cop.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 +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "redis-operator-cop.fullname" . }} + labels: + {{- include "redis-operator-cop.labels" . | nindent 4 }} +subjects: +- kind: ServiceAccount + namespace: {{ .Release.Namespace }} + name: {{ include "redis-operator-cop.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "redis-operator-cop.fullname" . }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "redis-operator-cop.fullname" . }} + labels: + {{- include "redis-operator-cop.labels" . | nindent 4 }} +subjects: +- kind: ServiceAccount + namespace: {{ .Release.Namespace }} + name: {{ include "redis-operator-cop.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 0000000..9478ddf --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,63 @@ +# -- Override full name +fullnameOverride: "" +# -- Override name +nameOverride: "" + +# -- Replica count +replicaCount: 1 + +image: + # -- Image repository + repository: ghcr.io/sap/redis-operator-cop + # -- Image tag (defauls to .Chart.AppVersion) + tag: "" + # -- Image pull policy + pullPolicy: IfNotPresent + +# -- Image pull secrets +imagePullSecrets: [] + +# -- Additional pod labels +podLabels: {} + +# -- Additional pod annotations +podAnnotations: {} + +# -- Node selector +nodeSelector: {} + +# -- Affinity settings +affinity: {} + +# -- Topology spread constraints (if unspecified, default constraints for hostname and zone will be generated) +topologySpreadConstraints: [] + +# -- Default topology spread policy for hostname +defaultHostNameSpreadPolicy: ScheduleAnyway + +# -- Default topology spread policy for zone +defaultZoneSpreadPolicy: ScheduleAnyway + +# -- Tolerations +tolerations: [] + +# -- Priority class +priorityClassName: "" + +# -- Pod security context +podSecurityContext: {} + +# -- Container security context +securityContext: {} + +resources: + limits: + # -- CPU limit + cpu: 100m + # -- Memory limit + memory: 128Mi + requests: + # -- CPU request + cpu: 100m + # -- Memory request + memory: 128Mi diff --git a/hack/genclient.sh b/hack/genclient.sh new file mode 100755 index 0000000..b0bcbb2 --- /dev/null +++ b/hack/genclient.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +set -eo pipefail + +export GOROOT=$(go env GOROOT) + +BASEDIR=$(realpath $(dirname "$0")/..) +TEMPDIR=$BASEDIR/tmp/gen +trap 'rm -rf "$TEMPDIR"' EXIT +mkdir -p "$TEMPDIR" + +mkdir -p "$TEMPDIR"/apis/operator.kyma-project.io +ln -s "$BASEDIR"/api/v1alpha1 "$TEMPDIR"/apis/operator.kyma-project.io/v1alpha1 + +"$BASEDIR"/bin/client-gen \ + --clientset-name versioned \ + --input-base "" \ + --input github.com/sap/redis-operator-cop/tmp/gen/apis/operator.kyma-project.io/v1alpha1 \ + --go-header-file "$BASEDIR"/hack/boilerplate.go.txt \ + --output-package github.com/sap/redis-operator-cop/pkg/client/clientset \ + --output-base "$TEMPDIR"/pkg/client \ + --plural-exceptions RedisOperator:redisoperators + +"$BASEDIR"/bin/lister-gen \ + --input-dirs github.com/sap/redis-operator-cop/tmp/gen/apis/operator.kyma-project.io/v1alpha1 \ + --go-header-file "$BASEDIR"/hack/boilerplate.go.txt \ + --output-package github.com/sap/redis-operator-cop/pkg/client/listers \ + --output-base "$TEMPDIR"/pkg/client \ + --plural-exceptions RedisOperator:redisoperators + +"$BASEDIR"/bin/informer-gen \ + --input-dirs github.com/sap/redis-operator-cop/tmp/gen/apis/operator.kyma-project.io/v1alpha1 \ + --versioned-clientset-package github.com/sap/redis-operator-cop/pkg/client/clientset/versioned \ + --listers-package github.com/sap/redis-operator-cop/pkg/client/listers \ + --go-header-file "$BASEDIR"/hack/boilerplate.go.txt \ + --output-package github.com/sap/redis-operator-cop/pkg/client/informers \ + --output-base "$TEMPDIR"/pkg/client \ + --plural-exceptions RedisOperator:redisoperators + +find "$TEMPDIR"/pkg/client -name "*.go" -exec \ + perl -pi -e "s#github\.com/sap/redis-operator-cop/tmp/gen/apis/operator\.kyma-project\.io/v1alpha1#github.com/sap/redis-operator-cop/api/v1alpha1#g" \ + {} + + +rm -rf "$BASEDIR"/pkg/client +mv "$TEMPDIR"/pkg/client/github.com/sap/redis-operator-cop/pkg/client "$BASEDIR"/pkg + +cd "$BASEDIR" +go fmt ./pkg/client/... +go vet ./pkg/client/...