diff --git a/Makefile b/Makefile index baeb80a63..fe8849fa6 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,10 @@ TEST_COHERENCE_IMAGE ?= $(COHERENCE_IMAGE) TEST_COHERENCE_VERSION ?= $(COHERENCE_VERSION) TEST_COHERENCE_GID ?= com.oracle.coherence.ce +# The minimum certified OpenShift version the Operator runs on +OPENSHIFT_MIN_VERSION := v4.15 +OPENSHIFT_COMPONENT_PID := 67b738ef88736e8a179ac976 + # The current working directory CURRDIR := $(shell pwd) @@ -340,13 +344,15 @@ SHELL = /usr/bin/env bash -o pipefail # Capture the Git commit to add to the build information that is then embedded in the Go binary # ---------------------------------------------------------------------------------------------------------------------- GITCOMMIT ?= $(shell git rev-list -1 HEAD) +GITBRANCH ?= $(shell git branch --show-current) GITREPO := https://github.com/oracle/coherence-operator.git SOURCE_DATE_EPOCH := $(shell git show -s --format=format:%ct HEAD) DATE_FMT := "%Y-%m-%dT%H:%M:%SZ" -BUILD_DATE := $(shell date -u -d "@$SOURCE_DATE_EPOCH" "+${DATE_FMT}" 2>/dev/null || date -u -r "${SOURCE_DATE_EPOCH}" "+${DATE_FMT}" 2>/dev/null || date -u "+${DATE_FMT}") +#BUILD_DATE := $(shell date -u -d "@$SOURCE_DATE_EPOCH" "+${DATE_FMT}" 2>/dev/null || date -u -r "${SOURCE_DATE_EPOCH}" "+${DATE_FMT}" 2>/dev/null || date -u "+${DATE_FMT}") +BUILD_DATE := $(shell date -u "+${DATE_FMT}") BUILD_USER := $(shell whoami) -LDFLAGS = -X main.Version=$(VERSION) -X main.Commit=$(GITCOMMIT) -X main.Date=$(BUILD_DATE) -X main.Author=$(BUILD_USER) +LDFLAGS = -X main.Version=$(VERSION) -X main.Commit=$(GITCOMMIT) -X main.Branch=$(GITBRANCH) -X main.Date=$(BUILD_DATE) -X main.Author=$(BUILD_USER) GOS = $(shell find . -type f -name "*.go" ! -name "*_test.go") HELM_FILES = $(shell find helm-charts/coherence-operator -type f) API_GO_FILES = $(shell find . -type f -name "*.go" ! -name "*_test.go" ! -name "zz*.go") @@ -460,7 +466,9 @@ $(BUILD_PROPS): clean: ## Cleans the build -rm -rf $(BUILD_OUTPUT) || true -rm -rf $(BUILD_BIN) || true + -rm -rf artifacts || true -rm -rf bundle || true + -rm bundle.Dockerfile || true rm config/crd/bases/*.yaml || true rm -rf config/crd-small || true rm pkg/data/zz_generated_*.go || true @@ -895,6 +903,13 @@ bundle: $(BUILD_PROPS) ensure-sdk $(TOOLS_BIN)/kustomize $(BUILD_TARGETS)/manife $(OPERATOR_SDK) generate kustomize manifests cd config/manager && $(KUSTOMIZE) edit set image controller=$(OPERATOR_IMAGE) $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) + @echo "" >> ./bundle/metadata/annotations.yaml + @echo " # OpenShift annotations" >> ./bundle/metadata/annotations.yaml + @echo " com.redhat.openshift.versions: $(OPENSHIFT_MIN_VERSION)" >> ./bundle/metadata/annotations.yaml + @echo "" >> bundle.Dockerfile + @echo "# OpenShift labels" >> bundle.Dockerfile + @echo "LABEL com.redhat.openshift.versions=$(OPENSHIFT_MIN_VERSION)" >> bundle.Dockerfile + @echo "cert_project_id: $(OPENSHIFT_COMPONENT_PID)" > bundle/ci.yaml $(OPERATOR_SDK) bundle validate ./bundle $(OPERATOR_SDK) bundle validate ./bundle --select-optional suite=operatorframework --optional-values=k8s-version=1.26 $(OPERATOR_SDK) bundle validate ./bundle --select-optional name=operatorhubv2 --optional-values=k8s-version=1.26 @@ -952,7 +967,7 @@ catalog-build: opm ## Build a catalog image. # Push the catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. - $(DOCKER_CMD) push $(CATALOG_IMAGE) + $(DOCKER_CMD) push $(PUSH_ARGS) $(CATALOG_IMAGE) .PHONY: scorecard scorecard: $(BUILD_PROPS) ensure-sdk bundle ## Run the Operator SDK scorecard tests. @@ -1037,9 +1052,6 @@ jk: $(BUILD_PREFLIGHT)/preflight.yaml # Generate the preflight job yaml $(BUILD_PREFLIGHT)/preflight.yaml: hack/preflight.yaml -#ifeq ($(PREFLIGHT_REGISTRY_CRED),) -# $(error $(n)The PREFLIGHT_REGISTRY_CRED variable must be specified to run preflight$(n)Typically using the command$(n)$(n) export PREFLIGHT_REGISTRY_CRED=$$(echo -n bogus:$$(oc whoami -t) | base64)$(n)$(n)) -#endif cp hack/preflight.yaml $(BUILD_PREFLIGHT)/preflight.yaml $(SED) -e 's^image-placeholder^$(OPERATOR_IMAGE)^g' $(BUILD_PREFLIGHT)/preflight.yaml $(SED) -e 's/registry-credential-placeholder/$(PREFLIGHT_REGISTRY_CRED)/g' $(BUILD_PREFLIGHT)/preflight.yaml @@ -2299,6 +2311,7 @@ PUSH_ARGS ?= push-operator-image: $(BUILD_TARGETS)/build-operator $(DOCKER_CMD) push $(PUSH_ARGS) $(OPERATOR_IMAGE_AMD) $(DOCKER_CMD) push $(PUSH_ARGS) $(OPERATOR_IMAGE_ARM) + $(DOCKER_CMD) rmi $(OPERATOR_IMAGE) || true $(DOCKER_CMD) manifest create $(PUSH_ARGS) $(OPERATOR_IMAGE) \ --amend $(OPERATOR_IMAGE_AMD) \ --amend $(OPERATOR_IMAGE_ARM) @@ -2326,27 +2339,27 @@ push-test-images: .PHONY: push-ttl-test-images push-ttl-test-images: $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE) $(TTL_APPLICATION_IMAGE) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_CLIENT) $(TTL_APPLICATION_IMAGE_CLIENT) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_CLIENT) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_CLIENT) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_HELIDON) $(TTL_APPLICATION_IMAGE_HELIDON) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_HELIDON) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_HELIDON) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_HELIDON_3) $(TTL_APPLICATION_IMAGE_HELIDON_3) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_HELIDON_3) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_HELIDON_3) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_HELIDON_2) $(TTL_APPLICATION_IMAGE_HELIDON_2) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_HELIDON_2) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_HELIDON_2) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_SPRING) $(TTL_APPLICATION_IMAGE_SPRING) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_SPRING) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_SPRING) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_SPRING_FAT) $(TTL_APPLICATION_IMAGE_SPRING_FAT) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_SPRING_FAT) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_SPRING_FAT) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_SPRING_CNBP) $(TTL_APPLICATION_IMAGE_SPRING_CNBP) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_SPRING_CNBP) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_SPRING_CNBP) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_SPRING_2) $(TTL_APPLICATION_IMAGE_SPRING_2) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_SPRING_2) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_SPRING_2) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_SPRING_FAT_2) $(TTL_APPLICATION_IMAGE_SPRING_FAT_2) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_SPRING_FAT_2) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_SPRING_FAT_2) $(DOCKER_CMD) tag $(TEST_APPLICATION_IMAGE_SPRING_CNBP_2) $(TTL_APPLICATION_IMAGE_SPRING_CNBP_2) - $(DOCKER_CMD) push $(TTL_APPLICATION_IMAGE_SPRING_CNBP_2) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_APPLICATION_IMAGE_SPRING_CNBP_2) # ---------------------------------------------------------------------------------------------------------------------- # Build the Operator Test images @@ -2365,7 +2378,7 @@ build-compatibility-image: $(BUILD_TARGETS)/java # ---------------------------------------------------------------------------------------------------------------------- .PHONY: push-compatibility-image push-compatibility-image: build-compatibility-image - $(DOCKER_CMD) push $(TEST_COMPATIBILITY_IMAGE) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TEST_COMPATIBILITY_IMAGE) # ---------------------------------------------------------------------------------------------------------------------- # Push the Operator JIB Test Docker images to ttl.sh @@ -2373,7 +2386,7 @@ push-compatibility-image: build-compatibility-image .PHONY: push-ttl-compatibility-image push-ttl-compatibility-image: $(DOCKER_CMD) tag $(TEST_COMPATIBILITY_IMAGE) $(TTL_COMPATIBILITY_IMAGE) - $(DOCKER_CMD) push $(TTL_COMPATIBILITY_IMAGE) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_COMPATIBILITY_IMAGE) # ---------------------------------------------------------------------------------------------------------------------- # Push all of the Docker images @@ -2387,7 +2400,7 @@ push-all-images: push-test-images push-operator-image .PHONY: push-ttl-operator-images push-ttl-operator-images: $(DOCKER_CMD) tag $(OPERATOR_IMAGE) $(TTL_OPERATOR_IMAGE) - $(DOCKER_CMD) push $(TTL_OPERATOR_IMAGE) + $(DOCKER_CMD) push $(PUSH_ARGS) $(TTL_OPERATOR_IMAGE) # ---------------------------------------------------------------------------------------------------------------------- # Push all the images to ttl.sh diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 2620e0880..0e97ee782 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -28,6 +28,8 @@ spec: version: "3.4.3" app.kubernetes.io/component: manager app.kubernetes.io/part-of: coherence-operator + annotations: + alpha.image.policy.openshift.io/resolve-names: '*' spec: serviceAccountName: service-account terminationGracePeriodSeconds: 10 diff --git a/config/manifests/bases/coherence-operator.clusterserviceversion.yaml b/config/manifests/bases/coherence-operator.clusterserviceversion.yaml index c7d1eb25e..a5ed7bcfe 100644 --- a/config/manifests/bases/coherence-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/coherence-operator.clusterserviceversion.yaml @@ -11,25 +11,36 @@ metadata: containerImage: ghcr.io/oracle/coherence-operator:3.4.3 description: The Oracle Coherence Kubernetes Operator enables easy management of Coherence clusters in a Kubernetes environment. + features.operators.openshift.io/cnf: "false" + features.operators.openshift.io/cni: "false" + features.operators.openshift.io/csi: "false" + features.operators.openshift.io/disconnected: "true" + features.operators.openshift.io/fips-compliant: "true" + features.operators.openshift.io/proxy-aware: "false" + features.operators.openshift.io/tls-profiles: "false" + features.operators.openshift.io/token-auth-aws: "false" + features.operators.openshift.io/token-auth-azure: "false" + features.operators.openshift.io/token-auth-gcp: "false" + operators.openshift.io/infrastructure-features: '["disconnected"]' operators.operatorframework.io/builder: operator-sdk-v1.39.1 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 repository: https://github.com/oracle/coherence-operator - support: "" + support: Oracle Corporation name: coherence-operator.v3.4.2 spec: apiservicedefinitions: {} customresourcedefinitions: owned: - - description: Coherence is the Schema for the Coherence Cluster API. - displayName: Coherence - kind: Coherence - name: coherence.coherence.oracle.com - version: v1 - - description: CoherenceJob is the Schema for the Coherence Job API. - displayName: CoherenceJob - kind: CoherenceJob - name: coherencejob.coherence.oracle.com - version: v1 + - description: Coherence is the Schema for the Coherence Cluster API. + displayName: Coherence + kind: Coherence + name: coherence.coherence.oracle.com + version: v1 + - description: CoherenceJob is the Schema for the Coherence Job API. + displayName: CoherenceJob + kind: CoherenceJob + name: coherencejob.coherence.oracle.com + version: v1 description: | The Oracle Coherence Kubernetes Operator enables easy management of Coherence clusters in a Kubernetes environment. @@ -39,28 +50,28 @@ spec: [Coherence Operator Documentation](https://oracle.github.io/coherence-operator/docs/latest/#/about/01_overview). displayName: Coherence Kubernetes Operator icon: - - base64data: iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAsTAAALEwEAmpwYAAAIK2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICAgICAgICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjYwMDwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+NjU1MzU8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjExMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgICAgIDx4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+eG1wLmRpZDpkNDMwYzI2Yi05YTNhLTQzN2ItYWZlMy02NWRjYjgyZTY1NTc8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkhpc3Rvcnk+CiAgICAgICAgICAgIDxyZGY6U2VxPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMjAtMDUtMjlUMDc6NTI6MjUtMDQ6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6ZDQzMGMyNmItOWEzYS00MzdiLWFmZTMtNjVkY2I4MmU2NTU3PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmNyZWF0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOmQ0MzBjMjZiLTlhM2EtNDM3Yi1hZmUzLTY1ZGNiODJlNjU1NzwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpkNDMwYzI2Yi05YTNhLTQzN2ItYWZlMy02NWRjYjgyZTY1NTc8L3ht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 - mediatype: image/png + - base64data: iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAAsTAAALEwEAmpwYAAAIK2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICAgICAgICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjYwMDwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+NjU1MzU8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjExMjwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgICAgIDx4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+eG1wLmRpZDpkNDMwYzI2Yi05YTNhLTQzN2ItYWZlMy02NWRjYjgyZTY1NTc8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkhpc3Rvcnk+CiAgICAgICAgICAgIDxyZGY6U2VxPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMjAtMDUtMjlUMDc6NTI6MjUtMDQ6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6ZDQzMGMyNmItOWEzYS00MzdiLWFmZTMtNjVkY2I4MmU2NTU3PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPmNyZWF0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8eG1wTU06SW5zdGFuY2VJRD54bXAuaWlkOmQ0MzBjMjZiLTlhM2EtNDM3Yi1hZmUzLTY1ZGNiODJlNjU1NzwveG1wTU06SW5zdGFuY2VJRD4KICAgICAgICAgPHhtcE1NOkRvY3VtZW50SUQ+eG1wLmRpZDpkNDMwYzI2Yi05YTNhLTQzN2ItYWZlMy02NWRjYjgyZTY1NTc8L3ht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 + mediatype: image/png install: spec: deployments: null strategy: "" installModes: - - supported: true - type: OwnNamespace - - supported: true - type: SingleNamespace - - supported: true - type: MultiNamespace - - supported: true - type: AllNamespaces + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: true + type: MultiNamespace + - supported: true + type: AllNamespaces keywords: - - coherence + - coherence links: - - name: Coherence Kubernetes Operator - url: https://oracle.github.io/coherence-operator/docs/latest - - name: Oracle Coherence - url: https://coherence.community + - name: Coherence Kubernetes Operator + url: https://oracle.github.io/coherence-operator/docs/latest + - name: Oracle Coherence + url: https://coherence.community maturity: alpha minKubeVersion: 1.26.0 provider: diff --git a/hack/setup-podman.sh b/hack/setup-podman.sh index 4ab8ad680..d37cca460 100644 --- a/hack/setup-podman.sh +++ b/hack/setup-podman.sh @@ -18,4 +18,9 @@ export OPERATOR_IMAGE_REGISTRY=${REGISTRY}/oracle export PREFLIGHT_REGISTRY_CRED=$(echo -n bogus:$(oc whoami -t) | base64) podman login -u bogus -p $(oc whoami -t) --tls-verify=false ${REGISTRY} +podman login -u bogus -p $(oc whoami -t) --tls-verify=false ${OPERATOR_IMAGE_REGISTRY} +#oc new-project oracle || true +#oc -n oracle create is coherence-operator || true +# Allow anyone to pull oracle images +#oc adm policy add-role-to-group system:image-puller system:authenticated --namespace=oracle diff --git a/java/coherence-operator/src/main/java/com/oracle/coherence/k8s/CoherenceOperatorLifecycleListener.java b/java/coherence-operator/src/main/java/com/oracle/coherence/k8s/CoherenceOperatorLifecycleListener.java index cfa27832c..7049d7090 100644 --- a/java/coherence-operator/src/main/java/com/oracle/coherence/k8s/CoherenceOperatorLifecycleListener.java +++ b/java/coherence-operator/src/main/java/com/oracle/coherence/k8s/CoherenceOperatorLifecycleListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. + * Copyright (c) 2021, 2025, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -157,7 +157,7 @@ public void preStart(Context context) { context.getConfigurableCacheFactory().getInterceptorRegistry().registerEventInterceptor(this); } catch (Throwable t) { - LOGGER.error("Failed to initialise the Coherence Operator", t); + LOGGER.error("CoherenceOperator: Failed to initialise the Coherence Operator", t); } } @@ -281,59 +281,126 @@ void initCohCtl() { String provider = Config.getProperty("coherence.management.http.provider"); String defaultProtocol = provider == null || provider.isEmpty() ? "http" : "https"; String protocol = Config.getProperty("coherence.operator.cli.protocol", defaultProtocol); - String home = System.getProperty("user.home"); - File fileHome = new File(home); String connectionType = "http"; - if (!fileHome.exists() || !fileHome.isDirectory()) { - LOGGER.info("CoherenceOperator: user home \"" + home - + "\" does not exist, creating default cohctl config at /coherence-operator/utils"); - home = "/coherence-operator/utils"; - fileHome = new File(home); + + // If COHCTL_HOME is set use that for the config location + String cohCtlHome = System.getenv("COHCTL_HOME"); + if (cohCtlHome == null || !cohCtlHome.isEmpty()) { + // Try corresponding system property + cohCtlHome = Config.getProperty("cohctl.home"); } - if (!fileHome.exists() || !fileHome.isDirectory()) { - LOGGER.error("CoherenceOperator: Cannot create cohctl config, directory " + home + " does not exist"); + // If we have a COHCTL_HOME env var or property try to create the config there + if (cohCtlHome != null && !cohCtlHome.isEmpty() + && tryCreateConfig(new File(cohCtlHome), connectionType, protocol, port, clusterName)) { return; } - File cohctlHome = new File(home + File.separator + ".cohctl"); - File configFile = new File(cohctlHome, "cohctl.yaml"); + // Either COHCTL_HOME was not set or we failed to create a config there, try ${user.home} + String userHome = System.getProperty("user.home"); + File fileUserHome = new File(userHome); + File fileCohCtlHome = null; + + if (fileUserHome.exists() && fileUserHome.isDirectory()) { + // use ${user.home}/.cohctl + fileCohCtlHome = new File(fileUserHome, ".cohctl"); + if (!fileCohCtlHome.exists()) { + try { + if (!fileCohCtlHome.mkdirs()) { + fileCohCtlHome = null; + } + } + catch (Exception e) { + LOGGER.error("CoherenceOperator: Failed to create cohctl home directory at " + fileCohCtlHome, e); + fileCohCtlHome = null; + } + } + } - if (!configFile.exists()) { - LOGGER.info("CoherenceOperator: creating default cohctl config at " + configFile.getAbsolutePath()); - if (!cohctlHome.exists()) { - cohctlHome.mkdirs(); + boolean success = false; + if (fileCohCtlHome != null) { + if (fileCohCtlHome.exists() && fileCohCtlHome.isDirectory()) { + success = tryCreateConfig(fileCohCtlHome, connectionType, protocol, port, clusterName); + } + else { + if (!fileCohCtlHome.exists()) { + LOGGER.error("CoherenceOperator: Cannot create cohctl config, directory " + + fileCohCtlHome + " does not exist"); + } + else { + LOGGER.error("CoherenceOperator: Cannot create cohctl config, location " + + fileCohCtlHome + " is not a directory"); + } } - try (PrintWriter out = new PrintWriter(configFile)) { - out.println("clusters:"); - out.println(" - name: default"); - out.println(" discoverytype: manual"); - out.println(" connectiontype: " + connectionType); - out.println(" connectionurl: " + protocol + "://127.0.0.1:" + port + "/management/coherence/cluster"); - out.println(" nameservicediscovery: \"\""); - out.println(" clusterversion: \"" + CacheFactory.VERSION + "\""); - out.println(" clustername: \"" + clusterName + "\""); - out.println(" clustertype: Standalone"); - out.println(" manuallycreated: false"); - out.println(" baseclasspath: \"\""); - out.println(" additionalclasspath: \"\""); - out.println(" arguments: \"\""); - out.println(" managementport: 0"); - out.println(" persistencemode: \"\""); - out.println(" loggingdestination: \"\""); - out.println(" managementavailable: false"); - out.println("color: \"on\""); - out.println("currentcontext: default"); - out.println("debug: false"); - out.println("defaultbytesformat: m"); - out.println("ignoreinvalidcerts: false"); - out.println("requesttimeout: 30"); + } + + if (!success) { + if (fileCohCtlHome != null) { + LOGGER.info("CoherenceOperator: unable to create cohctl config in \"" + fileCohCtlHome + + "\" creating cohctl config at /coherence-operator/utils"); } + tryCreateConfig(new File("/coherence-operator/utils"), connectionType, protocol, port, clusterName); + } + } + catch (Exception e) { + LOGGER.error("Coherence Operator: Failed to create default cohctl config. " + e.getMessage()); + } + } + + /** + * Try to create the Coherence CLI configuration. + * + * @param home the location of the CLI home directory + * @param connectionType the type of the connection http or https + * @param protocol the protocol for the connection http or https + * @param port the management over REST port + * @param clusterName the cluster name + * + * @return {@code true} of the configuration was created + */ + protected boolean tryCreateConfig(File home, String connectionType, String protocol, String port, String clusterName) { + File configFile = new File(home, "cohctl.yaml"); + + if (configFile.exists()) { + return true; + } + + try { + LOGGER.info("CoherenceOperator: creating default cohctl config at " + configFile.getAbsolutePath()); + if (!home.exists()) { + home.mkdirs(); + } + try (PrintWriter out = new PrintWriter(configFile)) { + out.println("clusters:"); + out.println(" - name: default"); + out.println(" discoverytype: manual"); + out.println(" connectiontype: " + connectionType); + out.println(" connectionurl: " + protocol + "://127.0.0.1:" + port + "/management/coherence/cluster"); + out.println(" nameservicediscovery: \"\""); + out.println(" clusterversion: \"" + CacheFactory.VERSION + "\""); + out.println(" clustername: \"" + clusterName + "\""); + out.println(" clustertype: Standalone"); + out.println(" manuallycreated: false"); + out.println(" baseclasspath: \"\""); + out.println(" additionalclasspath: \"\""); + out.println(" arguments: \"\""); + out.println(" managementport: 0"); + out.println(" persistencemode: \"\""); + out.println(" loggingdestination: \"\""); + out.println(" managementavailable: false"); + out.println("color: \"on\""); + out.println("currentcontext: default"); + out.println("debug: false"); + out.println("defaultbytesformat: m"); + out.println("ignoreinvalidcerts: false"); + out.println("requesttimeout: 30"); } + return true; } catch (Exception e) { LOGGER.error("Coherence Operator: Failed to create default cohctl config. " + e.getMessage()); + return false; } } } diff --git a/java/operator-compatibility/image-build.sh b/java/operator-compatibility/image-build.sh index 87b1616b0..ba641d5e2 100644 --- a/java/operator-compatibility/image-build.sh +++ b/java/operator-compatibility/image-build.sh @@ -1,3 +1,3 @@ #!/bin/sh -e -docker build -t jk/coherence:1.0.0 -f operator-compatibility/target/classes/Dockerfile ./operator-compatibility \ No newline at end of file +docker build --load -t jk/coherence:1.0.0 -f operator-compatibility/target/classes/Dockerfile ./operator-compatibility \ No newline at end of file diff --git a/java/operator-compatibility/pom.xml b/java/operator-compatibility/pom.xml index dceb2a092..e79184668 100644 --- a/java/operator-compatibility/pom.xml +++ b/java/operator-compatibility/pom.xml @@ -60,6 +60,7 @@ docker build + --load -t ${coherence.compatibility.image.name} -f diff --git a/java/operator-test-helidon-2/pom.xml b/java/operator-test-helidon-2/pom.xml index 34cd98a61..a6d0a188e 100644 --- a/java/operator-test-helidon-2/pom.xml +++ b/java/operator-test-helidon-2/pom.xml @@ -55,6 +55,10 @@ coherence ${coherence.version} + + ${coherence.groupId} + coherence-json + ${coherence.groupId} coherence-cdi-server diff --git a/java/operator-test-helidon-3/pom.xml b/java/operator-test-helidon-3/pom.xml index 5a95864fc..ea15ae4ea 100644 --- a/java/operator-test-helidon-3/pom.xml +++ b/java/operator-test-helidon-3/pom.xml @@ -54,6 +54,10 @@ ${coherence.groupId} coherence + + ${coherence.groupId} + coherence-json + ${coherence.groupId} coherence-cdi-server diff --git a/java/operator-test-helidon/pom.xml b/java/operator-test-helidon/pom.xml index eece76f42..b48e9e435 100644 --- a/java/operator-test-helidon/pom.xml +++ b/java/operator-test-helidon/pom.xml @@ -54,6 +54,10 @@ ${coherence.groupId} coherence + + ${coherence.groupId} + coherence-json + ${coherence.groupId} coherence-cdi-server diff --git a/java/operator-test-spring-2/pom.xml b/java/operator-test-spring-2/pom.xml index 962eb96a5..1cf551e4c 100644 --- a/java/operator-test-spring-2/pom.xml +++ b/java/operator-test-spring-2/pom.xml @@ -47,6 +47,10 @@ ${coherence.groupId} coherence + + ${coherence.groupId} + coherence-json + org.springframework.boot spring-boot-starter-web diff --git a/java/operator-test-spring/pom.xml b/java/operator-test-spring/pom.xml index cae5357ec..01b25a566 100644 --- a/java/operator-test-spring/pom.xml +++ b/java/operator-test-spring/pom.xml @@ -46,6 +46,10 @@ ${coherence.groupId} coherence + + ${coherence.groupId} + coherence-json + org.springframework.boot spring-boot-starter-web diff --git a/java/operator-test/pom.xml b/java/operator-test/pom.xml index 5314652c2..f35187cab 100644 --- a/java/operator-test/pom.xml +++ b/java/operator-test/pom.xml @@ -41,6 +41,10 @@ ${coherence.groupId} coherence + + ${coherence.groupId} + coherence-json + ${coherence.groupId} coherence-grpc-proxy diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index a20de6d09..3b693074b 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -77,6 +77,8 @@ const ( FlagWebhookCertDir = "webhook-cert-dir" FlagWebhookSecret = "webhook-secret" FlagWebhookService = "webhook-service" + FlagEnvVar = "env" + FlagJvmArg = "jvm" // EnvVarWatchNamespace is the environment variable to use to set the watch namespace(s) EnvVarWatchNamespace = "WATCH_NAMESPACE" @@ -476,6 +478,14 @@ func GetGlobalLabels(v *viper.Viper) (map[string]string, error) { return stringSliceToMap(args, FlagGlobalLabel) } +func GetExtraEnvVars() []string { + return GetViper().GetStringSlice(FlagEnvVar) +} + +func GetExtraJvmArgs() []string { + return GetViper().GetStringSlice(FlagJvmArg) +} + func stringSliceToMap(args []string, flag string) (map[string]string, error) { var m map[string]string if args != nil { diff --git a/pkg/runner/cmd_console.go b/pkg/runner/cmd_console.go index 1ae7827bc..390e6be28 100644 --- a/pkg/runner/cmd_console.go +++ b/pkg/runner/cmd_console.go @@ -20,16 +20,20 @@ const ( // consoleCommand creates the cobra sub-command to run a Coherence CacheFactory console. func consoleCommand(v *viper.Viper) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: CommandConsole, Short: "Start a Coherence interactive console", Long: "Starts a Coherence interactive console", RunE: func(cmd *cobra.Command, args []string) error { - return run(cmd, func(details *RunDetails, _ *cobra.Command) { + return run(cmd, func(details *RunDetails, cmd *cobra.Command) { console(details, args, v) }) }, } + addEnvVarFlag(cmd) + addJvmArgFlag(cmd) + setupFlags(cmd, v) + return cmd } // Configure the runner to run a Coherence CacheFactory console @@ -45,10 +49,9 @@ func console(details *RunDetails, args []string, v *viper.Viper) { } details.Command = CommandConsole details.addArg("-Dcoherence.distributed.localstorage=false") + details.addArg("-Dcoherence.localport.adjust=true") + details.addArg("-Dcoherence.management.http.enabled=false") + details.addArg("-Dcoherence.metrics.http.enabled=false") details.setenv(v1.EnvVarCohRole, "console") - details.unsetenv(v1.EnvVarJvmMemoryHeap) - details.unsetenv(v1.EnvVarCoherenceLocalPortAdjust) - details.unsetenv(v1.EnvVarCohMgmtPrefix + v1.EnvVarCohEnabledSuffix) - details.unsetenv(v1.EnvVarCohMetricsPrefix + v1.EnvVarCohEnabledSuffix) details.MainArgs = args } diff --git a/pkg/runner/cmd_jshell.go b/pkg/runner/cmd_jshell.go index 9c32b5560..83f6e5483 100644 --- a/pkg/runner/cmd_jshell.go +++ b/pkg/runner/cmd_jshell.go @@ -9,6 +9,7 @@ package runner import ( v1 "github.com/oracle/coherence-operator/api/v1" "github.com/spf13/cobra" + "github.com/spf13/viper" "os" ) @@ -18,8 +19,8 @@ const ( ) // queryPlusCommand creates the corba "jshell" sub-command -func jShellCommand() *cobra.Command { - return &cobra.Command{ +func jShellCommand(v *viper.Viper) *cobra.Command { + cmd := &cobra.Command{ Use: CommandJShell, Short: "Start a Coherence interactive JShell console", Long: "Starts a Coherence interactive JShell console", @@ -28,6 +29,10 @@ func jShellCommand() *cobra.Command { return run(cmd, jShell) }, } + addEnvVarFlag(cmd) + addJvmArgFlag(cmd) + setupFlags(cmd, v) + return cmd } // Configure the runner to run a Coherence JShell console @@ -39,9 +44,8 @@ func jShell(details *RunDetails, _ *cobra.Command) { details.MainArgs = os.Args[2:] } details.addArg("-Dcoherence.distributed.localstorage=false") + details.addArg("-Dcoherence.localport.adjust=true") + details.addArg("-Dcoherence.management.http.enabled=false") + details.addArg("-Dcoherence.metrics.http.enabled=false") details.setenv(v1.EnvVarCohRole, "jshell") - details.unsetenv(v1.EnvVarJvmMemoryHeap) - details.unsetenv(v1.EnvVarCoherenceLocalPortAdjust) - details.unsetenv(v1.EnvVarCohMgmtPrefix + v1.EnvVarCohEnabledSuffix) - details.unsetenv(v1.EnvVarCohMetricsPrefix + v1.EnvVarCohEnabledSuffix) } diff --git a/pkg/runner/cmd_query_plus.go b/pkg/runner/cmd_query_plus.go index 2efa8d6b4..e989326e1 100644 --- a/pkg/runner/cmd_query_plus.go +++ b/pkg/runner/cmd_query_plus.go @@ -9,6 +9,7 @@ package runner import ( v1 "github.com/oracle/coherence-operator/api/v1" "github.com/spf13/cobra" + "github.com/spf13/viper" "os" ) @@ -18,8 +19,8 @@ const ( ) // queryPlusCommand creates the corba "queryplus" sub-command -func queryPlusCommand() *cobra.Command { - return &cobra.Command{ +func queryPlusCommand(v *viper.Viper) *cobra.Command { + cmd := &cobra.Command{ Use: CommandQueryPlus, Short: "Start a Coherence interactive QueryPlus console", Long: "Starts a Coherence interactive QueryPlus console", @@ -28,6 +29,10 @@ func queryPlusCommand() *cobra.Command { return run(cmd, queryPlus) }, } + addEnvVarFlag(cmd) + addJvmArgFlag(cmd) + setupFlags(cmd, v) + return cmd } // Configure the runner to run a Coherence Query Plus console @@ -39,9 +44,8 @@ func queryPlus(details *RunDetails, _ *cobra.Command) { details.MainArgs = os.Args[2:] } details.addArg("-Dcoherence.distributed.localstorage=false") + details.addArg("-Dcoherence.localport.adjust=true") + details.addArg("-Dcoherence.management.http.enabled=false") + details.addArg("-Dcoherence.metrics.http.enabled=false") details.setenv(v1.EnvVarCohRole, "queryPlus") - details.unsetenv(v1.EnvVarJvmMemoryHeap) - details.unsetenv(v1.EnvVarCoherenceLocalPortAdjust) - details.unsetenv(v1.EnvVarCohMgmtPrefix + v1.EnvVarCohEnabledSuffix) - details.unsetenv(v1.EnvVarCohMetricsPrefix + v1.EnvVarCohEnabledSuffix) } diff --git a/pkg/runner/cmd_sleep.go b/pkg/runner/cmd_sleep.go index b5ced347e..32f6ca259 100644 --- a/pkg/runner/cmd_sleep.go +++ b/pkg/runner/cmd_sleep.go @@ -9,6 +9,7 @@ package runner import ( v1 "github.com/oracle/coherence-operator/api/v1" "github.com/spf13/cobra" + "github.com/spf13/viper" ) const ( @@ -17,8 +18,8 @@ const ( ) // queryPlusCommand creates the corba "sleep" sub-command -func sleepCommand() *cobra.Command { - return &cobra.Command{ +func sleepCommand(v *viper.Viper) *cobra.Command { + cmd := &cobra.Command{ Use: CommandSleep, Short: "Sleep for a number of seconds", Long: "Sleep for a number of seconds", @@ -29,6 +30,10 @@ func sleepCommand() *cobra.Command { }) }, } + addEnvVarFlag(cmd) + addJvmArgFlag(cmd) + setupFlags(cmd, v) + return cmd } func sleep(details *RunDetails, args []string) { @@ -38,6 +43,8 @@ func sleep(details *RunDetails, args []string) { details.MainArgs = args details.UseOperatorHealth = true details.addArg("-Dcoherence.distributed.localstorage=false") + details.addArg("-Dcoherence.localport.adjust=true") + details.addArg("-Dcoherence.management.http.enabled=false") + details.addArg("-Dcoherence.metrics.http.enabled=false") details.setenv(v1.EnvVarCohRole, "sleep") - details.unsetenv(v1.EnvVarJvmMemoryHeap) } diff --git a/pkg/runner/run_details.go b/pkg/runner/run_details.go index 7ed640725..97c14b44d 100644 --- a/pkg/runner/run_details.go +++ b/pkg/runner/run_details.go @@ -53,6 +53,7 @@ type RunDetails struct { MainClass string MainArgs []string BuildPacks *bool + ExtraEnv []string env *viper.Viper } @@ -198,13 +199,6 @@ func (in *RunDetails) setenv(key, value string) { in.env.Set(key, value) } -func (in *RunDetails) unsetenv(key string) { - if in.env == nil { - in.env = viper.New() - } - in.env.Set(key, nil) -} - func (in *RunDetails) isEnvTrue(name string) bool { value := in.Getenv(name) return strings.ToLower(value) == "true" diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 0a8da4101..24947be1e 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -125,14 +125,14 @@ func NewRootCommand(env map[string]string, v *viper.Viper) *cobra.Command { rootCmd.AddCommand(initCommand(env)) rootCmd.AddCommand(serverCommand()) rootCmd.AddCommand(consoleCommand(v)) - rootCmd.AddCommand(queryPlusCommand()) + rootCmd.AddCommand(queryPlusCommand(v)) rootCmd.AddCommand(statusCommand()) rootCmd.AddCommand(readyCommand()) rootCmd.AddCommand(nodeCommand()) rootCmd.AddCommand(operatorCommand(v)) rootCmd.AddCommand(networkTestCommand()) - rootCmd.AddCommand(jShellCommand()) - rootCmd.AddCommand(sleepCommand()) + rootCmd.AddCommand(jShellCommand(v)) + rootCmd.AddCommand(sleepCommand(v)) return rootCmd } @@ -600,6 +600,11 @@ func createCommand(details *RunDetails) (string, *exec.Cmd, error) { details.addArgs(strings.Split(jvmArgs, " ")...) } + extraJvmArgs := operator.GetExtraJvmArgs() + if extraJvmArgs != nil { + details.addArgs(extraJvmArgs...) + } + var cmd *exec.Cmd var app string switch { @@ -623,6 +628,11 @@ func createCommand(details *RunDetails) (string, *exec.Cmd, error) { cmd, err = createGraalCommand(details) } + extraEnv := operator.GetExtraEnvVars() + if cmd != nil && extraEnv != nil { + cmd.Env = append(cmd.Env, extraEnv...) + } + return app, cmd, err } @@ -1131,3 +1141,31 @@ func closeFile(f *os.File, log logr.Logger) { log.Error(err, "error closing file "+f.Name()) } } + +func addEnvVarFlag(cmd *cobra.Command) { + cmd.PersistentFlags().StringSlice( + operator.FlagEnvVar, + nil, + "Additional environment variables to pass to the process", + ) +} + +func addJvmArgFlag(cmd *cobra.Command) { + cmd.PersistentFlags().StringSlice( + operator.FlagJvmArg, + nil, + "AdditionalJVM args to pass to the process", + ) +} + +func setupFlags(cmd *cobra.Command, v *viper.Viper) { + // enable using dashed notation in flags and underscores in env + v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + + if err := v.BindPFlags(cmd.Flags()); err != nil { + setupLog.Error(err, "binding flags") + os.Exit(1) + } + + v.AutomaticEnv() +} diff --git a/pkg/runner/runner_spring_test.go b/pkg/runner/runner_spring_test.go index 58832eb63..e03c0f6c3 100644 --- a/pkg/runner/runner_spring_test.go +++ b/pkg/runner/runner_spring_test.go @@ -161,6 +161,8 @@ func TestSpringBootFatJarConsole(t *testing.T) { expectedCommand := GetJavaCommand() expectedArgs := GetMinimalExpectedSpringBootFatJarArgsForRole(jar, ConsoleMain, "") + expectedArgs = append(expectedArgs, "-Dcoherence.localport.adjust=true", + "-Dcoherence.management.http.enabled=false", "-Dcoherence.metrics.http.enabled=false") e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) @@ -192,7 +194,10 @@ func TestSpringBootFatJarConsoleWithArgs(t *testing.T) { env := EnvVarsFromDeployment(d) expectedCommand := GetJavaCommand() - expectedArgs := append(GetMinimalExpectedSpringBootFatJarArgsForRole(jar, ConsoleMain, ""), "foo", "bar") + expectedArgs := GetMinimalExpectedSpringBootFatJarArgsForRole(jar, ConsoleMain, "") + expectedArgs = append(expectedArgs, "-Dcoherence.localport.adjust=true", + "-Dcoherence.management.http.enabled=false", "-Dcoherence.metrics.http.enabled=false", + "foo", "bar") e, err := ExecuteWithArgsAndNewViper(env, args) g.Expect(err).NotTo(HaveOccurred()) diff --git a/runner/main.go b/runner/main.go index 9581045f4..46dee2fde 100644 --- a/runner/main.go +++ b/runner/main.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -24,6 +24,8 @@ var ( Version string // Commit is the runner Git commit injected by the Go linker at build time. Commit string + // Branch is the runner Git branch injected by the Go linker at build time. + Branch string // Date is the runner build date injected by the Go linker at build time. Date string // Author is the username of the account at build time @@ -46,7 +48,7 @@ func printVersion() { log.Info(fmt.Sprintf("Operator Version: %s", Version)) log.Info(fmt.Sprintf("Operator Build Date: %s", Date)) log.Info(fmt.Sprintf("Operator Built By: %s", Author)) - log.Info(fmt.Sprintf("Operator Git Commit: %s", Commit)) + log.Info(fmt.Sprintf("Operator Git Commit: %s (%s)", Commit, Branch)) log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) } diff --git a/test/e2e/local/cli_test.go b/test/e2e/local/cli_test.go index baa6da9f3..3467343db 100644 --- a/test/e2e/local/cli_test.go +++ b/test/e2e/local/cli_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. + * Copyright (c) 2020, 2025, Oracle and/or its affiliates. * Licensed under the Universal Permissive License v 1.0 as shown at * http://oss.oracle.com/licenses/upl. */ @@ -29,7 +29,36 @@ func TestCoherenceCLI(t *testing.T) { hasFinalizer := controllerutil.ContainsFinalizer(&data, coh.CoherenceFinalizer) g.Expect(hasFinalizer).To(BeTrue()) - _, err := exec.Command("kubectl", "-n", data.Namespace, "exec", "storage-0", + out, err := exec.Command("kubectl", "-n", data.Namespace, "exec", "storage-0", "-c", "coherence", "--", "/coherence-operator/utils/cohctl", "get", "members").CombinedOutput() + t.Log("CLI Output:") + t.Log(string(out)) + g.Expect(err).NotTo(HaveOccurred()) +} + +// Test that the Coherence CLI can be executed in a Pod +func TestCoherenceCLIWithCustomHome(t *testing.T) { + // Make sure we defer clean-up when we're done!! + testContext.CleanupAfterTest(t) + g := NewWithT(t) + + deployments, _ := helper.AssertDeployments(testContext, t, "deployment-cli-home.yaml") + + data, ok := deployments["storage"] + g.Expect(ok).To(BeTrue(), "did not find expected 'storage' deployment") + + hasFinalizer := controllerutil.ContainsFinalizer(&data, coh.CoherenceFinalizer) + g.Expect(hasFinalizer).To(BeTrue()) + + out, err := exec.Command("kubectl", "-n", data.Namespace, "exec", "storage-0", + "-c", "coherence", "--", "/coherence-operator/utils/cohctl", "get", "members").CombinedOutput() + t.Log("CLI Output:") + t.Log(string(out)) + g.Expect(err).NotTo(HaveOccurred()) + + out, err = exec.Command("kubectl", "-n", data.Namespace, "exec", "storage-0", + "-c", "coherence", "--", "/coherence-operator/utils/cohctl", "--config-dir", "/test/cli", "get", "members").CombinedOutput() + t.Log("CLI Output:") + t.Log(string(out)) g.Expect(err).NotTo(HaveOccurred()) } diff --git a/test/e2e/local/deployment-cli-home.yaml b/test/e2e/local/deployment-cli-home.yaml new file mode 100644 index 000000000..a125fec84 --- /dev/null +++ b/test/e2e/local/deployment-cli-home.yaml @@ -0,0 +1,21 @@ +apiVersion: coherence.oracle.com/v1 +kind: Coherence +metadata: + name: storage +spec: + env: + - name: COH_SKIP_SITE + value: "true" + - name: COHCTL_HOME + value: /test/cli + volumeMounts: + - mountPath: /test/cli + name: cli + readOnly: false + volumes: + - name: cli + emptyDir: {} + + + +