diff --git a/.github/actions/install-trustify/action.yml b/.github/actions/install-trustify/action.yml index c07718f..9f6a320 100644 --- a/.github/actions/install-trustify/action.yml +++ b/.github/actions/install-trustify/action.yml @@ -14,6 +14,10 @@ inputs: description: "JSON encoded Trustify Custom Resource (CR) string" required: false default: "" + app-name: + description: "Name of the Trustify instance (should match CR metadata if passed)" + required: false + default: "myapp" runs: using: "composite" steps: @@ -33,12 +37,13 @@ runs: echo "operator-sdk is already installed...yay" exit 0 fi - curl -LO https://github.com/operator-framework/operator-sdk/releases/download/v1.35.0/operator-sdk_linux_amd64 + curl -LO https://github.com/operator-framework/operator-sdk/releases/download/v1.37.0/operator-sdk_linux_amd64 sudo install -o root -g root -m 0755 operator-sdk_linux_amd64 /usr/local/bin/operator-sdk - name: Install Trustify env: OPERATOR_BUNDLE_IMAGE: ${{ inputs.operator-bundle-image }} NAMESPACE: ${{ inputs.namespace }} + APP_NAME: ${{ inputs.app-name }} TRUSTIFY_CR: ${{ inputs.trustify-cr }} run: | make install-trustify diff --git a/.github/workflows/image-build.yaml b/.github/workflows/image-build.yaml index a28c5ad..b281191 100644 --- a/.github/workflows/image-build.yaml +++ b/.github/workflows/image-build.yaml @@ -31,7 +31,7 @@ jobs: registry: "ghcr.io" image_name: "${{ github.repository_owner }}/trustify-operator" containerfile: "./Dockerfile" - architectures: '[ "amd64" ]' + architectures: '[ "amd64", "arm64" ]' secrets: registry_username: ${{ github.actor }} registry_password: ${{ secrets.GITHUB_TOKEN }} @@ -45,7 +45,7 @@ jobs: registry: "ghcr.io" image_name: "${{ github.repository_owner }}/trustify-operator-bundle" containerfile: "./bundle.Dockerfile" - architectures: '[ "amd64" ]' + architectures: '[ "amd64", "arm64" ]' extra-args: '--build-arg QUARKUS_OPTS="-Dquarkus.container-image.image=ghcr.io/${{ github.repository_owner }}/trustify-operator:${{ needs.prepare.outputs.tag }}"' secrets: registry_username: ${{ github.actor }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83e9237..d03fffe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,10 @@ on: description: "The Trustify container image" default: "ghcr.io/trustification/trustd:latest" required: true + image-db: + description: "The Database container image" + default: "quay.io/sclorg/postgresql-15-c9s:latest" + required: true jobs: prepare: @@ -26,9 +30,25 @@ jobs: java-version: 21 distribution: temurin cache: maven + - name: Install YQ + run: | + wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq + chmod +x /usr/bin/yq + - name: Prepare application.yaml + run: | + yq e -P -i '.related.image.server=strenv(IMAGE_SERVER)' src/main/resources/application.yaml + yq e -P -i '.related.image.db=strenv(IMAGE_DB)' src/main/resources/application.yaml + env: + IMAGE_SERVER: ${{ github.event.inputs.image-server }} + IMAGE_DB: ${{ github.event.inputs.image-db }} + - name: Prepare Chart.yaml + run: | + NEW_VERSION=$NEW_VERSION yq e -i '.version=strenv(NEW_VERSION)' helm/Chart.yaml + NEW_VERSION=$NEW_VERSION yq e -i '.appVersion=strenv(NEW_VERSION)' helm/Chart.yaml + env: + NEW_VERSION: ${{ github.event.inputs.version }} - name: Set release version ${{ github.event.inputs.version }} run: | - sed -i "/related\.image\.server=/ s/=.*/=ghcr\.io\/trustification\/trustd:${{ github.event.inputs.image-server }}/" src/main/resources/application.properties mvn -B versions:set versions:commit -DnewVersion=$NEW_VERSION env: NEW_VERSION: ${{ github.event.inputs.version }} @@ -36,7 +56,7 @@ jobs: uses: trustification/release-tools/.github/actions/commit@main with: commit_message: "🏁 Releasing version ${{ github.event.inputs.version }}" - branch: main + branch: ${{github.ref_name}} release: needs: [ prepare ] @@ -67,16 +87,30 @@ jobs: with: name: jreleaser-log path: 'jreleaser-log.tgz' + - name: Install YQ + run: | + wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq + chmod +x /usr/bin/yq + - name: Restore application.yaml + run: | + yq e -P -i '.related.image.server="${RELATED_IMAGE_SERVER:ghcr.io/trustification/trustd:latest}"' src/main/resources/application.yaml + yq e -P -i '.related.image.db="${RELATED_IMAGE_DB:quay.io/sclorg/postgresql-15-c9s:latest}"' src/main/resources/application.yaml + - name: Restore Chart.yaml + run: | + NEXT_VERSION=$NEXT_VERSION yq e -i '.version=strenv(NEXT_VERSION)' helm/Chart.yaml + yq e -i '.appVersion="latest"' helm/Chart.yaml + env: + NEXT_VERSION: ${{ github.event.inputs.next }} - name: Set version ${{ github.event.inputs.next }} run: | - mvn -B versions:set versions:commit -DnewVersion=$NEW_VERSION + mvn -B versions:set versions:commit -DnewVersion=$NEXT_VERSION env: - NEW_VERSION: ${{ github.event.inputs.next }} + NEXT_VERSION: ${{ github.event.inputs.next }} - name: Commit and Push uses: trustification/release-tools/.github/actions/commit@main with: commit_message: "⬆️ Next version ${{ github.event.inputs.next }}" - branch: main + branch: ${{github.ref_name}} publish-bundle: needs: [ release ] diff --git a/README.md b/README.md index e991de7..27ae240 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,31 @@ +# Quick Start - Helm Chart + +If you want to give a quick try of the operator without the whole glory of OLM you can deploy our Operator using Helm. + +- Start minikube: + +```shell +minikube start --addons=ingress,dashboard +``` + +- Install the Helm Chart: + +```shell +helm install myhelm helm/ +``` + +- Create an instance of Trustify: + +```shell +cat << EOF | kubectl apply -f - +apiVersion: "org.trustify/v1alpha1" +kind: "Trustify" +metadata: + name: myapp +spec: { } +EOF +``` + # Local development ## Minikube diff --git a/bundle.Dockerfile b/bundle.Dockerfile index 705bfaf..302982c 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -15,15 +15,23 @@ RUN ./mvnw package -DskipTests ${QUARKUS_OPTS} -Dquarkus.operator-sdk.bundle.cha FROM registry.access.redhat.com/ubi9/ubi:latest AS bundle COPY scripts /scripts COPY --from=build /code/target/bundle/trustify-operator/ /code/target/bundle/trustify-operator/ -RUN dnf install curl zip unzip --allowerasing -y && \ - curl -s "https://get.sdkman.io?rcupdate=false" | bash && \ - source "$HOME/.sdkman/bin/sdkman-init.sh" && \ - sdk install java && \ - sdk install groovy && \ - groovy scripts/enrichCSV.groovy /code/target/bundle/trustify-operator/manifests/trustify-operator.clusterserviceversion.yaml -RUN curl --output /usr/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && \ +RUN dnf install wget --allowerasing -y && \ + wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && \ chmod +x /usr/bin/yq && \ - yq e -P -i '.annotations."com.redhat.openshift.versions"="v4.10"' + # annotations.yaml \ + ANNOTATIONS_FILE=/code/target/bundle/trustify-operator/metadata/annotations.yaml && \ + yq e -P -i '.annotations."com.redhat.openshift.versions"="v4.10"' ${ANNOTATIONS_FILE} && \ + # clusterserviceversion.yaml \ + CSV_FILE=/code/target/bundle/trustify-operator/manifests/trustify-operator.clusterserviceversion.yaml && \ + yq e -P -i '.metadata.annotations.support = "https://github.com/trustification/trustify-operator/issues"' ${CSV_FILE} && \ + yq e -P -i '.metadata.annotations.description = "An Operator for installing and managing Trustify"' ${CSV_FILE} && \ + NOW_DATE=$(date --iso-8601=seconds) yq e -P -i '.metadata.annotations.createdAt = strenv(NOW_DATE)' ${CSV_FILE} && \ + yq e -P -i '.metadata.annotations.containerImage = .spec.install.spec.deployments[0].spec.template.spec.containers[0].image' ${CSV_FILE} && \ + yq e -P -i '.spec.customresourcedefinitions.owned[0].description = "Represents a Trustify instance"' ${CSV_FILE} && \ + yq e -P -i '.spec.customresourcedefinitions.owned[0].displayName = "Trustify"' ${CSV_FILE} && \ + yq e -P -i '.spec.install.spec.clusterPermissions[0].rules[0].apiGroups = ["apiextensions.k8s.io", "config.openshift.io"]' ${CSV_FILE} && \ + yq e -P -i '.spec.install.spec.clusterPermissions[0].rules[0].resources = ["customresourcedefinitions", "ingresses"]' ${CSV_FILE} && \ + yq e -P -i '.spec.install.spec.clusterPermissions[0].rules[0].verbs = ["get", "list"]' ${CSV_FILE} FROM scratch ARG CHANNELS=alpha diff --git a/hack/install-trustify.sh b/hack/install-trustify.sh index 25c317f..44dc286 100755 --- a/hack/install-trustify.sh +++ b/hack/install-trustify.sh @@ -5,6 +5,7 @@ set -x set -o pipefail NAMESPACE="${NAMESPACE:-trustify}" +APP_NAME="${APP_NAME:-myapp}" OPERATOR_BUNDLE_IMAGE="${OPERATOR_BUNDLE_IMAGE:-ghcr.io/trustification/trustify-operator-bundle:latest}" TRUSTIFY_CR="${TRUSTIFY_CR:-}" TIMEOUT="${TIMEOUT:-15m}" @@ -21,7 +22,15 @@ fi run_bundle() { kubectl auth can-i create namespace --all-namespaces - kubectl create namespace ${NAMESPACE} || true + + # delete the ns if it exists, effectively undeploying the current + # Trustify instance. This kinda defeats the purpose of operators, + # obviously, but I'm not familiar enough with the operator-sdk + # command to convince it to reinstall/upgrade the bundle -- run + # bundle-upgrade didn't work :( + kubectl delete namespace ${NAMESPACE} || true + + kubectl create namespace ${NAMESPACE} operator-sdk run bundle "${OPERATOR_BUNDLE_IMAGE}" --namespace "${NAMESPACE}" --timeout "${TIMEOUT}" || (kubectl get Subscription --namespace "${NAMESPACE}" -o yaml && exit 1) # If on MacOS, need to install `brew install coreutils` to get `timeout` @@ -47,40 +56,38 @@ install_trustify() { kind: Trustify apiVersion: org.trustify/v1alpha1 metadata: - name: myapp + name: ${APP_NAME} spec: {} EOF fi # Want to see in github logs what we just created - kubectl get --namespace "${NAMESPACE}" -o yaml trustifies.org.trustify/myapp + kubectl get --namespace "${NAMESPACE}" -o yaml trustifies.org.trustify/${APP_NAME} # Wait for reconcile to finish kubectl wait \ --namespace ${NAMESPACE} \ --for=condition=Successful \ --timeout=600s \ - trustifies.org.trustify/myapp \ + trustifies.org.trustify/${APP_NAME} \ || kubectl get \ --namespace ${NAMESPACE} \ -o yaml \ - trustifies.org.trustify/myapp # Print trustify debug when timed out + trustifies.org.trustify/${APP_NAME} # Print trustify debug when timed out # Now wait for all the trustify deployments kubectl wait \ --namespace ${NAMESPACE} \ - --selector="app.kubernetes.io/part-of=myapp" \ + --selector="app.kubernetes.io/part-of=${APP_NAME}" \ --for=condition=Available \ --timeout=600s \ deployments.apps \ || kubectl get \ --namespace ${NAMESPACE} \ - --selector="app.kubernetes.io/part-of=myapp" \ + --selector="app.kubernetes.io/part-of=${APP_NAME}" \ --field-selector=status.phase!=Running \ -o yaml \ pods # Print not running trustify pods when timed out - - kubectl get deployments.apps -n "${NAMESPACE}" -o yaml } # Available versions of OLM here https://github.com/operator-framework/operator-lifecycle-manager/releases diff --git a/helm/Chart.yaml b/helm/Chart.yaml new file mode 100644 index 0000000..3cb5da1 --- /dev/null +++ b/helm/Chart.yaml @@ -0,0 +1,5 @@ +--- +name: "trustify-operator" +version: "1.0.0-SNAPSHOT" +apiVersion: "v2" +appVersion: "latest" diff --git a/helm/README.md b/helm/README.md new file mode 100644 index 0000000..d48edfe --- /dev/null +++ b/helm/README.md @@ -0,0 +1,10 @@ +## Configuration + +The following values are configurable: + +- `watchNamespaces` - namespaces to be watched, either: + - a list of comma-separated namespace names + - `JOSDK_ALL_NAMESPACES` to watch all namespaces + - `JOSDK_WATCH_CURRENT` to watch only the namespace in which the operator is deployed +- `version` - the current version of the application. + diff --git a/helm/crds/trustifies.org.trustify-v1.yml b/helm/crds/trustifies.org.trustify-v1.yml new file mode 100644 index 0000000..8c37639 --- /dev/null +++ b/helm/crds/trustifies.org.trustify-v1.yml @@ -0,0 +1,203 @@ +# Generated by Fabric8 CRDGenerator, manual edits might get overwritten! +apiVersion: "apiextensions.k8s.io/v1" +kind: "CustomResourceDefinition" +metadata: + name: "trustifies.org.trustify" +spec: + group: "org.trustify" + names: + kind: "Trustify" + plural: "trustifies" + singular: "trustify" + scope: "Namespaced" + versions: + - name: "v1alpha1" + schema: + openAPIV3Schema: + properties: + spec: + properties: + db: + description: "In this section you can find all properties related\ + \ to connect to a database." + properties: + externalDatabase: + description: "Use external database." + type: "boolean" + host: + description: "The host of the database." + type: "string" + name: + description: "The database name." + type: "string" + passwordSecret: + description: "The reference to a secret holding the password of\ + \ the database user." + properties: + key: + type: "string" + name: + type: "string" + optional: + type: "boolean" + type: "object" + port: + description: "The port of the database." + type: "string" + pvcSize: + description: "Size of the PVC to create. Valid only if externalDatabase=false" + type: "string" + resourceLimits: + description: "In this section you can configure resource limits\ + \ settings. Valid only if externalDatabase=false" + properties: + cpuLimit: + description: "Limit CPU." + type: "string" + cpuRequest: + description: "Requested CPU." + type: "string" + memoryLimit: + description: "Limit Memory." + type: "string" + memoryRequest: + description: "Requested memory." + type: "string" + type: "object" + usernameSecret: + description: "The reference to a secret holding the username of\ + \ the database user." + properties: + key: + type: "string" + name: + type: "string" + optional: + type: "boolean" + type: "object" + type: "object" + dbImage: + description: "Custom Trustify DB Server image to be used. For internal\ + \ use only" + type: "string" + hostname: + description: "In this section you can configure hostname and related\ + \ properties." + properties: + hostname: + description: "Hostname for the server." + type: "string" + type: "object" + http: + description: "In this section you can configure features related to\ + \ HTTP and HTTPS" + properties: + tlsSecret: + description: "A secret containing the TLS configuration for HTTPS.\ + \ Reference: https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets." + type: "string" + type: "object" + imagePullPolicy: + description: "Custom Image Pull Policy for images managed by the Operator" + type: "string" + imagePullSecrets: + description: "Secret(s) that might be used when pulling an image from\ + \ a private container image registry or repository." + items: + properties: + name: + type: "string" + type: "object" + type: "array" + oidc: + description: "In this section you can configure Oidc settings." + properties: + enabled: + description: "Enable Oidc Auth." + type: "boolean" + serverClientId: + description: "Oidc client id for the Server." + type: "string" + serverUrl: + description: "Oidc server url." + type: "string" + uiClientId: + description: "Oidc client id for the UI." + type: "string" + type: "object" + serverImage: + description: "Custom Trustify Server image to be used. For internal\ + \ use only" + type: "string" + serverResourceLimits: + description: "In this section you can configure resource limits settings\ + \ for the Server." + properties: + cpuLimit: + description: "Limit CPU." + type: "string" + cpuRequest: + description: "Requested CPU." + type: "string" + memoryLimit: + description: "Limit Memory." + type: "string" + memoryRequest: + description: "Requested memory." + type: "string" + type: "object" + storage: + description: "In this section you can configure Storage settings." + properties: + compression: + description: "Storage compression." + enum: + - "NONE" + - "ZSTD" + type: "string" + filesystem: + properties: + pvcSize: + description: "Size of the PVC to create." + type: "string" + type: "object" + s3: + properties: + accessKey: + description: "Access key." + type: "string" + bucket: + description: "Bucket name." + type: "string" + region: + description: "Region name." + type: "string" + secretKey: + description: "Secret key." + type: "string" + type: "object" + type: + description: "Storage type." + enum: + - "FILESYSTEM" + - "S3" + type: "string" + type: "object" + type: "object" + status: + properties: + conditions: + items: + properties: + status: + type: "string" + type: + type: "string" + type: "object" + type: "array" + type: "object" + type: "object" + served: true + storage: true + subresources: + status: {} diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl new file mode 100644 index 0000000..be02936 --- /dev/null +++ b/helm/templates/_helpers.tpl @@ -0,0 +1,7 @@ +{{- define "app.tag" -}} +{{- if eq .Chart.AppVersion "latest" }} +{{- "latest" }} +{{- else }} +{{- printf "v%s" .Chart.AppVersion }} +{{- end }} +{{- end }} diff --git a/helm/templates/additional-crd-role-binding.yaml b/helm/templates/additional-crd-role-binding.yaml new file mode 100644 index 0000000..e69de29 diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml new file mode 100644 index 0000000..99556df --- /dev/null +++ b/helm/templates/deployment.yaml @@ -0,0 +1,90 @@ +--- +apiVersion: "apps/v1" +kind: "Deployment" +metadata: + annotations: + app.quarkus.io/quarkus-version: "3.15.1" + app.quarkus.io/vcs-uri: "https://github.com/trustification/trustify-operator.git" + prometheus.io/scrape: "true" + prometheus.io/path: "/q/metrics" + prometheus.io/port: "8080" + prometheus.io/scheme: "http" + labels: + app.kubernetes.io/name: "trustify-operator" + app.kubernetes.io/managed-by: "quarkus" + name: "trustify-operator" +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: "trustify-operator" + template: + metadata: + annotations: + app.quarkus.io/quarkus-version: "3.15.1" + app.quarkus.io/vcs-uri: "https://github.com/trustification/trustify-operator.git" + prometheus.io/scrape: "true" + prometheus.io/path: "/q/metrics" + prometheus.io/port: "8080" + prometheus.io/scheme: "http" + labels: + app.kubernetes.io/managed-by: "quarkus" + app.kubernetes.io/name: "trustify-operator" + spec: + containers: + - env: + - name: "KUBERNETES_NAMESPACE" + valueFrom: + fieldRef: + fieldPath: "metadata.namespace" + - name: "RELATED_IMAGE_TRUSTIFY_SERVER" + value: "ghcr.io/trustification/trustd:latest" + - name: "RELATED_IMAGE_DB" + value: "quay.io/sclorg/postgresql-15-c9s:latest" + - name: "QUARKUS_OPERATOR_SDK_CONTROLLERS_TRUSTIFY_NAMESPACES" + value: {{ .Values.watchNamespaces }} + image: "ghcr.io/trustification/trustify-operator:{{ include "app.tag" . }}" + imagePullPolicy: "Always" + livenessProbe: + failureThreshold: 3 + httpGet: + path: "/q/health/live" + port: 8080 + scheme: "HTTP" + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 10 + name: "trustify-operator" + ports: + - containerPort: 8080 + name: "http" + protocol: "TCP" + readinessProbe: + failureThreshold: 3 + httpGet: + path: "/q/health/ready" + port: 8080 + scheme: "HTTP" + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 10 + resources: + limits: + cpu: "250m" + memory: "256Mi" + requests: + cpu: "50m" + memory: "64Mi" + startupProbe: + failureThreshold: 3 + httpGet: + path: "/q/health/started" + port: 8080 + scheme: "HTTP" + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 10 + serviceAccountName: "trustify-operator" diff --git a/helm/templates/generic-crd-cluster-role-binding.yaml b/helm/templates/generic-crd-cluster-role-binding.yaml new file mode 100644 index 0000000..25f17ed --- /dev/null +++ b/helm/templates/generic-crd-cluster-role-binding.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Chart.Name }}-crd-validating-role-binding + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion }} + app.kubernetes.io/managed-by: quarkus +roleRef: + kind: ClusterRole + apiGroup: rbac.authorization.k8s.io + name: {{ .Chart.Name }}-crd-validating-cluster-role +subjects: + - kind: ServiceAccount + name: {{ .Chart.Name }} + namespace: {{ .Release.Namespace }} diff --git a/helm/templates/generic-crd-cluster-role.yaml b/helm/templates/generic-crd-cluster-role.yaml new file mode 100644 index 0000000..d7d6b0e --- /dev/null +++ b/helm/templates/generic-crd-cluster-role.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Chart.Name }}-crd-validating-cluster-role + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion }} + app.kubernetes.io/managed-by: quarkus +rules: + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list diff --git a/helm/templates/service.yaml b/helm/templates/service.yaml new file mode 100644 index 0000000..efedaa9 --- /dev/null +++ b/helm/templates/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion }} + app.kubernetes.io/managed-by: quarkus + name: {{ .Chart.Name }} +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion }} + type: ClusterIP diff --git a/helm/templates/serviceaccount.yaml b/helm/templates/serviceaccount.yaml new file mode 100644 index 0000000..d9181f0 --- /dev/null +++ b/helm/templates/serviceaccount.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/managed-by: quarkus + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion }} + name: {{ .Chart.Name }} diff --git a/helm/templates/trustify-crd-cluster-role.yaml b/helm/templates/trustify-crd-cluster-role.yaml new file mode 100644 index 0000000..fb89f79 --- /dev/null +++ b/helm/templates/trustify-crd-cluster-role.yaml @@ -0,0 +1,80 @@ +--- +apiVersion: "rbac.authorization.k8s.io/v1" +kind: "ClusterRole" +metadata: + name: "trustify-cluster-role" +rules: +- apiGroups: + - "org.trustify" + resources: + - "trustifies" + - "trustifies/status" + - "trustifies/finalizers" + verbs: + - "get" + - "list" + - "watch" + - "patch" + - "update" + - "create" + - "delete" +- apiGroups: + - "" + resources: + - "persistentvolumeclaims" + verbs: + - "create" + - "delete" + - "get" + - "list" + - "patch" + - "update" + - "watch" +- apiGroups: + - "apps" + resources: + - "deployments" + verbs: + - "create" + - "delete" + - "get" + - "list" + - "patch" + - "update" + - "watch" +- apiGroups: + - "" + resources: + - "secrets" + verbs: + - "create" + - "delete" + - "get" + - "list" + - "patch" + - "update" + - "watch" +- apiGroups: + - "" + resources: + - "services" + verbs: + - "create" + - "delete" + - "get" + - "list" + - "patch" + - "update" + - "watch" +- apiGroups: + - "networking.k8s.io" + resources: + - "ingresses" + verbs: + - "create" + - "delete" + - "get" + - "list" + - "patch" + - "update" + - "watch" diff --git a/helm/templates/trustify-crd-role-binding.yaml b/helm/templates/trustify-crd-role-binding.yaml new file mode 100644 index 0000000..16f71b2 --- /dev/null +++ b/helm/templates/trustify-crd-role-binding.yaml @@ -0,0 +1,57 @@ +{{ if eq $.Values.watchNamespaces "JOSDK_WATCH_CURRENT" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: trustify-role-binding + namespace: {{ $.Release.Namespace }} + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion }} + app.kubernetes.io/managed-by: quarkus +roleRef: + kind: ClusterRole + apiGroup: rbac.authorization.k8s.io + name: trustify-cluster-role +subjects: + - kind: ServiceAccount + name: {{ $.Chart.Name }} + namespace: {{ $.Release.Namespace }} +{{ else if eq $.Values.watchNamespaces "JOSDK_ALL_NAMESPACES" }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: trustify-role-binding + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion }} + app.kubernetes.io/managed-by: quarkus +roleRef: + kind: ClusterRole + apiGroup: rbac.authorization.k8s.io + name: trustify-cluster-role +subjects: + - kind: ServiceAccount + name: {{ $.Chart.Name }} + namespace: {{ $.Release.Namespace }} +{{ else }} +{{ range $anamespace := ( split "," $.Values.watchNamespaces ) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: trustify-role-binding + namespace: {{ $anamespace }} + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion }} + app.kubernetes.io/managed-by: quarkus +roleRef: + kind: ClusterRole + apiGroup: rbac.authorization.k8s.io + name: trustify-cluster-role +subjects: + - kind: ServiceAccount + name: {{ $.Chart.Name }} + namespace: {{ $.Release.Namespace }} +--- +{{- end }} +{{- end }} diff --git a/helm/values.schema.json b/helm/values.schema.json new file mode 100644 index 0000000..d7b8b83 --- /dev/null +++ b/helm/values.schema.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "watchNamespaces": { + "type": "string" + } + } +} \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml new file mode 100644 index 0000000..7096e08 --- /dev/null +++ b/helm/values.yaml @@ -0,0 +1,2 @@ +--- +watchNamespaces: "JOSDK_ALL_NAMESPACES" diff --git a/pom.xml b/pom.xml index b6918a3..fcf71a1 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ true 3.2.5 - 6.8.2 + 6.8.4 @@ -45,12 +45,16 @@ io.quarkus - quarkus-resteasy-reactive + quarkus-rest io.quarkus quarkus-smallrye-health + + io.quarkus + quarkus-config-yaml + io.quarkus @@ -86,20 +90,20 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.17.2 + 2.17.3 test org.testcontainers k3s - 1.19.7 + 1.20.3 test org.awaitility awaitility - 4.2.1 + 4.2.2 test diff --git a/scripts/enrichCSV.groovy b/scripts/enrichCSV.groovy deleted file mode 100644 index 7707ba3..0000000 --- a/scripts/enrichCSV.groovy +++ /dev/null @@ -1,32 +0,0 @@ -@Grab('org.yaml:snakeyaml:1.33') -import org.yaml.snakeyaml.* -import groovy.yaml.* -import java.util.Map -import java.time.LocalDate - -def file = new File(this.args[0]) - -def fileReader = new FileReader(file) -def yaml = new Yaml().load(fileReader) - -yaml.metadata.annotations.support = "https://github.com/trustification/trustify-operator/issues" -yaml.metadata.annotations.description = "An Operator for installing and managing Trustify" -yaml.metadata.annotations.createdAt = LocalDate.now().toString() -yaml.metadata.annotations.containerImage = yaml.spec.install.spec.deployments[0].spec.template.spec.containers[0].image - -yaml.spec.customresourcedefinitions.owned[0].description = "Represents a Trustify instance" -yaml.spec.customresourcedefinitions.owned[0].displayName = "Trustify" - -// Adding cluster permissions to be able to fetch host domain -yaml.spec.install.spec.clusterPermissions.rules[0][1] = [:] -yaml.spec.install.spec.clusterPermissions.rules[0][1].apiGroups = ['config.openshift.io'] -yaml.spec.install.spec.clusterPermissions.rules[0][1].resources = ['ingresses'] -yaml.spec.install.spec.clusterPermissions.rules[0][1].verbs = ['get', 'list'] - -DumperOptions options = new DumperOptions(); -options.indent = 2 -options.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK -options.defaultScalarStyle = DumperOptions.ScalarStyle.PLAIN -options.prettyFlow = true - -new Yaml(options).dump(yaml, new FileWriter(file)) diff --git a/scripts/examples/storage-filesystem.yaml b/scripts/examples/storage-filesystem.yaml new file mode 100644 index 0000000..29a1e6f --- /dev/null +++ b/scripts/examples/storage-filesystem.yaml @@ -0,0 +1,9 @@ +apiVersion: "org.trustify/v1alpha1" +kind: "Trustify" +metadata: + name: myapp +spec: + storage: + type: FILESYSTEM + filesystem: + pvcSize: 5Gi diff --git a/scripts/examples/storage-s3.yaml b/scripts/examples/storage-s3.yaml new file mode 100644 index 0000000..60429f5 --- /dev/null +++ b/scripts/examples/storage-s3.yaml @@ -0,0 +1,12 @@ +apiVersion: "org.trustify/v1alpha1" +kind: "Trustify" +metadata: + name: myapp +spec: + storage: + type: S3 + s3: + region: myregion + bucket: mybucket + secretKey: mysecretkey + accessKey: myaccesskey diff --git a/src/main/java/org/trustify/operator/Constants.java b/src/main/java/org/trustify/operator/Constants.java index 6a971a6..b868b5b 100644 --- a/src/main/java/org/trustify/operator/Constants.java +++ b/src/main/java/org/trustify/operator/Constants.java @@ -44,11 +44,12 @@ public class Constants { public static final String OIDC_DB_DEPLOYMENT_SUFFIX = "-" + OIDC_DB_NAME + "-deployment"; public static final String OIDC_DB_SERVICE_SUFFIX = "-" + OIDC_DB_NAME + "-service"; + public static final String SERVER_PVC_SUFFIX = "-" + TRUSTI_SERVER_NAME + "-pvc"; public static final String SERVER_DEPLOYMENT_SUFFIX = "-" + TRUSTI_SERVER_NAME + "-deployment"; public static final String SERVER_SERVICE_SUFFIX = "-" + TRUSTI_SERVER_NAME + "-service"; - public static final String INGRESS_SUFFIX = "-" + TRUSTI_SERVER_NAME + "-ingress"; + public static final String INGRESS_SUFFIX = "-" + TRUSTI_NAME + "-ingress"; // public static final String DB_SECRET_USERNAME = "username"; @@ -56,7 +57,7 @@ public class Constants { public static final String DB_NAME = "database"; public static final Integer DB_PORT= 5432; - public static final String POSTGRESQL_PVC_SIZE = "10G"; + public static final String DEFAULT_PVC_SIZE = "10G"; public static final String CERTIFICATES_FOLDER = "/mnt/certificates"; public static final String WORKSPACES_FOLDER = "/mnt/workspace"; diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/TrustifySpec.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/TrustifySpec.java index a4cbb6c..770fb86 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/TrustifySpec.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/TrustifySpec.java @@ -36,6 +36,10 @@ public record TrustifySpec( @JsonPropertyDescription("In this section you can configure Oidc settings.") OidcSpec oidcSpec, + @JsonProperty("storage") + @JsonPropertyDescription("In this section you can configure Storage settings.") + StorageSpec storageSpec, + @JsonProperty("serverResourceLimits") @JsonPropertyDescription("In this section you can configure resource limits settings for the Server.") ResourcesLimitSpec serverResourceLimitSpec @@ -51,6 +55,7 @@ public TrustifySpec() { null, null, null, + null, null ); } @@ -96,11 +101,13 @@ public record HttpSpec( public record OidcSpec( @JsonPropertyDescription("Enable Oidc Auth.") - String enabled, + boolean enabled, @JsonPropertyDescription("Oidc server url.") String serverUrl, - @JsonPropertyDescription("Oidc client id.") - String clientId, + @JsonPropertyDescription("Oidc client id for the UI.") + String uiClientId, + @JsonPropertyDescription("Oidc client id for the Server.") + String serverClientId, @JsonProperty("db") @JsonPropertyDescription("In this section you can find all properties related to connect to a database.") @@ -108,6 +115,64 @@ public record OidcSpec( ) { } + public enum StorageStrategyType { + FILESYSTEM("fs"), + S3("s3"); + private final String value; + + StorageStrategyType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public enum StorageCompressionType { + NONE("none"), + ZSTD("zstd"); + private final String value; + + StorageCompressionType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public record StorageSpec( + @JsonPropertyDescription("Storage compression.") + StorageCompressionType compression, + @JsonPropertyDescription("Storage type.") + StorageStrategyType type, + @JsonProperty("filesystem") + FilesystemStorageSpec filesystemStorageSpec, + @JsonProperty("s3") + S3StorageSpec s3StorageSpec + ) { + } + + public record FilesystemStorageSpec( + @JsonPropertyDescription("Size of the PVC to create.") + String pvcSize + ) { + } + + public record S3StorageSpec( + @JsonPropertyDescription("Region name.") + String region, + @JsonPropertyDescription("Bucket name.") + String bucket, + @JsonPropertyDescription("Access key.") + String accessKey, + @JsonPropertyDescription("Secret key.") + String secretKey + ) { + } + public record ResourcesLimitSpec( @JsonPropertyDescription("Requested CPU.") String cpuRequest, diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBDeploymentDiscriminator.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBDeploymentDiscriminator.java index 9268fb3..8f78d84 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBDeploymentDiscriminator.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBDeploymentDiscriminator.java @@ -6,6 +6,7 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.controllers.TrustifyReconciler; import java.util.Optional; @@ -14,7 +15,7 @@ public class DBDeploymentDiscriminator implements ResourceDiscriminator distinguish(Class resource, Trustify cr, Context context) { String deploymentName = DBDeployment.getDeploymentName(cr); ResourceID resourceID = new ResourceID(deploymentName, cr.getMetadata().getNamespace()); - var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Deployment.class, "db-deployment"); + var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Deployment.class, TrustifyReconciler.DEPLOYMENT_EVENT_SOURCE); return informerEventSource.get(resourceID); } } diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBPersistentVolumeClaim.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBPersistentVolumeClaim.java index f1bcaa1..934aa2d 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBPersistentVolumeClaim.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBPersistentVolumeClaim.java @@ -36,7 +36,7 @@ private PersistentVolumeClaim newPersistentVolumeClaim(Trustify cr, Context distinguish(Class resource, Trustify cr, Context context) { String persistentVolumeClaimName = DBPersistentVolumeClaim.getPersistentVolumeClaimName(cr); ResourceID resourceID = new ResourceID(persistentVolumeClaimName, cr.getMetadata().getNamespace()); - var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(PersistentVolumeClaim.class); + var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(PersistentVolumeClaim.class, TrustifyReconciler.PVC_EVENT_SOURCE); return informerEventSource.get(resourceID); } } diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBServiceDiscriminator.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBServiceDiscriminator.java index 8eeff53..ec1048d 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBServiceDiscriminator.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/db/DBServiceDiscriminator.java @@ -6,6 +6,7 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.controllers.TrustifyReconciler; import java.util.Optional; @@ -14,7 +15,7 @@ public class DBServiceDiscriminator implements ResourceDiscriminator distinguish(Class resource, Trustify cr, Context context) { String serviceName = DBService.getServiceName(cr); ResourceID resourceID = new ResourceID(serviceName, cr.getMetadata().getNamespace()); - var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Service.class, "db-service"); + var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Service.class, TrustifyReconciler.SERVICE_EVENT_SOURCE); return informerEventSource.get(resourceID); } } diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerDeployment.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerDeployment.java index df02aba..085d9eb 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerDeployment.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerDeployment.java @@ -130,6 +130,18 @@ private DeploymentSpec getDeploymentSpec(Trustify cr, Context context, .withTerminationGracePeriodSeconds(70L) .withImagePullSecrets(cr.getSpec().imagePullSecrets()) // .withServiceAccountName(Constants.TRUSTI_NAME) + .withInitContainers(new ContainerBuilder() + .withName("migrate") + .withImage(image) + .withImagePullPolicy(imagePullPolicy) + .withEnv(envVars) + .withCommand( + "/usr/local/bin/trustd", + "db", + "migrate" + ) + .build() + ) .withContainers(new ContainerBuilder() .withName(Constants.TRUSTI_SERVER_NAME) .withImage(image) @@ -138,7 +150,7 @@ private DeploymentSpec getDeploymentSpec(Trustify cr, Context context, .withCommand( "/usr/local/bin/trustd", "api", - "--devmode", + "--sample-data", "--infrastructure-enabled", "--infrastructure-bind=0.0.0.0:" + Constants.HTTP_INFRAESTRUCTURE_PORT ) diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaim.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaim.java index f6f5975..5cced6f 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaim.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaim.java @@ -8,9 +8,11 @@ import jakarta.enterprise.context.ApplicationScoped; import org.trustify.operator.Constants; import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.cdrs.v2alpha1.TrustifySpec; import org.trustify.operator.utils.CRDUtils; import java.util.Map; +import java.util.Optional; @KubernetesDependent(labelSelector = ServerStoragePersistentVolumeClaim.LABEL_SELECTOR, resourceDiscriminator = ServerStoragePersistentVolumeClaimDiscriminator.class) @ApplicationScoped @@ -33,7 +35,10 @@ private PersistentVolumeClaim newPersistentVolumeClaim(Trustify cr, Context) context.managedDependentResourceContext() .getMandatory(Constants.CONTEXT_LABELS_KEY, Map.class); - String pvcStorageSize = "10Gi"; + String pvcStorageSize = Optional.ofNullable(cr.getSpec().storageSpec()) + .flatMap(storageSpec -> Optional.ofNullable(storageSpec.filesystemStorageSpec())) + .map(TrustifySpec.FilesystemStorageSpec::pvcSize) + .orElse(Constants.DEFAULT_PVC_SIZE); return new PersistentVolumeClaimBuilder() .withNewMetadata() @@ -65,7 +70,7 @@ public Result match(PersistentVolumeClaim actual, Trustif } public static String getPersistentVolumeClaimName(Trustify cr) { - return cr.getMetadata().getName() + Constants.DB_PVC_SUFFIX; + return cr.getMetadata().getName() + Constants.SERVER_PVC_SUFFIX; } } diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaimActivationCondition.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaimActivationCondition.java new file mode 100644 index 0000000..d079722 --- /dev/null +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaimActivationCondition.java @@ -0,0 +1,24 @@ +package org.trustify.operator.cdrs.v2alpha1.server; + +import io.fabric8.kubernetes.api.model.PersistentVolumeClaim; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; +import jakarta.enterprise.context.ApplicationScoped; +import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.cdrs.v2alpha1.TrustifySpec; + +import java.util.Objects; +import java.util.Optional; + +@ApplicationScoped +public class ServerStoragePersistentVolumeClaimActivationCondition implements Condition { + + @Override + public boolean isMet(DependentResource resource, Trustify cr, Context context) { + return Optional.ofNullable(cr.getSpec().storageSpec()) + .map(storageSpec -> Objects.isNull(storageSpec.type()) || Objects.equals(TrustifySpec.StorageStrategyType.FILESYSTEM, storageSpec.type())) + .orElse(true); + } + +} diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaimDiscriminator.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaimDiscriminator.java index d10225f..a277503 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaimDiscriminator.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerStoragePersistentVolumeClaimDiscriminator.java @@ -6,6 +6,7 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.controllers.TrustifyReconciler; import java.util.Optional; @@ -14,7 +15,7 @@ public class ServerStoragePersistentVolumeClaimDiscriminator implements Resource public Optional distinguish(Class resource, Trustify cr, Context context) { String persistentVolumeClaimName = ServerStoragePersistentVolumeClaim.getPersistentVolumeClaimName(cr); ResourceID resourceID = new ResourceID(persistentVolumeClaimName, cr.getMetadata().getNamespace()); - var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(PersistentVolumeClaim.class); + var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(PersistentVolumeClaim.class, TrustifyReconciler.PVC_EVENT_SOURCE); return informerEventSource.get(resourceID); } } diff --git a/src/main/java/org/trustify/operator/controllers/TrustifyDistConfigurator.java b/src/main/java/org/trustify/operator/controllers/TrustifyDistConfigurator.java index 2e86267..ee14ea8 100644 --- a/src/main/java/org/trustify/operator/controllers/TrustifyDistConfigurator.java +++ b/src/main/java/org/trustify/operator/controllers/TrustifyDistConfigurator.java @@ -122,33 +122,79 @@ private void configureDatabase() { } private void configureStorage() { - List envVars = optionMapper(cr.getSpec()) - .mapOption("TRUSTD_STORAGE_FS_PATH", spec -> "/opt/trustify/storage") - .getEnvVars(); + List envVars = new ArrayList<>(); - var volume = new VolumeBuilder() - .withName("trustify-pvol") - .withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSourceBuilder() - .withClaimName(ServerStoragePersistentVolumeClaim.getPersistentVolumeClaimName(cr)) - .build() - ) - .build(); + TrustifySpec.StorageSpec storageSpec = Optional.ofNullable(cr.getSpec().storageSpec()) + .orElse(new TrustifySpec.StorageSpec(null, null, null, null)); - var volumeMount = new VolumeMountBuilder() - .withName(volume.getName()) - .withMountPath("/opt/trustify") - .build(); + // Storage type + TrustifySpec.StorageStrategyType storageStrategyType = Objects.nonNull(storageSpec.type()) ? storageSpec.type() : TrustifySpec.StorageStrategyType.FILESYSTEM; + envVars.add(new EnvVarBuilder() + .withName("TRUSTD_STORAGE_STRATEGY") + .withValue(storageStrategyType.getValue()) + .build() + ); - allVolumes.add(volume); - allVolumeMounts.add(volumeMount); + // Other config + envVars.addAll(optionMapper(storageSpec) + .mapOption("TRUSTD_STORAGE_COMPRESSION", spec -> Objects.nonNull(spec.compression()) ? spec.compression().getValue() : null) + .getEnvVars() + ); + + switch (storageStrategyType) { + case FILESYSTEM -> { + envVars.add(new EnvVarBuilder() + .withName("TRUSTD_STORAGE_FS_PATH") + .withValue("/opt/trustify/storage") + .build() + ); + + var volume = new VolumeBuilder() + .withName("trustify-pvol") + .withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSourceBuilder() + .withClaimName(ServerStoragePersistentVolumeClaim.getPersistentVolumeClaimName(cr)) + .build() + ) + .build(); + + var volumeMount = new VolumeMountBuilder() + .withName(volume.getName()) + .withMountPath("/opt/trustify") + .build(); + + allVolumes.add(volume); + allVolumeMounts.add(volumeMount); + } + case S3 -> { + envVars.addAll(optionMapper(storageSpec.s3StorageSpec()) + .mapOption("TRUSTD_S3_BUCKET", TrustifySpec.S3StorageSpec::bucket) + .mapOption("TRUSTD_S3_REGION", TrustifySpec.S3StorageSpec::region) + .mapOption("TRUSTD_S3_ACCESS_KEY", TrustifySpec.S3StorageSpec::accessKey) + .mapOption("TRUSTD_S3_SECRET_KEY", TrustifySpec.S3StorageSpec::secretKey) + .getEnvVars() + ); + } + } allEnvVars.addAll(envVars); } private void configureOidc() { - List envVars = optionMapper(cr.getSpec()) - .mapOption("AUTH_DISABLED", spec -> true) - .getEnvVars(); + List envVars = Optional.ofNullable(cr.getSpec().oidcSpec()) + .map(oidcSpec -> optionMapper(oidcSpec) + .mapOption("AUTH_DISABLED", spec -> !spec.enabled()) + .mapOption("AUTHENTICATOR_OIDC_ISSUER_URL", TrustifySpec.OidcSpec::serverUrl) + .mapOption("AUTHENTICATOR_OIDC_CLIENT_IDS", TrustifySpec.OidcSpec::serverClientId) + .mapOption("UI_ISSUER_URL", TrustifySpec.OidcSpec::serverUrl) + .mapOption("UI_CLIENT_ID", TrustifySpec.OidcSpec::uiClientId) + .getEnvVars() + ) + .orElseGet(() -> List.of(new EnvVarBuilder() + .withName("AUTH_DISABLED") + .withValue(Boolean.TRUE.toString()) + .build()) + ); + allEnvVars.addAll(envVars); } diff --git a/src/main/java/org/trustify/operator/controllers/TrustifyReconciler.java b/src/main/java/org/trustify/operator/controllers/TrustifyReconciler.java index bf38b08..fe0a036 100644 --- a/src/main/java/org/trustify/operator/controllers/TrustifyReconciler.java +++ b/src/main/java/org/trustify/operator/controllers/TrustifyReconciler.java @@ -12,10 +12,9 @@ import org.trustify.operator.cdrs.v2alpha1.Trustify; import org.trustify.operator.cdrs.v2alpha1.TrustifyStatusCondition; import org.trustify.operator.cdrs.v2alpha1.db.*; -import org.trustify.operator.cdrs.v2alpha1.keycloak.*; -import org.trustify.operator.cdrs.v2alpha1.server.ServerDeployment; -import org.trustify.operator.cdrs.v2alpha1.server.ServerIngress; -import org.trustify.operator.cdrs.v2alpha1.server.ServerService; +import org.trustify.operator.cdrs.v2alpha1.keycloak.KeycloakDBPersistentVolumeClaim; +import org.trustify.operator.cdrs.v2alpha1.keycloak.KeycloakDBPersistentVolumeClaimActivationCondition; +import org.trustify.operator.cdrs.v2alpha1.server.*; import java.time.Duration; import java.util.Map; @@ -33,78 +32,76 @@ activationCondition = KeycloakDBPersistentVolumeClaimActivationCondition.class, useEventSourceWithName = TrustifyReconciler.PVC_EVENT_SOURCE ), -// @Dependent( -// name = "oidc-db-secret", -// type = KeycloakDBSecret.class, -// activationCondition = KeycloakDBSecretActivationCondition.class -// ), -// @Dependent( -// name = "oidc-db-deployment", -// type = KeycloakDBDeployment.class, -// dependsOn = {"db-pvc", "db-secret"}, -// readyPostcondition = KeycloakDBDeployment.class, -// activationCondition = KeycloakDBDeploymentActivationCondition.class -// ), -// @Dependent( -// name = "oidc-db-service", -// type = KeycloakDBService.class, -// dependsOn = {"oidc-db-deployment"}, -// activationCondition = KeycloakDBServiceActivationCondition.class -// ), - - // Trustify + @Dependent( + name = "oidc-db-secret", + type = KeycloakDBSecret.class, + activationCondition = KeycloakDBSecretActivationCondition.class + ), + @Dependent( + name = "oidc-db-deployment", + type = KeycloakDBDeployment.class, + dependsOn = {"db-pvc", "db-secret"}, + readyPostcondition = KeycloakDBDeployment.class, + activationCondition = KeycloakDBDeploymentActivationCondition.class + ), + @Dependent( + name = "oidc-db-service", + type = KeycloakDBService.class, + dependsOn = {"oidc-db-deployment"}, + activationCondition = KeycloakDBServiceActivationCondition.class + ), + @Dependent( name = "db-pvc", type = DBPersistentVolumeClaim.class, - activationCondition = DBPersistentVolumeClaimActivationCondition.class, - useEventSourceWithName = TrustifyReconciler.PVC_EVENT_SOURCE + activationCondition = DBPersistentVolumeClaimActivationCondition.class ), -// @Dependent( -// name = "db-secret", -// type = DBSecret.class, -// activationCondition = DBSecretActivationCondition.class -// ), -// @Dependent( -// name = "db-deployment", -// type = DBDeployment.class, -// dependsOn = {"db-pvc", "db-secret"}, -// readyPostcondition = DBDeployment.class, -// activationCondition = DBDeploymentActivationCondition.class -// ), -// @Dependent( -// name = "db-service", -// type = DBService.class, -// dependsOn = {"db-deployment"}, -// activationCondition = DBServiceActivationCondition.class -// ), -// -// @Dependent( -// name = "server-deployment", -// type = ServerDeployment.class, -//// dependsOn = {"db-service"}, -// readyPostcondition = ServerDeployment.class, -// useEventSourceWithName = "server-deployment" -// ), -// @Dependent( -// name = "server-service", -// type = ServerService.class, -// dependsOn = {"server-deployment"}, -// useEventSourceWithName = "server-service" -// ), -// -// @Dependent( -// name = "ingress", -// type = ServerIngress.class, -// dependsOn = {"server-service"}, -// readyPostcondition = ServerIngress.class -// ) + @Dependent( + name = "db-secret", + type = DBSecret.class, + activationCondition = DBSecretActivationCondition.class + ), + @Dependent( + name = "db-deployment", + type = DBDeployment.class, + dependsOn = {"db-pvc", "db-secret"}, + readyPostcondition = DBDeployment.class, + activationCondition = DBDeploymentActivationCondition.class + ), + @Dependent( + name = "db-service", + type = DBService.class, + dependsOn = {"db-deployment"}, + activationCondition = DBServiceActivationCondition.class + ), + + @Dependent( + name = "server-deployment", + type = ServerDeployment.class, +// dependsOn = {"db-service"}, + readyPostcondition = ServerDeployment.class, + useEventSourceWithName = "server-deployment" + ), + @Dependent( + name = "server-service", + type = ServerService.class, + dependsOn = {"server-deployment"}, + useEventSourceWithName = "server-service" + ), + + @Dependent( + name = "ingress", + type = ServerIngress.class, + dependsOn = {"server-service"}, + readyPostcondition = ServerIngress.class + ) } ) public class TrustifyReconciler implements Reconciler, ContextInitializer, EventSourceInitializer { private static final Logger logger = Logger.getLogger(TrustifyReconciler.class); - public static final String PVC_EVENT_SOURCE = "pvcSource"; + public static final String PVC_EVENT_SOURCE = "pcvSource"; public static final String DEPLOYMENT_EVENT_SOURCE = "deploymentSource"; public static final String SERVICE_EVENT_SOURCE = "serviceSource"; @@ -152,16 +149,16 @@ public UpdateControl reconcile(Trustify cr, Context context) { @Override public Map prepareEventSources(EventSourceContext context) { - var pvcInformerConfiguration = InformerConfiguration.from(PersistentVolumeClaim.class, context).build(); + var pcvInformerConfiguration = InformerConfiguration.from(PersistentVolumeClaim.class, context).build(); var deploymentInformerConfiguration = InformerConfiguration.from(Deployment.class, context).build(); var serviceInformerConfiguration = InformerConfiguration.from(Service.class, context).build(); - var pvcInformerEventSource = new InformerEventSource<>(pvcInformerConfiguration, context); + var pcvInformerEventSource = new InformerEventSource<>(pcvInformerConfiguration, context); var deploymentInformerEventSource = new InformerEventSource<>(deploymentInformerConfiguration, context); var serviceInformerEventSource = new InformerEventSource<>(serviceInformerConfiguration, context); return Map.of( - PVC_EVENT_SOURCE, pvcInformerEventSource, + PVC_EVENT_SOURCE, pcvInformerEventSource, DEPLOYMENT_EVENT_SOURCE, deploymentInformerEventSource, SERVICE_EVENT_SOURCE, serviceInformerEventSource ); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index ee358db..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,35 +0,0 @@ -quarkus.container-image.registry=ghcr.io -quarkus.container-image.tag=v${quarkus.application.version} - -# set to true to automatically apply CRDs to the cluster when they get regenerated -quarkus.operator-sdk.crd.apply=true - -# Operator config -related.image.server=${RELATED_IMAGE_SERVER:ghcr.io/trustification/trustd:latest} -related.image.db=${RELATED_IMAGE_DB:quay.io/sclorg/postgresql-15-c9s:latest} -related.image.pull-policy=Always - -# https://quarkus.io/guides/deploying-to-kubernetes#environment-variables-from-keyvalue-pairs -quarkus.kubernetes.env.vars.related-image-trustify-server=${related.image.server} -quarkus.kubernetes.env.vars.related-image-db=${related.image.db} -quarkus.kubernetes.env.vars.related-image-importer=${related.image.importer} -quarkus.openshift.env.vars.related-image-trustify-server=${related.image.server} -quarkus.openshift.env.vars.related-image-db=${related.image.db} -quarkus.openshift.env.vars.related-image-importer=${related.image.importer} - -quarkus.kubernetes.resources.requests.memory=64Mi -quarkus.kubernetes.resources.requests.cpu=50m -quarkus.kubernetes.resources.limits.memory=256Mi -quarkus.kubernetes.resources.limits.cpu=250m - -quarkus.kubernetes.rbac.roles.trustify.policy-rules.0.api-groups= -quarkus.kubernetes.rbac.roles.trustify.policy-rules.0.resources=* -quarkus.kubernetes.rbac.roles.trustify.policy-rules.0.verbs=* - -quarkus.kubernetes.rbac.roles.trustify.policy-rules.1.api-groups=security.openshift.io -quarkus.kubernetes.rbac.roles.trustify.policy-rules.1.resources=securitycontextconstraints -quarkus.kubernetes.rbac.roles.trustify.policy-rules.1.verbs=use -quarkus.kubernetes.rbac.roles.trustify.policy-rules.1.resource-names=anyuid - -# Increase test hang time since the operator takes time to initiate all deployments -quarkus.test.hang-detection-timeout=30M \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..77f33ec --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,50 @@ +# Operator config +related: + image: + server: ${RELATED_IMAGE_SERVER:ghcr.io/trustification/trustd:latest} + db: ${RELATED_IMAGE_DB:quay.io/sclorg/postgresql-15-c9s:latest} + pull-policy: Always +# Quarkus config +quarkus: + container-image: + registry: ghcr.io + tag: v${quarkus.application.version} + # Increase test hang time since the operator takes time to initiate all deployments + test: + hang-detection-timeout: 30M + operator-sdk: + crd: + # set to true to automatically apply CRDs to the cluster when they get regenerated + apply: true + namespaces: JOSDK_WATCH_CURRENT + generate-with-watched-namespaces: JOSDK_WATCH_CURRENT + helm: + enabled: true + # https://quarkus.io/guides/deploying-to-kubernetes#environment-variables-from-keyvalue-pairs + kubernetes: + idempotent: "true" + prometheus: + generate-service-monitor: "false" + env: + vars: + related-image-trustify-server: ${related.image.server} + related-image-db: ${related.image.db} + related-image-importer: ${related.image.importer} + resources: + requests: + memory: 64Mi + cpu: 50m + limits: + memory: 256Mi + cpu: 250m + rbac: + roles: + trustify: + policy-rules: + - api-groups: + resources: '*' + verbs: '*' + - api-groups: security.openshift.io + resources: securitycontextconstraints + verbs: use + resource-names: anyuid diff --git a/src/test/java/org/trustify/operator/controllers/TrustifyReconcilerTest.java b/src/test/java/org/trustify/operator/controllers/TrustifyReconcilerTest.java index c6ba426..11cd94d 100644 --- a/src/test/java/org/trustify/operator/controllers/TrustifyReconcilerTest.java +++ b/src/test/java/org/trustify/operator/controllers/TrustifyReconcilerTest.java @@ -12,12 +12,12 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.*; import org.trustify.operator.Constants; -import org.trustify.operator.cdrs.v2alpha1.*; +import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.cdrs.v2alpha1.db.DBDeployment; +import org.trustify.operator.cdrs.v2alpha1.db.DBService; import org.trustify.operator.cdrs.v2alpha1.server.ServerDeployment; import org.trustify.operator.cdrs.v2alpha1.server.ServerIngress; import org.trustify.operator.cdrs.v2alpha1.server.ServerService; -import org.trustify.operator.cdrs.v2alpha1.db.DBDeployment; -import org.trustify.operator.cdrs.v2alpha1.db.DBService; import org.trustify.operator.controllers.setup.K3sResource; import java.util.List;