diff --git a/.github/workflows/build-test-dev.yml b/.github/workflows/build-test-dev.yml
index d83389f..1de2195 100644
--- a/.github/workflows/build-test-dev.yml
+++ b/.github/workflows/build-test-dev.yml
@@ -6,7 +6,7 @@ on:
- main
env:
- VERSION: 1.3.0
+ VERSION: 1.4.0
IMAGE_NAME: pubsubplus-eventbroker-operator
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
GCLOUD_PROJECT_ID_DEV: ${{ secrets.GCLOUD_PROJECT_ID }}
@@ -252,6 +252,11 @@ jobs:
uses: ./.github/workflows/test-broker-chaos-situation.yml
secrets: inherit
+ int-nodeport:
+ needs: build
+ uses: ./.github/workflows/test-nodeport.yml
+ secrets: inherit
+
taints-and-tolerations:
if: ${{ false }} # disable for now
needs: build
diff --git a/.github/workflows/prep-release.yml b/.github/workflows/prep-release.yml
index 819778f..0c6a3c1 100644
--- a/.github/workflows/prep-release.yml
+++ b/.github/workflows/prep-release.yml
@@ -4,7 +4,7 @@ on:
release_tag:
description: 'Release tag'
required: true
- default: '1.3.0'
+ default: '1.4.0'
prep_internal_release:
# Need to distinguish between internal and external releases
# Internal release: Will use default internal location for created images (ghcr.io) and will tag and push operator candidate there
diff --git a/.github/workflows/test-nodeport.yml b/.github/workflows/test-nodeport.yml
new file mode 100644
index 0000000..1d58e81
--- /dev/null
+++ b/.github/workflows/test-nodeport.yml
@@ -0,0 +1,118 @@
+name: Integration Test for NodePort Service Type
+
+on: workflow_call
+
+jobs:
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: 'read'
+ id-token: 'write'
+ packages: 'read'
+
+ steps:
+ - name: Set env and tools
+ run: |
+ echo "TESTNAMESPACE=op-test-nodeport-$(date +%s)" >> $GITHUB_ENV
+
+ - name: Check out code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.head_ref }}
+ fetch-depth: 0
+
+ - id: 'auth'
+ name: 'Authenticate to Google Cloud'
+ uses: 'google-github-actions/auth@v1.0.0'
+ with:
+ service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
+ workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
+ access_token_lifetime: 600s
+
+ - name: Use the GKE Autopilot test cluster
+ uses: 'google-github-actions/get-gke-credentials@v1.0.0'
+ with:
+ cluster_name: 'dev-integrationtesting'
+ location: 'us-central1'
+
+ - name: Login to Github Packages
+ uses: docker/login-action@v2
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Deploy Operator
+ run: |
+ sleep 20;
+ for i in {1..3}; do
+ kubectl cluster-info
+ kubectl get pods -n kube-system
+ echo "current-context:" $(kubectl config current-context)
+ echo "environment-kubeconfig:" ${KUBECONFIG}
+ kubectl get ns pubsubplus-operator-system || kubectl create ns pubsubplus-operator-system
+ if kubectl get deployment pubsubplus-eventbroker-operator -n pubsubplus-operator-system; then
+ echo "pubsubplus-eventbroker-operator is already deployed"
+ break
+ else
+ kubectl apply -f <(kubectl create secret generic regcred --from-file=.dockerconfigjson=${HOME}/.docker/config.json --type=kubernetes.io/dockerconfigjson -n pubsubplus-operator-system --dry-run=client -o yaml)
+ make deploy | grep 'created \| configured'
+ kubectl rollout status deployment pubsubplus-eventbroker-operator -n pubsubplus-operator-system --timeout=240s
+ if [ $? -eq 0 ]; then
+ break
+ else
+ echo "Rollout status check failed, retrying in 20 seconds..."
+ sleep 20
+ fi
+ fi
+ done
+
+ - name: Testing NodePort with Fixed Port Assignment
+ run: |
+ kubectl create ns $TESTNAMESPACE && kubectl config set-context --current --namespace=$TESTNAMESPACE
+ kubectl apply -f ci/manifests/eventbroker-nonha-nodeport.yaml | grep "test-nonha-nodeport created"
+ sleep 25 ; kubectl get all
+ for i in {1..3}; do
+ if kubectl wait pods --selector app.kubernetes.io/instance=test-nonha-nodeport --for condition=Ready --timeout=120s; then
+ echo "Pods are ready."
+ break
+ else
+ echo "Waiting for pods failed, retrying in 10 seconds..."
+ kubectl describe pods --selector app.kubernetes.io/instance=test-nonha-nodeport
+ sleep 10
+ fi
+ done
+
+ # Verify service type is NodePort
+ kubectl get service test-nonha-nodeport-pubsubplus -o jsonpath='{.spec.type}' | grep "NodePort"
+
+ # Verify nodePort values are set correctly
+ SEMP_NODEPORT=$(kubectl get service test-nonha-nodeport-pubsubplus -o jsonpath='{.spec.ports[?(@.name=="tcp-semp")].nodePort}')
+ SMF_NODEPORT=$(kubectl get service test-nonha-nodeport-pubsubplus -o jsonpath='{.spec.ports[?(@.name=="tcp-smf")].nodePort}')
+ WEB_NODEPORT=$(kubectl get service test-nonha-nodeport-pubsubplus -o jsonpath='{.spec.ports[?(@.name=="tcp-web")].nodePort}')
+
+ echo "SEMP NodePort: $SEMP_NODEPORT, Expected: 30080"
+ echo "SMF NodePort: $SMF_NODEPORT, Expected: 30555"
+ echo "WEB NodePort: $WEB_NODEPORT, Expected: 30008"
+
+ [ "$SEMP_NODEPORT" = "30080" ] && echo "SEMP NodePort test passed" || echo "SEMP NodePort test failed"
+ [ "$SMF_NODEPORT" = "30555" ] && echo "SMF NodePort test passed" || echo "SMF NodePort test failed"
+ [ "$WEB_NODEPORT" = "30008" ] && echo "WEB NodePort test passed" || echo "WEB NodePort test failed"
+
+ # Test connectivity through NodePort
+ NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
+ echo "Using node IP: $NODE_IP"
+
+ # Test SMF connectivity directly through NodePort
+ echo "Testing direct NodePort connection to $NODE_IP:$SMF_NODEPORT"
+ curl -O https://sftp.solace.com/download/SDKPERF_C_LINUX64
+ tar -xvf SDKPERF_C_LINUX64
+ pubSubTools/sdkperf_c -cip=tcp://$NODE_IP:$SMF_NODEPORT -mn=1000 -mr=0 -ptl=t1 -stl=t1 | grep "Total Messages"
+
+ kubectl delete eventbroker test-nonha-nodeport | grep deleted
+
+ - name: Delete broker deployment
+ run: |
+ kubectl delete ns $TESTNAMESPACE --ignore-not-found
diff --git a/.github/workflows/vulncheck_periodic.yml b/.github/workflows/vulncheck_periodic.yml
index 54ee452..4abc699 100644
--- a/.github/workflows/vulncheck_periodic.yml
+++ b/.github/workflows/vulncheck_periodic.yml
@@ -4,7 +4,7 @@ on:
- cron: '0 */ * * *'
env:
- VERSION: 1.3.0
+ VERSION: 1.4.0
IMAGE_NAME: pubsubplus-eventbroker-operator
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
GCLOUD_PROJECT_ID_DEV: ${{ secrets.GCLOUD_PROJECT_ID }}
@@ -65,7 +65,7 @@ jobs:
secrets: |
secret/data/development/gcp-gcr GCP_SERVICE_ACCOUNT | GCP_DEV_SERVICE_ACCOUNT
env:
- VERSION: 1.3.0
+ VERSION: 1.4.0
IMAGE_NAME: pubsubplus-eventbroker-operator
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
GCLOUD_PROJECT_ID_DEV: ${{ secrets.GCLOUD_PROJECT_ID }}
@@ -85,7 +85,7 @@ jobs:
gcr.io/${{ env.GCLOUD_PROJECT_ID_DEV }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}
push: true
env:
- VERSION: 1.3.0
+ VERSION: 1.4.0
IMAGE_NAME: pubsubplus-eventbroker-operator
VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
GCLOUD_PROJECT_ID_DEV: ${{ secrets.GCLOUD_PROJECT_ID }}
diff --git a/Dockerfile b/Dockerfile
index b2c6d65..463655e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -24,8 +24,8 @@ FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5-1745855087
LABEL name="solace/pubsubplus-eventbroker-operator"
LABEL maintainer="Solace Corporation"
LABEL vendor="Solace Corporation"
-LABEL version="1.3.0"
-LABEL release="1.3.0"
+LABEL version="1.4.0"
+LABEL release="1.4.0"
LABEL summary="Solace PubSub+ Event Broker Kubernetes Operator"
LABEL description="The Solace PubSub+ Event Broker Kubernetes Operator deploys and manages the lifecycle of PubSub+ Event Brokers"
diff --git a/Makefile b/Makefile
index 3ebde3b..44c2d59 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
# To re-generate a bundle for another specific version without changing the standard setup, you can:
# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
-VERSION ?= 1.3.0
+VERSION ?= 1.4.0
# API_VERSION defines the API version for the PubSubPlusEventBroker CRD
API_VERSION ?= v1beta1
diff --git a/api/v1beta1/eventbroker_types.go b/api/v1beta1/eventbroker_types.go
index 89a6f4f..600b7a5 100644
--- a/api/v1beta1/eventbroker_types.go
+++ b/api/v1beta1/eventbroker_types.go
@@ -160,6 +160,12 @@ type BrokerPort struct {
//+kubebuilder:validation:Type:=number
// Port number to expose on the service
ServicePort int32 `json:"servicePort"`
+ //+optional
+ //+kubebuilder:validation:Minimum=30000
+ //+kubebuilder:validation:Maximum=32767
+ //+kubebuilder:validation:Type:=number
+ // NodePort specifies a fixed node port when service type is NodePort
+ NodePort int32 `json:"nodePort,omitempty"`
}
// Service defines parameters configure Service details for the Broker
diff --git a/bundle/manifests/pubsubplus-eventbroker-operator.clusterserviceversion.yaml b/bundle/manifests/pubsubplus-eventbroker-operator.clusterserviceversion.yaml
index 1f2015d..6f32dcf 100644
--- a/bundle/manifests/pubsubplus-eventbroker-operator.clusterserviceversion.yaml
+++ b/bundle/manifests/pubsubplus-eventbroker-operator.clusterserviceversion.yaml
@@ -29,7 +29,7 @@ metadata:
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
repository: https://github.com/SolaceProducts/pubsubplus-kubernetes-quickstart
support: Solace Products
- name: pubsubplus-eventbroker-operator.v1.3.0
+ name: pubsubplus-eventbroker-operator.v1.4.0
namespace: placeholder
spec:
apiservicedefinitions: {}
@@ -296,7 +296,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.annotations['olm.targetNamespaces']
- image: docker.io/solace/pubsubplus-eventbroker-operator:1.3.0
+ image: docker.io/solace/pubsubplus-eventbroker-operator:1.4.0
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -411,4 +411,4 @@ spec:
provider:
name: Solace Corporation
url: www.solace.com
- version: 1.3.0
\ No newline at end of file
+ version: 1.4.0
\ No newline at end of file
diff --git a/ci/manifests/eventbroker-nonha-nodeport.yaml b/ci/manifests/eventbroker-nonha-nodeport.yaml
new file mode 100644
index 0000000..deec4c6
--- /dev/null
+++ b/ci/manifests/eventbroker-nonha-nodeport.yaml
@@ -0,0 +1,25 @@
+---
+apiVersion: pubsubplus.solace.com/v1beta1
+kind: PubSubPlusEventBroker
+metadata:
+ name: test-nonha-nodeport
+spec:
+ developer: true
+ service:
+ type: NodePort
+ ports:
+ - name: tcp-semp
+ protocol: TCP
+ containerPort: 8080
+ servicePort: 8080
+ nodePort: 30080
+ - name: tcp-smf
+ protocol: TCP
+ containerPort: 55555
+ servicePort: 55555
+ nodePort: 30555
+ - name: tcp-web
+ protocol: TCP
+ containerPort: 8008
+ servicePort: 8008
+ nodePort: 30008
diff --git a/config/crd/bases/pubsubplus.solace.com_pubsubpluseventbrokers.yaml b/config/crd/bases/pubsubplus.solace.com_pubsubpluseventbrokers.yaml
index 32c69d5..e3dabb3 100644
--- a/config/crd/bases/pubsubplus.solace.com_pubsubpluseventbrokers.yaml
+++ b/config/crd/bases/pubsubplus.solace.com_pubsubpluseventbrokers.yaml
@@ -1459,6 +1459,13 @@ spec:
description: Unique name for the port that can be referred
to by services.
type: string
+ nodePort:
+ description: NodePort specifies a fixed node port when service
+ type is NodePort
+ format: int32
+ maximum: 32767
+ minimum: 30000
+ type: number
protocol:
default: TCP
description: Protocol for port. Must be UDP, TCP, or SCTP.
diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml
index 3f6d212..aa12764 100644
--- a/config/manager/kustomization.yaml
+++ b/config/manager/kustomization.yaml
@@ -11,4 +11,4 @@ kind: Kustomization
images:
- name: controller
newName: ghcr.io/solacedev/pubsubplus-eventbroker-operator
- newTag: 1.3.0
+ newTag: 1.4.0
diff --git a/config/manifests/bases/pubsubplus-eventbroker-operator.clusterserviceversion.yaml b/config/manifests/bases/pubsubplus-eventbroker-operator.clusterserviceversion.yaml
index 3d2824b..69faf27 100644
--- a/config/manifests/bases/pubsubplus-eventbroker-operator.clusterserviceversion.yaml
+++ b/config/manifests/bases/pubsubplus-eventbroker-operator.clusterserviceversion.yaml
@@ -8,7 +8,7 @@ metadata:
certified: "true"
com.redhat.delivery.operator.bundle: "true"
com.redhat.openshift.versions: v4.10
- containerImage: docker.io/solace/pubsubplus-eventbroker-operator:v1.3.0
+ containerImage: docker.io/solace/pubsubplus-eventbroker-operator:v1.4.0
createdAt: "2023-03-31T12:00:00.000Z"
description: The Solace PubSub+ Event Broker Operator deploys and manages the
lifecycle of PubSub+ Event Brokers
diff --git a/config/samples/sample_nodeport.yaml b/config/samples/sample_nodeport.yaml
new file mode 100644
index 0000000..9ca7a07
--- /dev/null
+++ b/config/samples/sample_nodeport.yaml
@@ -0,0 +1,24 @@
+apiVersion: pubsubplus.solace.com/v1beta1
+kind: PubSubPlusEventBroker
+metadata:
+ name: node-port-example
+spec:
+ developer: true
+ service:
+ type: NodePort
+ ports:
+ - name: tcp-semp
+ protocol: TCP
+ containerPort: 8080
+ servicePort: 8080
+ nodePort: 30080
+ - name: tcp-smf
+ protocol: TCP
+ containerPort: 55555
+ servicePort: 55555
+ nodePort: 30555
+ - name: tcp-web
+ protocol: TCP
+ containerPort: 8008
+ servicePort: 8008
+ nodePort: 30008
diff --git a/controllers/service.go b/controllers/service.go
index 213dc98..9e09a96 100644
--- a/controllers/service.go
+++ b/controllers/service.go
@@ -63,6 +63,9 @@ func (r *PubSubPlusEventBrokerReconciler) updateServiceForEventBroker(service *c
Port: pbPort.ServicePort,
TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: pbPort.ContainerPort},
}
+
+ // Use the helper function to assign NodePort if needed
+ assignNodePortIfNeeded(&ports[idx], *pbPort, getServiceType(m.Spec.Service))
}
service.Spec.Ports = ports
} else {
@@ -77,6 +80,9 @@ func (r *PubSubPlusEventBrokerReconciler) updateServiceForEventBroker(service *c
Port: pbPort.ServicePort,
TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: pbPort.ContainerPort},
}
+
+ // Use the helper function to assign NodePort if needed
+ assignNodePortIfNeeded(&ports[idx], *pbPort, getServiceType(m.Spec.Service))
}
service.Spec.Ports = ports
}
@@ -89,3 +95,10 @@ func getServiceType(ms eventbrokerv1beta1.Service) corev1.ServiceType {
}
return corev1.ServiceTypeLoadBalancer
}
+
+// Helper function to assign NodePort if needed
+func assignNodePortIfNeeded(servicePort *corev1.ServicePort, brokerPort eventbrokerv1beta1.BrokerPort, serviceType corev1.ServiceType) {
+ if serviceType == corev1.ServiceTypeNodePort && brokerPort.NodePort > 0 {
+ servicePort.NodePort = brokerPort.NodePort
+ }
+}
diff --git a/controllers/service_test.go b/controllers/service_test.go
index ec7ac7c..b09bd1b 100644
--- a/controllers/service_test.go
+++ b/controllers/service_test.go
@@ -192,6 +192,73 @@ var _ = Describe("Service test", func() {
})
+ By("confirming NodePort values are set correctly when service type is NodePort", func() {
+ brokerNodePort := pubsubplus.PubSubPlusEventBroker{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "service-nodeport",
+ Namespace: namespace,
+ },
+ Spec: pubsubplus.EventBrokerSpec{
+ Developer: true,
+ Redundancy: false,
+ UpdateStrategy: pubsubplus.AutomatedRollingUpdateStrategy,
+ Service: pubsubplus.Service{
+ ServiceType: corev1.ServiceTypeNodePort,
+ Annotations: map[string]string{
+ "Sample": "Test",
+ },
+ Ports: []*pubsubplus.BrokerPort{
+ {
+ Name: "tcp-semp",
+ Protocol: corev1.ProtocolTCP,
+ ContainerPort: 8080,
+ ServicePort: 8080,
+ NodePort: 30080,
+ },
+ {
+ Name: "tcp-smf",
+ Protocol: corev1.ProtocolTCP,
+ ContainerPort: 55555,
+ ServicePort: 55555,
+ NodePort: 30555,
+ },
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Create(ctx, &brokerNodePort)).Should(Succeed())
+
+ // Service created successfully
+ EventuallyWithOffset(10, func() bool {
+ service := &corev1.Service{}
+ serviceName := getObjectName("BrokerService", brokerNodePort.Name)
+ err := k8sClient.Get(ctx, types.NamespacedName{Name: serviceName, Namespace: brokerNodePort.Namespace}, service)
+ if err != nil {
+ return false
+ }
+
+ // Verify service type is NodePort
+ if service.Spec.Type != corev1.ServiceTypeNodePort {
+ return false
+ }
+
+ // Verify nodePort values are set correctly
+ for _, port := range service.Spec.Ports {
+ if port.Name == "tcp-semp" && port.NodePort != 30080 {
+ return false
+ }
+ if port.Name == "tcp-smf" && port.NodePort != 30555 {
+ return false
+ }
+ }
+
+ return true
+ }).WithTimeout(20 * time.Second).Should(BeTrue())
+
+ // Delete broker
+ Expect(k8sClient.Delete(ctx, &brokerNodePort)).To(Succeed())
+ })
+
})
})
diff --git a/deploy/deploy.yaml b/deploy/deploy.yaml
index df95aaa..09a1459 100644
--- a/deploy/deploy.yaml
+++ b/deploy/deploy.yaml
@@ -1987,7 +1987,7 @@ spec:
env:
- name: WATCH_NAMESPACE
value: ""
- image: docker.io/solace/pubsubplus-eventbroker-operator:1.3.0
+ image: docker.io/solace/pubsubplus-eventbroker-operator:1.4.0
imagePullPolicy: Always
livenessProbe:
httpGet:
diff --git a/docs/EventBrokerOperatorParametersReference.md b/docs/EventBrokerOperatorParametersReference.md
index 396f4ef..fad5c06 100644
--- a/docs/EventBrokerOperatorParametersReference.md
+++ b/docs/EventBrokerOperatorParametersReference.md
@@ -2737,6 +2737,15 @@ Port defines parameters configure Service details for the Broker
Unique name for the port that can be referred to by services.