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: {}
+
+
+
+