Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
# All Kubernetes version in between expose the same APIs, hence the operator
# should be compatible with them.
kube-version:
- "1.31"
- "1.34"
group:
- e2e
steps:
Expand All @@ -31,10 +31,10 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "~1.23.3"
go-version: "~1.24.10"
- name: Setup kind
env:
KIND_VERSION: "0.25.0"
KIND_VERSION: "0.30.0"
run: go install sigs.k8s.io/kind@v${KIND_VERSION}
- name: "install kuttl"
run: ./hack/install-kuttl.sh
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/integration_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ jobs:
# All Kubernetes version in between expose the same APIs, hence the operator
# should be compatible with them.
kube-version:
- "1.31"
- "1.34"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "~1.23.3"
go-version: "~1.24.10"
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Setup kind
env:
KIND_VERSION: "0.25.0"
KIND_VERSION: "0.30.0"
run: go install sigs.k8s.io/kind@v${KIND_VERSION}
- name: "install kuttl"
run: ./hack/install-kuttl.sh
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM GO_BUILD_IMG AS builder
FROM --platform=$BUILDPLATFORM GO_BUILD_IMG AS builder

ARG AUTHOR=Layer7
ARG VENDOR="Broadcom Inc."
Expand Down Expand Up @@ -28,7 +28,7 @@ COPY scripts/ scripts/
ENV GOPROXY=${GOPROXY}
RUN go mod download

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager cmd/main.go
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH GO111MODULE=on go build -a -o manager cmd/main.go

FROM DISTROLESS_IMG

