Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7ccedea
expanding externalCerts to support configMaps
Gazza7205 Mar 30, 2026
273c04f
added support for policy/service aliases, sampleMessages and policyBa…
Gazza7205 Apr 8, 2026
7b12e1f
added retry scenario for failed applies where a new commit is detected
Gazza7205 Apr 9, 2026
37a6fdb
updated ingress and service reconcile
Gazza7205 Apr 10, 2026
d56a59f
added graphman constraint to listenPorts for management connection
Gazza7205 Apr 10, 2026
86b2410
added additional checks and logs for repositories
Gazza7205 Apr 10, 2026
59e4bf5
updated logs, refactored temp paths
Gazza7205 Apr 13, 2026
b53525c
added checks for init configuration where an empty repository is used
Gazza7205 Apr 15, 2026
f92c43b
updated handling for root level bundles in repositories that have no …
Gazza7205 Apr 15, 2026
8d73b46
added additional checks for commits
Gazza7205 Apr 16, 2026
f2ec943
updated statestore repository handling
Gazza7205 Apr 17, 2026
ef90490
removed redundant repository conditions
Gazza7205 Apr 20, 2026
9b9dff4
added additional checks and validation for invalid repository bundles…
Gazza7205 Apr 21, 2026
4269978
added additional logging for invalid entities
Gazza7205 Apr 22, 2026
40dc1c9
added gateway startup probe
Gazza7205 Apr 22, 2026
8a02415
added merge condition for serviceResolutionConfigs
Gazza7205 Apr 22, 2026
4748e6e
fix tests
Gazza7205 Apr 22, 2026
081ed58
updated dependencies, set gateway version to 11.2.1
Gazza7205 Apr 22, 2026
fd3ae33
rebuilt config
Gazza7205 Apr 22, 2026
6bc4946
updated redis connection handling
Gazza7205 Apr 23, 2026
3a2752d
updated examples - gateway v11.2.1, graphman-static-init 1.0.5, opera…
Gazza7205 Apr 23, 2026
1587dd8
updated dual mode otk examples, removed unused otk schema
Gazza7205 Apr 24, 2026
c79e16a
fixed ephemeral storage rebuild for failover scenarios
Gazza7205 Jun 3, 2026
9cad8f7
added check to handle ephemeral gateway restarts, fixed a bug where g…
Gazza7205 Jun 3, 2026
c8080c0
addressing internal review comments and statestore apply reliability
Gazza7205 Jun 9, 2026
0526154
added type guard for externalCerts, updated default initImages, added…
Gazza7205 Jun 10, 2026
4d96704
adding insecureSkipVerify to init config, updating tests
Gazza7205 Jun 13, 2026
b2a1629
propagated insecureSkipVerify to graphman-static-init, updated stateS…
Gazza7205 Jun 25, 2026
f4dee40
Add db migrate job for Gateway upgrades (#99)
jchanbcbc Jul 7, 2026
325a265
updated helm chart, jenkinsfile, go version, mod versions
Gazza7205 Jul 8, 2026
9a4db8e
bumping go version
Gazza7205 Jul 9, 2026
413668a
updated branch/tag names checking, improved error handling for compre…
Gazza7205 Jul 13, 2026
e12b604
updating copyright
Gazza7205 Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "~1.24.13"
go-version: "~1.26.5"
- name: Setup kind
env:
KIND_VERSION: "0.30.0"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "~1.24.13"
go-version: "~1.26.5"
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ docker-secret.yaml
tenant_info.json
example/repositories/local-tests
example/gateway/local-tests
example/state-store/local-tests
example/base/resources/configmaps/cassandra
.vscode
plans/

# internal automation modified files
deploy/no-crd.yaml
Expand Down
6 changes: 3 additions & 3 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pipeline {
agent { label "default" }
environment {
ARTIFACTORY_DOCKER_IMS_IMAGE_REG = "ims-base-images-docker-release-local.usw1.packages.broadcom.com"
ARTIFACTORY_DOCKER_IMS_IMAGE = "ims-distro-debian13-static:202510"
ARTIFACTORY_DOCKER_IMS_IMAGE = "ims-distro-debian13-static:26.06.24"
ARTIFACTORY_DOCKER_GO_IMAGE_REG = "docker-hub.usw1.packages.broadcom.com"
ARTIFACTORY_DOCKER_DEV_LOCAL_REG_HOST = "apim-docker-dev-local.usw1.packages.broadcom.com"
ARTIFACT_HOST = "${ARTIFACTORY_DOCKER_DEV_LOCAL_REG_HOST}"
Expand Down Expand Up @@ -34,7 +34,7 @@ pipeline {
if [[ "${BRANCH_NAME}" = "develop" ]]; then
export IMAGE_TAG="latest"
else
export RELEASE_VERSION="${BRANCH_NAME}"
export RELEASE_VERSION="$(echo "${BRANCH_NAME}" | tr '/' '-')"
fi
fi

Expand Down Expand Up @@ -63,7 +63,7 @@ pipeline {
docker login ${ARTIFACTORY_DOCKER_IMS_IMAGE_REG} -u ${ARTIFACTORY_DEV_LOCAL_USERNAME} -p ${ARTIFACTORY_DEV_LOCAL_APIKEY}
docker login ${ARTIFACTORY_DOCKER_GO_IMAGE_REG} -u ${ARTIFACTORY_DEV_LOCAL_USERNAME} -p ${ARTIFACTORY_DEV_LOCAL_APIKEY}
DISTROLESS_IMG=${ARTIFACTORY_DOCKER_IMS_IMAGE_REG}/${ARTIFACTORY_DOCKER_IMS_IMAGE}
GO_BUILD_IMG=${ARTIFACTORY_DOCKER_GO_IMAGE_REG}/golang:1.24
GO_BUILD_IMG=${ARTIFACTORY_DOCKER_GO_IMAGE_REG}/golang:1.26
cat Dockerfile | sed -e "s~DISTROLESS_IMG~${DISTROLESS_IMG}~g" | sed -e "s~GO_BUILD_IMG~${GO_BUILD_IMG}~g" > operator.Dockerfile
docker buildx build -f operator.Dockerfile -t ${ARTIFACTORY_DOCKER_DEV_LOCAL_REG_HOST}/${IMAGE_TAG_BASE}:${RELEASE_VERSION} --builder "${DOCKER_BUILDER_NAME}" --platform="${TARGET_PLATFORMS}" --build-arg TITLE="${IMAGE_NAME}" --build-arg COPYRIGHT="${COPYRIGHT}" --build-arg VERSION="${RELEASE_VERSION}" --build-arg CREATED="${CREATED}" --build-arg GOPROXY="${GOPROXY}" . --push
'''
Expand Down
11 changes: 7 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ endif
OPERATOR_SDK_VERSION ?= v1.33.0
# Image URL to use all building/pushing image targets
#IMG ?= docker.io/layer7api/layer7-operator:v$(VERSION)
IMG ?= $(IMAGE_TAG_BASE):$(VERSION)
# Sanitize VERSION for use as a Docker image tag: slashes (from branch names like
# feature/foo) are not valid in tags and must be replaced with hyphens.
DOCKER_VERSION := $(subst /,-,$(VERSION))
IMG ?= $(IMAGE_TAG_BASE):$(DOCKER_VERSION)
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.30.0

Expand All @@ -68,8 +71,8 @@ START_KIND_CLUSTER ?= true
KUBE_VERSION ?= 1.34
KIND_CONFIG ?= kind-$(KUBE_VERSION).yaml

GATEWAY_IMG ?= docker.io/caapim/gateway:11.1.3
GO_BUILD_IMG ?= golang:1.24
GATEWAY_IMG ?= docker.io/caapim/gateway:11.2.1
GO_BUILD_IMG ?= golang:1.26
DISTROLESS_IMG ?= gcr.io/distroless/static:nonroot
GO_PROXY ?= ""

Expand Down Expand Up @@ -218,7 +221,7 @@ build: manifests generate fmt vet ## Build manager binary.

.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
go run ./cmd/main.go --zap-log-level=5
go run ./cmd/main.go --zap-log-level=5 --leader-elect=false

.PHONY: docker-build
docker-build: dockerfile #test ## Build docker image with the manager.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Layer7 Gateway Operator
The Layer7 Gateway Operator, built using the [Operator SDK](https://github.com/operator-framework/operator-sdk) covers all aspects of deploying, maintaining and upgrading API Gateways in Kubernetes.

##### Note: The Operator examples currently use ***Gateway 11.1.3*** as a base.
##### Note: The Operator examples currently use ***Gateway 11.2.1*** as a base.

## [Getting Started](https://github.com/CAAPIM/layer7-operator/wiki/Getting-Started)
## [Additional Documentation](https://github.com/CAAPIM/layer7-operator/wiki)
Expand Down
67 changes: 51 additions & 16 deletions api/v1/gateway_types.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Copyright (c) 2026 Broadcom Inc. and its subsidiaries. All Rights Reserved.
/*
Copyright 2021.

Expand All @@ -12,6 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
* AI assistance has been used to generate some or all contents of this file. That includes, but is not limited to, new code, modifying existing code, stylistic edits.
*/

package v1
Expand Down Expand Up @@ -64,6 +66,20 @@ type GatewayList struct {
Items []Gateway `json:"items"`
}

// MigrationStatus tracks the state of the pre-upgrade database migration job.
// It is persisted in Gateway.Status so that completed migrations survive job
// deletion and are not re-run on daily reconciles or operator restarts.
type MigrationStatus struct {
// SpecHash is a short hash of the migration-relevant spec fields (image,
// effective jdbcUrl, clearLocks). When any of these change the hash changes
// and a fresh migration job is triggered.
SpecHash string `json:"specHash,omitempty"`
// Complete indicates that the migration job succeeded for the current SpecHash.
// Once true, GatewayMigrationJob skips job management entirely and the
// Deployment step is unblocked — regardless of whether the Job still exists.
Complete bool `json:"complete,omitempty"`
}

// GatewayStatus defines the observed state of Gateways
type GatewayStatus struct {
// Host is the Gateway Cluster Hostname
Expand Down Expand Up @@ -119,6 +135,8 @@ type GatewayStatus struct {
LastAppliedExternalCerts map[string][]string `json:"lastAppliedExternalCerts,omitempty"`
// LastAppliedOtkFipsCerts tracks which OTK FIPS user certificates have been applied
LastAppliedOtkFipsCerts map[string][]string `json:"lastAppliedOtkFipsCerts,omitempty"`
// MigrationStatus tracks the state of the pre-upgrade database migration job.
MigrationStatus MigrationStatus `json:"migrationStatus,omitempty"`
}

// GatewayState tracks the status of Gateway Resources
Expand Down Expand Up @@ -171,6 +189,9 @@ type GatewayRepositoryStatus struct {
Conditions []RepositoryCondition `json:"conditions,omitempty"`
// Directories
Directories []string `json:"directories,omitempty"`
// InsecureSkipVerify disables TLS certificate verification for http and git
// repository types. Propagated from Repository.Spec.InsecureSkipVerify.
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
}

type RepositoryCondition struct {
Expand Down Expand Up @@ -259,6 +280,7 @@ type App struct {
ExternalSecrets []ExternalSecret `json:"externalSecrets,omitempty"`
ExternalKeys []ExternalKey `json:"externalKeys,omitempty"`
ExternalCerts []ExternalCert `json:"externalCerts,omitempty"`
StartupProbe corev1.Probe `json:"startupProbe,omitempty"`
LivenessProbe corev1.Probe `json:"livenessProbe,omitempty"`
ReadinessProbe corev1.Probe `json:"readinessProbe,omitempty"`
CustomConfig CustomConfig `json:"customConfig,omitempty"`
Expand Down Expand Up @@ -344,9 +366,6 @@ type PortalReference struct {
type Otk struct {
// Enable or disable the OTK initContainer
Enabled bool `json:"enabled,omitempty"`
// ManageCrossNamespace allows a cluster-wide layer7 operator to manage internal/dmz gateways across namespaces
// this is limited to a single kubernetes cluster.
ManageCrossNamespace bool `json:"manageCrossNamespace,omitempty"`
// InitContainerImage for the initContainer
InitContainerImage string `json:"initContainerImage,omitempty"`
// InitContainerImagePullPolicy
Expand Down Expand Up @@ -392,16 +411,9 @@ type OtkMaintenanceTasks struct {
}

type GatewayReference struct {
// Name of the gateway
// if managing otk gateways across namespaces this must match the referenced gateway CR
Name string `json:"name,omitempty"`
// Namespace of the referenced gateway if managing gateways cross namespace (optional)
Namespace string `json:"namespace,omitempty"`
// Url of the target gateway
// used for post-installation gateway policy configuration
Url string `json:"url,omitempty"`
// Port of the target gateway
Port int `json:"port,omitempty"`
}

type OtkOverrides struct {
Expand Down Expand Up @@ -694,6 +706,27 @@ type Database struct {
Password string `json:"password,omitempty"`
// LiquibaseLogLevel
LiquibaseLogLevel LiquibaseLogLevel `json:"liquibaseLogLevel,omitempty"`
// MigrationJob for pre-upgrade schema updates
MigrationJob MigrationJob `json:"migrationJob,omitempty"`
}

// MigrationJob configures the pre-upgrade database migration job
type MigrationJob struct {
// Enabled or disabled
Enabled bool `json:"enabled,omitempty"`
// JDBCUrl overrides the main database.jdbcUrl for the migration job (e.g. to bypass a proxy).
// Only applies in diskless mode (disklessConfig.disabled: false, the default).
// In non-diskless mode the entrypoint reads the JDBC URL from the mounted node.properties
// file and this field has no effect — node.properties always wins, consistent with Helm behavior.
JDBCUrl string `json:"jdbcUrl,omitempty"`
// ClearLocks forces release of any stuck Liquibase locks before applying
// schema updates. Use with caution: forcefully releasing a lock while
// another process is actively updating the schema can corrupt the database.
ClearLocks bool `json:"clearLocks,omitempty"`
// ActiveDeadlineSeconds is the max duration each pod attempt is allowed to run.
// The job may retry once (backoffLimit=1), so total elapsed time can be up to
// 2× this value. Defaults to 300 seconds (5 minutes) per attempt.
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
}

// Restman is a Gateway Management interface that can be automatically provisioned.
Expand All @@ -720,7 +753,7 @@ type Graphman struct {
// Service
type Service struct {
// Enabled or disabled
Enabled bool ` json:"enabled,omitempty"`
Enabled bool `json:"enabled,omitempty"`
// Annotations for the service
Annotations map[string]string `json:"annotations,omitempty"`
// Type ClusterIP, NodePort, LoadBalancer
Expand Down Expand Up @@ -885,15 +918,17 @@ const (
RevocationCheckPolicyTypeSpecified RevocationCheckPolicyType = "SPECIFIED"
)

// ExternalCert is a reference to an existing TLS or Opaque Secret in Kubernetes
// The Layer7 Operator will attempt to convert this secret to a Graphman bundle that can be applied
// ExternalCert is a reference to an existing TLS or Opaque Secret or ConfigMap in Kubernetes
// The Layer7 Operator will attempt to convert this to a Graphman bundle that can be applied
// dynamically keeping any referenced trusted certs up-to-date.
// You can bring in external secrets using tools like cert-manager
// You can bring in external certs using tools like cert-manager
type ExternalCert struct {
// Enabled or disabled
Enabled bool `json:"enabled,omitempty"`
// Name of the Secret which already exists in Kubernetes
Name string `json:"name,omitempty"`
// Name of the Secret or ConfigMap which already exists in Kubernetes
Name string `json:"name,omitempty"`
// Type of the referenced resource: "secret" (default) or "configmap"
Type string `json:"type,omitempty"`
VerifyHostname bool `json:"verifyHostname,omitempty"`
TrustedFor []TrustedFor `json:"trustedFor,omitempty"`
TrustAnchor bool `json:"trustAnchor,omitempty"`
Expand Down
54 changes: 26 additions & 28 deletions api/v1/gateway_webhook.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Copyright (c) 2026 Broadcom Inc. and its subsidiaries. All Rights Reserved.
/*
Copyright 2021.

Expand All @@ -24,7 +25,6 @@ import (
"strings"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
Expand All @@ -34,52 +34,39 @@ import (
//var gatewaylog = logf.Log.WithName("gateway-resource")

func (r *Gateway) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
return ctrl.NewWebhookManagedBy(mgr, r).
WithDefaulter(r).
WithValidator(r).
Complete()
}

//+kubebuilder:webhook:path=/mutate-security-brcmlabs-com-v1-gateway,mutating=true,failurePolicy=fail,sideEffects=None,groups=security.brcmlabs.com,resources=gateways,verbs=create;update,versions=v1,name=mgateway.kb.io,admissionReviewVersions=v1

var _ admission.CustomDefaulter = &Gateway{}
var _ admission.Defaulter[*Gateway] = &Gateway{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *Gateway) Default(ctx context.Context, obj runtime.Object) error {
// Default implements admission.Defaulter for *Gateway.
func (r *Gateway) Default(ctx context.Context, obj *Gateway) error {
return nil
}

//+kubebuilder:webhook:path=/validate-security-brcmlabs-com-v1-gateway,mutating=false,failurePolicy=fail,sideEffects=None,groups=security.brcmlabs.com,resources=gateways,verbs=create;update,versions=v1,name=vgateway.kb.io,admissionReviewVersions=v1

var _ admission.CustomValidator = &Gateway{}
var _ admission.Validator[*Gateway] = &Gateway{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *Gateway) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
gateway, ok := obj.(*Gateway)
if !ok {
return nil, fmt.Errorf("expected a Gateway, received %T", obj)
}
return validateGateway(gateway)
// ValidateCreate implements admission.Validator for *Gateway.
func (r *Gateway) ValidateCreate(ctx context.Context, obj *Gateway) (admission.Warnings, error) {
return validateGateway(obj)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *Gateway) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
_, ok := oldObj.(*Gateway)
if !ok {
return nil, fmt.Errorf("expected a Gateway for oldObj, received %T", oldObj)
}
gateway, ok := newObj.(*Gateway)
if !ok {
return nil, fmt.Errorf("expected a Gateway for newObj, received %T", newObj)
}
return validateGateway(gateway)
// ValidateUpdate implements admission.Validator for *Gateway.
func (r *Gateway) ValidateUpdate(ctx context.Context, oldObj, newObj *Gateway) (admission.Warnings, error) {
return validateGateway(newObj)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *Gateway) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
// ValidateDelete implements admission.Validator for *Gateway.
func (r *Gateway) ValidateDelete(ctx context.Context, obj *Gateway) (admission.Warnings, error) {
//gatewaylog.Info("validate delete", "name", r.Name)
return []string{}, nil
return nil, nil
}

func validateGateway(r *Gateway) (admission.Warnings, error) {
Expand Down Expand Up @@ -169,6 +156,12 @@ func validateGateway(r *Gateway) (admission.Warnings, error) {
}
}

for i, ec := range r.Spec.App.ExternalCerts {
if ec.Enabled && ec.Type != "" && strings.ToLower(ec.Type) != "secret" && strings.ToLower(ec.Type) != "configmap" {
return warnings, fmt.Errorf("please specify a valid external cert type, valid types are secret (default) or configmap. name: %s index: %d", ec.Name, i)
}
}

if r.Spec.App.Hazelcast.External {
if r.Spec.App.Hazelcast.Endpoint == "" {
return warnings, fmt.Errorf("please specify the endpoint for your external Hazelcast server")
Expand Down Expand Up @@ -201,6 +194,11 @@ func validateGateway(r *Gateway) (admission.Warnings, error) {
}
}

if r.Spec.App.Management.Database.MigrationJob.Enabled &&
!r.Spec.App.Management.Database.Enabled {
return warnings, fmt.Errorf("migrationJob.enabled requires database.enabled: true")
}

if r.Spec.App.Management.Service.Enabled {
if r.Spec.App.Management.Service.Type != v1.ServiceTypeClusterIP && r.Spec.App.Management.Service.Type != v1.ServiceTypeLoadBalancer && r.Spec.App.Management.Service.Type != v1.ServiceTypeNodePort {
return warnings, fmt.Errorf("please specify a valid management service type, valid types are LoadBalancer, ClusterIP and NodePort")
Expand Down
8 changes: 8 additions & 0 deletions api/v1/repository_types.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Copyright (c) 2026 Broadcom Inc. and its subsidiaries. All Rights Reserved.
/*
Copyright 2021.

Expand All @@ -12,6 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
* AI assistance has been used to generate some or all contents of this file. That includes, but is not limited to, new code, modifying existing code, stylistic edits.
*/

package v1
Expand Down Expand Up @@ -75,6 +77,12 @@ type RepositorySpec struct {
// Auth contains a reference to the credentials required to connect to your Git repository
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Auth"
Auth RepositoryAuth `json:"auth,omitempty"`
// InsecureSkipVerify disables TLS certificate verification for all repository
// types (http, git). For git repositories this is OR-combined with the legacy
// -insecure vendor flag: either condition set to true enables insecure mode.
// Should only be set to true for internal repositories using self-signed certificates.
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="InsecureSkipVerify"
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down
Loading
Loading