Expand Down
28 changes: 24 additions & 4 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ 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-debian12-static:202505-amd64"
ARTIFACTORY_DOCKER_IMS_IMAGE = "ims-distro-debian13-static:202510"
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}"
ARTIFACTORY_DOCKER_DEV_LOCAL_REG_PROJECT = "apim-gateway"
IMAGE_NAME = "layer7-operator"
IMAGE_TAG_BASE = "${ARTIFACTORY_DOCKER_DEV_LOCAL_REG_PROJECT}/${IMAGE_NAME}"
TARGET_PLATFORMS="linux/amd64,linux/arm64"
ARTIFACTORY_CREDS = credentials('ARTIFACTORY_USERNAME_TOKEN')
DOCKER_HUB_CREDS = credentials('DOCKERHUB_USERNAME_PASSWORD_RW')
def CREATED = sh(script: "echo `date -u +%Y-%m-%dT%H:%M:%SZ`", returnStdout: true).trim()
Expand All @@ -37,15 +38,34 @@ pipeline {
fi
fi

info "Getting the Docker and driver info"
docker --version

DOCKER_BUILDER_NAME=multiarch-builder
info "Using docker buildx builder ${DOCKER_BUILDER_NAME}"

# temporary workaround for buildx builder with driver docker-container
if docker buildx inspect ${DOCKER_BUILDER_NAME}; then
info "${DOCKER_BUILDER_NAME} already exists"

if docker buildx inspect ${DOCKER_BUILDER_NAME} | grep ^Driver: | grep -q docker-container; then
info "docker builder ${DOCKER_BUILDER_NAME} is using docker-container driver."
else
error "docker builder ${DOCKER_BUILDER_NAME} is not using docker-container driver."
fi
else
info "Creating docker builder ${DOCKER_BUILDER_NAME}"
docker buildx create --name ${DOCKER_BUILDER_NAME} --driver docker-container
fi

GOPROXY="https://${ARTIFACTORY_DEV_LOCAL_USERNAME}:${ARTIFACTORY_DEV_LOCAL_APIKEY}@usw1.packages.broadcom.com/artifactory/api/go/apim-golang-virtual"
docker login ${ARTIFACTORY_DOCKER_DEV_LOCAL_REG_HOST} -u ${ARTIFACTORY_DEV_LOCAL_USERNAME} -p ${ARTIFACTORY_DEV_LOCAL_APIKEY}
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.23
GO_BUILD_IMG=${ARTIFACTORY_DOCKER_GO_IMAGE_REG}/golang:1.24
cat Dockerfile | sed -e "s~DISTROLESS_IMG~${DISTROLESS_IMG}~g" | sed -e "s~GO_BUILD_IMG~${GO_BUILD_IMG}~g" > operator.Dockerfile
docker build -f operator.Dockerfile -t ${ARTIFACTORY_DOCKER_DEV_LOCAL_REG_HOST}/${IMAGE_TAG_BASE}:${RELEASE_VERSION} . --build-arg TITLE="${IMAGE_NAME}" --build-arg COPYRIGHT="${COPYRIGHT}" --build-arg VERSION="${RELEASE_VERSION}" --build-arg CREATED="${CREATED}" --build-arg GOPROXY="${GOPROXY}"
docker push ${ARTIFACTORY_DOCKER_DEV_LOCAL_REG_HOST}/${IMAGE_TAG_BASE}:${RELEASE_VERSION}
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
14 changes: 7 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ ENVTEST_K8S_VERSION = 1.30.0

START_KIND_CLUSTER ?= true

KUBE_VERSION ?= 1.30
KUBE_VERSION ?= 1.34
KIND_CONFIG ?= kind-$(KUBE_VERSION).yaml

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

Expand Down Expand Up @@ -218,7 +218,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=10
go run ./cmd/main.go --zap-log-level=5

.PHONY: docker-build
docker-build: dockerfile #test ## Build docker image with the manager.
Expand Down Expand Up @@ -307,8 +307,8 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest

## Tool Versions
KUSTOMIZE_VERSION ?= v5.4.2
CONTROLLER_TOOLS_VERSION ?= v0.16.5
KUSTOMIZE_VERSION ?= v5.6.0
CONTROLLER_TOOLS_VERSION ?= v0.19.0

.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
Expand All @@ -328,7 +328,7 @@ $(CONTROLLER_GEN): $(LOCALBIN)
.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
$(ENVTEST): $(LOCALBIN)
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@release-0.20
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@release-0.22

.PHONY: operator-sdk
OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk
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.2*** as a base.
##### Note: The Operator examples currently use ***Gateway 11.1.3*** 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
62 changes: 55 additions & 7 deletions api/v1/gateway_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ type GatewayRepositoryStatus struct {
Commit string `json:"commit,omitempty"`
// Type is static or dynamic
Type string `json:"type,omitempty"`
// RepoType - git, http, local, statestore
RepoType string `json:"repoType,omitempty"`
// Vendor i.e. Github, Gitlab, BitBucket, Azure
Vendor string `json:"vendor,omitempty"`
// AuthType defaults to basic, possible options are none, basic or ssh
AuthType string `json:"authType,omitempty"`
//SecretName is used to mount the correct repository secret to the initContainer
SecretName string `json:"secretName,omitempty"`
//StorageSecretName is used to mount existing repository bundles to the initContainer
Expand All @@ -161,6 +167,8 @@ type GatewayRepositoryStatus struct {
StateStoreKey string `json:"stateStoreKey,omitempty"`
// Conditions
Conditions []RepositoryCondition `json:"conditions,omitempty"`
// Directories
Directories []string `json:"directories,omitempty"`
}

type RepositoryCondition struct {
Expand Down Expand Up @@ -221,13 +229,19 @@ type App struct {
// this enables scheduled tasks that are set to execute on a single node and jms destinations that are outbound
// to be applied to one ephemeral gateway only.
// This works inconjunction with repository references and only supports dynamic repository references.
SingletonExtraction bool `json:"singletonExtraction,omitempty"`
RepositoryReferences []RepositoryReference `json:"repositoryReferences,omitempty"`
Ingress Ingress `json:"ingress,omitempty"`
Sidecars []corev1.Container `json:"sidecars,omitempty"`
InitContainers []corev1.Container `json:"initContainers,omitempty"`
Resources PodResources `json:"resources,omitempty"`
Autoscaling Autoscaling `json:"autoscaling,omitempty"`
SingletonExtraction bool `json:"singletonExtraction,omitempty"`
// BootstrapRepositoryReferences bootstraps repositoryReferences of type dynamic to avoid service unavailable at gateway ready.
RepositoryReferenceBootstrap RepositoryReferenceBootstrap `json:"repositoryReferenceBootstrap,omitempty"`
// RepositoryReferenceDelete enables repository delete when a repositoryReference is disabled or removed.
// To avoid potential conflicts the current gateway state is reset by reapplying all other repository references post
// delete
RepositoryReferenceDelete RepositoryReferenceDelete `json:"repositoryReferenceDelete,omitempty"`
RepositoryReferences []RepositoryReference `json:"repositoryReferences,omitempty"`
Ingress Ingress `json:"ingress,omitempty"`
Sidecars []corev1.Container `json:"sidecars,omitempty"`
InitContainers []corev1.Container `json:"initContainers,omitempty"`
Resources PodResources `json:"resources,omitempty"`
Autoscaling Autoscaling `json:"autoscaling,omitempty"`
// ServiceAccount to use for the Gateway Deployment
ServiceAccount ServiceAccount `json:"serviceAccount,omitempty"`
Hazelcast Hazelcast `json:"hazelcast,omitempty"`
Expand Down Expand Up @@ -256,6 +270,40 @@ type App struct {
Otel Otel `json:"otel,omitempty"`
}

// RepositoryReferenceBootstrap facilitates bootstrap of dynamic repo references and the desired source of truth.
type RepositoryReferenceBootstrap struct {
// Enable or disable bootstrapping repository references
Enabled bool `json:"enabled,omitempty"`
// If a L7StateStore is configured the initContainer will default to retrieving configuration from redis over git if a secret is not available (i.e. the repository is greater than 1MB in size)
// this configuration prioritizes git over the L7StateStore configuration to avoid excessive redis egress for large gateway deployments.
PreferGit bool `json:"preferGit,omitempty"`
}

// RepositoryReferenceBootstrap facilitates bootstrap of dynamic repo references and the desired source of truth.
type RepositoryReferenceDelete struct {
// Enable or disable deleting repository references
// by default this only applies to repositories that have a statestore reference
Enabled bool `json:"enabled,omitempty"`
// IncludeEfs we track deltas between repositories on the operators ephemeral filesystem
// setting this to true will enable delete functionality for all repositoryReferences
// USE WITH CAUTION, an operator restart removes the ephemeral filesystem with the state that is tracked there.
// We DO NOT recommend this setting for database backed gateways, ephemeral gateways can be restarted to reset state.
// use mappings instead
IncludeEfs bool `json:"includeEfs,omitempty"`
// ReconcileReferences resets the commits for all other repositories that have been applied
// this triggers a reconcile which replaces any entities that may have overlapped with the repository that was removed.
// example:
// myrepo1 ==> contains cwp1
// myrepo2 ==> also contains a cwp1
// if myrepo1 is deleted cwp1 will be removed. This functionality will then reapply myrepo2 which will reconcile cwp1
ReconcileReferences bool `json:"reconcileReferences,omitempty"`
// ReconcileDirectoryChanges will create and apply mappings if your dynamic repositoryReference folders change.
// Changes will be based on the current commit
// This is not recommended if you are using a database backed gateway
// Use mappings in your repo instead
ReconcileDirectoryChanges bool `json:"reconcileDirectoryChanges,omitempty"`
}

// Otel used when no dedicated OTel agent is present. This enriches the telemetry that the SDK is able to emit to your observability backend
type Otel struct {
OtelSDKOnly OtelSDKOnly `json:"sdkOnly,omitempty"`
Expand Down
40 changes: 24 additions & 16 deletions api/v1/gateway_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,21 @@ 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

import (
"context"
"fmt"
"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"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

Expand All @@ -34,42 +36,48 @@ import (
func (r *Gateway) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(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 _ webhook.Defaulter = &Gateway{}
var _ admission.CustomDefaulter = &Gateway{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *Gateway) Default() {
//gatewaylog.Info("default", "name", r.Name)

// TODO(user): fill in your defaulting logic.

func (r *Gateway) Default(ctx context.Context, obj runtime.Object) error {
return nil
}

// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
//+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 _ webhook.Validator = &Gateway{}
var _ admission.CustomValidator = &Gateway{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *Gateway) ValidateCreate() (admission.Warnings, error) {
return validateGateway(r)
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)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *Gateway) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
_, ok := old.(*Gateway)
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, received %T", r)
return nil, fmt.Errorf("expected a Gateway for newObj, received %T", newObj)
}
return validateGateway(r)
return validateGateway(gateway)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *Gateway) ValidateDelete() (admission.Warnings, error) {
func (r *Gateway) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
//gatewaylog.Info("validate delete", "name", r.Name)
return []string{}, nil
}
Expand Down
6 changes: 1 addition & 5 deletions api/v1/repository_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,10 @@ type RepositoryStatus struct {
// +operator-sdk:csv:customresourcedefinitions:type=status
// +operator-sdk:csv:customresourcedefinitions:displayName="StorageSecretName"
StorageSecretName string `json:"storageSecretName,omitempty"`
// StateStoreVersion tracks version in state store
// +operator-sdk:csv:customresourcedefinitions:type=status
// +operator-sdk:csv:customresourcedefinitions:displayName="StateStoreVersion"
StateStoreVersion int `json:"stateStoreVersion,omitempty"`
// StateStoreSynced whether or not the state store has been written to correctly
// +operator-sdk:csv:customresourcedefinitions:type=status
// +operator-sdk:csv:customresourcedefinitions:displayName="StateStoreVersion"
StateStoreSynced bool `json:"stateStoreSynced,omitempty"`
StateStoreSynced bool `json:"stateStoreSynced"`
}

func init() {
Expand Down
Loading
Loading