From 9a552bcdce3f1a9f09b54049695bbd9ba895247b Mon Sep 17 00:00:00 2001 From: Daniel Fox Date: Wed, 1 Apr 2026 09:07:49 -0700 Subject: [PATCH] Automatic API update from tigera/calico-private v3.23 --- deps.txt | 6 +- embed.go | 53 +++ go.mod | 3 +- go.sum | 2 + lib.Makefile | 373 +++++++++++++-- metadata.mk | 4 +- pkg/apis/projectcalico/v3/bgpconfig.go | 20 + pkg/apis/projectcalico/v3/bgpfilter.go | 206 ++++++++- pkg/apis/projectcalico/v3/bgppeer.go | 13 +- pkg/apis/projectcalico/v3/felixconfig.go | 29 +- .../projectcalico/v3/globalnetworkpolicy.go | 6 +- pkg/apis/projectcalico/v3/hostendpoint.go | 6 +- pkg/apis/projectcalico/v3/ipamblocks.go | 5 +- pkg/apis/projectcalico/v3/ipamconfig.go | 22 + pkg/apis/projectcalico/v3/ippool.go | 8 + pkg/apis/projectcalico/v3/policy_common.go | 12 +- .../v3/stagedglobalnetworkpolicy.go | 5 + .../v3/stagedkubernetesnetworkpolicy.go | 1 + .../projectcalico/v3/stagednetworkpolicy.go | 1 + pkg/apis/projectcalico/v3/tier.go | 24 +- .../projectcalico/v3/zz_generated.deepcopy.go | 240 +++++++++- .../clientset/typed/projectcalico/v3/tier.go | 2 + pkg/openapi/generated.openapi.go | 425 +++++++++++++++++- 23 files changed, 1382 insertions(+), 84 deletions(-) create mode 100644 embed.go diff --git a/deps.txt b/deps.txt index 00bd07b5..4da44464 100644 --- a/deps.txt +++ b/deps.txt @@ -1,6 +1,5 @@ !!! GENERATED FILE, DO NOT EDIT !!! -This file contains the list of modules that this package depends on -in order to trigger CI on changes +Run 'make gen-deps-files' to regenerate. go 1.25.7 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc @@ -38,13 +37,14 @@ gopkg.in/evanphx/json-patch.v4 v4.13.0 gopkg.in/inf.v0 v0.9.1 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.34.4 +k8s.io/apiextensions-apiserver v0.34.4 k8s.io/apimachinery v0.34.4 k8s.io/apiserver v0.34.4 k8s.io/client-go v0.34.4 k8s.io/klog v0.2.0 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 -k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 sigs.k8s.io/randfill v1.0.0 sigs.k8s.io/structured-merge-diff/v6 v6.3.0 diff --git a/embed.go b/embed.go new file mode 100644 index 00000000..54052583 --- /dev/null +++ b/embed.go @@ -0,0 +1,53 @@ +// Copyright (c) 2026 Tigera, Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// 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. + +package api + +import ( + "embed" + "fmt" + "strings" + + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "sigs.k8s.io/yaml" +) + +// crdFiles contains the v3 CRD YAML definitions. +// +//go:embed config/crd/*.yaml +var crdFiles embed.FS + +// AllCRDs loads and returns all embedded v3 CRD definitions. +func AllCRDs() ([]*v1.CustomResourceDefinition, error) { + var crds []*v1.CustomResourceDefinition + entries, err := crdFiles.ReadDir("config/crd") + if err != nil { + return nil, fmt.Errorf("failed to read embedded CRD directory: %w", err) + } + for _, d := range entries { + if d.IsDir() || !strings.HasSuffix(d.Name(), ".yaml") { + continue + } + rawYAML, err := crdFiles.ReadFile("config/crd/" + d.Name()) + if err != nil { + return nil, fmt.Errorf("failed to read embedded CRD %s: %w", d.Name(), err) + } + var crd v1.CustomResourceDefinition + if err := yaml.Unmarshal(rawYAML, &crd); err != nil { + return nil, fmt.Errorf("failed to unmarshal CRD %s: %w", d.Name(), err) + } + crds = append(crds, &crd) + } + return crds, nil +} diff --git a/go.mod b/go.mod index 52228145..44b94cd3 100644 --- a/go.mod +++ b/go.mod @@ -7,10 +7,12 @@ require ( github.com/onsi/ginkgo/v2 v2.28.1 github.com/onsi/gomega v1.39.1 k8s.io/api v0.34.4 + k8s.io/apiextensions-apiserver v0.0.0-00010101000000-000000000000 k8s.io/apimachinery v0.34.4 k8s.io/apiserver v0.34.4 k8s.io/client-go v0.34.4 k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b + sigs.k8s.io/yaml v1.6.0 ) require ( @@ -58,7 +60,6 @@ require ( sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect ) replace ( diff --git a/go.sum b/go.sum index 52f6defe..c1752d47 100644 --- a/go.sum +++ b/go.sum @@ -169,6 +169,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.34.4 h1:Z5hsoQcZ2yBjelb9j5JKzCVo9qv9XLkVm5llnqS4h+0= k8s.io/api v0.34.4/go.mod h1:6SaGYuGPkMqqCgg8rPG/OQoCrhgSEV+wWn9v21fDP3o= +k8s.io/apiextensions-apiserver v0.34.4 h1:TAh2mEduc27sR7lfEthOL2oNeQuux9pQCEJCVC9Gxrs= +k8s.io/apiextensions-apiserver v0.34.4/go.mod h1:13rZ7iu/F4APVV0I0StgBmhvWBGgjGTDaqi21G0113E= k8s.io/apimachinery v0.34.4 h1:C5SiSzLEMyWIk53sSbnk0WlOOyqv/MFnWvuc/d6M+xc= k8s.io/apimachinery v0.34.4/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/apiserver v0.34.4 h1:QmMakuCjlFBJpsXKIUom8OUE7+PhZk7hyNiLqlyDH58= diff --git a/lib.Makefile b/lib.Makefile index 615278bf..5dc33a84 100644 --- a/lib.Makefile +++ b/lib.Makefile @@ -21,28 +21,7 @@ test: ut fv st # The target architecture is select by setting the ARCH variable. # When ARCH is undefined it is set to the detected host architecture. # When ARCH differs from the host architecture a crossbuild will be performed. -# This variable is only set if ARCHES is not set -ARCHES ?= $(patsubst docker-image/Dockerfile.%,%,$(wildcard docker-image/Dockerfile.*)) - -# Some repositories keep their Dockerfile(s) in the sub-directories of the 'docker-image' -# directory (e.g., voltron). Make sure ARCHES gets filled from all unique Dockerfiles. -ifeq ($(ARCHES),) - dockerfiles_in_subdir=$(wildcard docker-image/**/Dockerfile.*) - ifneq ($(dockerfiles_in_subdir),) - ARCHES=$(patsubst Dockerfile.%,%,$(shell basename -a $(dockerfiles_in_subdir) | sort | uniq)) - endif -endif - -# Some repositories keep their Dockerfile(s) in the root directory instead of in -# the 'docker-image' subdir. Make sure ARCHES gets filled in either way. -ifeq ($(ARCHES),) - ARCHES=$(patsubst Dockerfile.%,%,$(wildcard Dockerfile.*)) -endif - -# If architectures cannot infer from Dockerfiles, set default supported architecture. -ifeq ($(ARCHES),) - ARCHES=amd64 arm64 -endif +ARCHES ?= amd64 arm64 # list of arches *not* to build when doing *-all EXCLUDEARCH?=ppc64le s390x @@ -100,6 +79,9 @@ endif # Enable binfmt adding support for miscellaneous binary formats. # This is only needed when running non-native binaries. +# Marked .PHONY since it produces no file; use as an order-only prerequisite +# (| register) to avoid triggering unnecessary rebuilds. +.PHONY: register register: ifneq ($(BUILDARCH),$(ARCH)) docker run --privileged --rm calico/binfmt:qemu-v10.1.3 --install all || true @@ -396,11 +378,10 @@ DOCKER_PULL = endif # DOCKER_BUILD is the base build command used for building all images. -DOCKER_BUILD=docker buildx build --load --platform=linux/$(ARCH) $(DOCKER_PULL)\ - --build-arg UBI_IMAGE=$(UBI_IMAGE) \ - --build-arg GIT_VERSION=$(GIT_VERSION) \ +DOCKER_BUILD=docker buildx build --load --platform=linux/$(ARCH) $(DOCKER_PULL) \ --build-arg CALICO_BASE=$(CALICO_BASE) \ - --build-arg BPFTOOL_IMAGE=$(BPFTOOL_IMAGE) + --build-arg GIT_VERSION=$(GIT_VERSION) \ + --build-arg UBI_IMAGE=$(UBI_IMAGE) DOCKER_BUILD_THIRD_PARTY = $(DOCKER_BUILD) \ --build-arg THIRD_PARTY_REGISTRY=$(THIRD_PARTY_REGISTRY) \ @@ -505,6 +486,29 @@ define host_native_deb_build sh -c 'cd package/$(1)/$(2) && debuild -us -uc -b' endef +############################################################################### +# Source file dependency tracking via deps.txt +# +# Each component's deps.txt contains both external module dependencies and +# local in-repo package directories (prefixed with "local:"). If deps.txt +# has local entries, SRC_FILES is automatically populated with all .go files +# in those directories. Components can append extra non-Go dependencies +# (e.g., BPF .c/.h files) after including lib.Makefile. +# +# IMAGE_DEPS lists non-Go files that the Docker image depends on (Dockerfiles, +# config templates, scripts, etc.). Components should override or append to +# this variable and include $(IMAGE_DEPS) in their .image.created prereqs. +############################################################################### +ifneq ($(wildcard deps.txt),) +SRC_FILES := $(shell find $(addprefix $(REPO_ROOT)/,$(shell grep '^local:' deps.txt | cut -d: -f2-)) -name '*.go' 2>/dev/null) +endif + +IMAGE_DEPS ?= Dockerfile + +# Expand a component's deps.txt local entries to the list of .go files. +# Usage: $(call local-deps-go-files,) +local-deps-go-files = $(shell find $(addprefix $(REPO_ROOT)/,$(shell grep '^local:' $(REPO_ROOT)/$(1)/deps.txt | cut -d: -f2-)) -name '*.go' 2>/dev/null) + # A target that does nothing but it always stale, used to force a rebuild on certain targets based on some non-file criteria. .PHONY: force-rebuild force-rebuild: @@ -843,7 +847,7 @@ trigger-auto-pin-update-process-wrapped: create-pin-update-head trigger-pin-upda static-checks: $(LOCAL_CHECKS) $(MAKE) golangci-lint -LINT_ARGS ?= --max-issues-per-linter 0 --max-same-issues 0 --timeout 8m +LINT_ARGS ?= --max-issues-per-linter 0 --max-same-issues 0 --timeout 15m .PHONY: golangci-lint golangci-lint: $(GENERATED_FILES) @@ -989,7 +993,7 @@ SEMAPHORE_TYPHA_PROJECT_ID=c2ea3f0a-58a0-427a-9ed5-6eff8d6543b3 SEMAPHORE_TYPHA_PRIVATE_PROJECT_ID=51e84cb9-0f38-408a-a113-0f5ca71844d7 SEMAPHORE_VOLTRON_PROJECT_ID=9d239362-9594-4c84-8983-868ee19ebd41 -SEMAPHORE_WORKFLOW_BRANCH?=master +SEMAPHORE_WORKFLOW_BRANCH?=release-calient-v3.23 # Sends a request to the semaphore API to run the request workflow. It requires setting the SEMAPHORE_API_TOKEN, SEMAPHORE_PROJECT_ID, # SEMAPHORE_WORKFLOW_BRANCH, and SEMAPHORE_WORKFLOW_FILE ENV variables. @@ -1601,7 +1605,7 @@ $(REPO_ROOT)/.$(KIND_NAME).created: $(KUBECTL) $(KIND) touch $@ -kind-cluster-destroy: $(KIND) $(KUBECTL) +kind-cluster-destroy kind-down: $(KIND) $(KUBECTL) # We need to drain the cluster gracefully when shutting down to avoid a netdev unregister error from the kernel. # This requires we execute CNI del on pods with pod networking. -$(KIND) delete cluster --name $(KIND_NAME) @@ -1678,6 +1682,317 @@ bin/yq: curl -sSfL --retry 5 https://github.com/mikefarah/yq/releases/download/v4.44.6/yq_linux_$(BUILDARCH).tar.gz | tar xz -C bin ./yq_linux_$(BUILDARCH) && \ mv bin/yq_linux_$(BUILDARCH) bin/yq +############################################################################### +# Common functions for setting up a kind cluster with Calico for testing. +############################################################################### +KIND_INFRA_DIR := $(REPO_ROOT)/hack/test/kind/infra +KIND_TEST_BUILD_TAG = test-build + +# Calico images built locally with latest-$(ARCH) tags. kind-build-images +# re-tags each as :test-build for the kind cluster. +KIND_CALICO_IMAGES = \ + tigera/node:$(KIND_TEST_BUILD_TAG) \ + tigera/typha:$(KIND_TEST_BUILD_TAG) \ + tigera/apiserver:$(KIND_TEST_BUILD_TAG) \ + tigera/calicoctl:$(KIND_TEST_BUILD_TAG) \ + tigera/cni:$(KIND_TEST_BUILD_TAG) \ + tigera/csi:$(KIND_TEST_BUILD_TAG) \ + tigera/node-driver-registrar:$(KIND_TEST_BUILD_TAG) \ + tigera/pod2daemon-flexvol:$(KIND_TEST_BUILD_TAG) \ + tigera/kube-controllers:$(KIND_TEST_BUILD_TAG) \ + tigera/webhooks:$(KIND_TEST_BUILD_TAG) + +# Operator is built separately (build-operator.sh tags it directly as +# :test-build), so it's not in KIND_CALICO_IMAGES. +KIND_OPERATOR_IMAGE = docker.io/tigera/operator:$(KIND_TEST_BUILD_TAG) + +# All images loaded onto the kind cluster. +KIND_IMAGES = $(KIND_OPERATOR_IMAGE) $(KIND_CALICO_IMAGES) + +# Enterprise-only: paths to license and pull secret for kind cluster setup. +GCR_IO_PULL_SECRET ?= $(HOME)/secrets/docker_cfg.json +TSEE_TEST_LICENSE ?= $(HOME)/secrets/license.yaml + +# Enterprise-only images (all images loaded onto the kind cluster). +# Enterprise images built locally with latest-$(ARCH) tags. kind-build-images +# re-tags each as :test-build for the kind cluster. +KIND_ENTERPRISE_LOCAL_IMAGES = \ + tigera/egress-gateway:$(KIND_TEST_BUILD_TAG) \ + tigera/queryserver:$(KIND_TEST_BUILD_TAG) \ + tigera/prometheus-service:$(KIND_TEST_BUILD_TAG) \ + tigera/fluentd:$(KIND_TEST_BUILD_TAG) \ + tigera/policy-recommendation:$(KIND_TEST_BUILD_TAG) \ + tigera/voltron:$(KIND_TEST_BUILD_TAG) \ + tigera/ui-apis:$(KIND_TEST_BUILD_TAG) \ + tigera/es-gateway:$(KIND_TEST_BUILD_TAG) \ + tigera/linseed:$(KIND_TEST_BUILD_TAG) \ + tigera/intrusion-detection-controller:$(KIND_TEST_BUILD_TAG) \ + tigera/webhooks-processor:$(KIND_TEST_BUILD_TAG) \ + tigera/intrusion-detection-job-installer:$(KIND_TEST_BUILD_TAG) \ + tigera/elasticsearch-metrics:$(KIND_TEST_BUILD_TAG) \ + tigera/prometheus-operator:$(KIND_TEST_BUILD_TAG) \ + tigera/prometheus-config-reloader:$(KIND_TEST_BUILD_TAG) \ + tigera/eck-operator:$(KIND_TEST_BUILD_TAG) + +# Enterprise images pulled from a registry. Stamp rules pull and tag as +# latest-$(ARCH); the common tagging loop handles the final tag. +KIND_ENTERPRISE_PULLED_IMAGES = \ + tigera/prometheus:$(KIND_TEST_BUILD_TAG) \ + tigera/alertmanager:$(KIND_TEST_BUILD_TAG) \ + tigera/manager:$(KIND_TEST_BUILD_TAG) \ + tigera/kibana:$(KIND_TEST_BUILD_TAG) \ + tigera/elasticsearch:$(KIND_TEST_BUILD_TAG) + +# All enterprise images = locally built + pulled from registry. +KIND_CALICO_ENTERPRISE_IMAGES = $(KIND_ENTERPRISE_LOCAL_IMAGES) $(KIND_ENTERPRISE_PULLED_IMAGES) + +KIND_IMAGES += $(KIND_CALICO_ENTERPRISE_IMAGES) + +# .image.created markers: the per-component image build stamp files. +# Each depends on its source files via deps.txt so Make knows when +# to rebuild. The sub-make handles the actual build; we just ensure it +# runs when sources are newer. +KIND_IMAGE_MARKERS = \ + $(REPO_ROOT)/node/.image.created-$(ARCH) \ + $(REPO_ROOT)/typha/.image.created-$(ARCH) \ + $(REPO_ROOT)/apiserver/.image.created-$(ARCH) \ + $(REPO_ROOT)/cni-plugin/.image.created-$(ARCH) \ + $(REPO_ROOT)/pod2daemon/.image.created-$(ARCH) \ + $(REPO_ROOT)/calicoctl/.image.created-$(ARCH) \ + $(REPO_ROOT)/kube-controllers/.image.created-$(ARCH) \ + $(REPO_ROOT)/webhooks/.image.created-$(ARCH) + +$(REPO_ROOT)/node/.image.created-$(ARCH): $(call local-deps-go-files,node) + $(MAKE) -C $(REPO_ROOT)/node image + +$(REPO_ROOT)/typha/.image.created-$(ARCH): $(call local-deps-go-files,typha) + $(MAKE) -C $(REPO_ROOT)/typha image + +$(REPO_ROOT)/apiserver/.image.created-$(ARCH): $(call local-deps-go-files,apiserver) + $(MAKE) -C $(REPO_ROOT)/apiserver image + +$(REPO_ROOT)/cni-plugin/.image.created-$(ARCH): $(call local-deps-go-files,cni-plugin) + $(MAKE) -C $(REPO_ROOT)/cni-plugin image + +$(REPO_ROOT)/pod2daemon/.image.created-$(ARCH): $(call local-deps-go-files,pod2daemon) + $(MAKE) -C $(REPO_ROOT)/pod2daemon image + +$(REPO_ROOT)/calicoctl/.image.created-$(ARCH): $(call local-deps-go-files,calicoctl) + $(MAKE) -C $(REPO_ROOT)/calicoctl image + +$(REPO_ROOT)/kube-controllers/.image.created-$(ARCH): $(call local-deps-go-files,kube-controllers) + $(MAKE) -C $(REPO_ROOT)/kube-controllers image + +$(REPO_ROOT)/webhooks/.image.created-$(ARCH): $(call local-deps-go-files,webhooks) + $(MAKE) -C $(REPO_ROOT)/webhooks image + +# Operator is built from a separate repo/branch and depends on all other +# images being built first. +$(REPO_ROOT)/.stamp.operator: $(KIND_IMAGE_MARKERS) $(KIND_INFRA_DIR)/calico_versions.yml + cd $(KIND_INFRA_DIR) && BRANCH=$(OPERATOR_BRANCH) ./build-operator.sh + touch $@ + +# Enterprise-only image markers for locally-built components. These follow +# the same pattern as KIND_IMAGE_MARKERS: source deps so Make knows when +# to rebuild. Components with deps.txt use local-deps-go-files; third_party +# components without deps.txt approximate with find. +KIND_ENTERPRISE_IMAGE_MARKERS = \ + $(REPO_ROOT)/egress-gateway/.image.created-$(ARCH) \ + $(REPO_ROOT)/queryserver/.image.created-$(ARCH) \ + $(REPO_ROOT)/prometheus-service/.image.created-$(ARCH) \ + $(REPO_ROOT)/fluentd/.image.created-$(ARCH) \ + $(REPO_ROOT)/policy-recommendation/.image.created-$(ARCH) \ + $(REPO_ROOT)/voltron/.image.created-$(ARCH) \ + $(REPO_ROOT)/ui-apis/.image.created-$(ARCH) \ + $(REPO_ROOT)/es-gateway/.image.created-$(ARCH) \ + $(REPO_ROOT)/linseed/.image.created-$(ARCH) \ + $(REPO_ROOT)/intrusion-detection-controller/.image.created-$(ARCH) \ + $(REPO_ROOT)/webhooks-processor/.image.created-$(ARCH) \ + $(REPO_ROOT)/intrusion-detection-controller/.dashboards-installer.image.created-$(ARCH) \ + $(REPO_ROOT)/elasticsearch-metrics/.image.created-$(ARCH) \ + $(REPO_ROOT)/third_party/prometheus-operator/.prometheus-operator.created-$(ARCH) \ + $(REPO_ROOT)/third_party/prometheus-operator/.prometheus-config-reloader.created-$(ARCH) \ + $(REPO_ROOT)/third_party/eck-operator/.eck-operator.created-$(ARCH) + +# Enterprise-only markers for images pulled from a registry. These have +# no local source deps and are pulled once per stamp lifetime. +KIND_ENTERPRISE_PULLED_IMAGE_MARKERS = \ + $(REPO_ROOT)/.stamp.prometheus \ + $(REPO_ROOT)/.stamp.alertmanager \ + $(REPO_ROOT)/.stamp.manager \ + $(REPO_ROOT)/.stamp.kibana \ + $(REPO_ROOT)/.stamp.elastic + +## Build all component images needed for kind cluster testing, then tag them. +.PHONY: kind-build-images +kind-build-images: $(KIND_IMAGE_MARKERS) $(REPO_ROOT)/.stamp.operator $(KIND_ENTERPRISE_IMAGE_MARKERS) $(KIND_ENTERPRISE_PULLED_IMAGE_MARKERS) + @for img in $(KIND_CALICO_IMAGES); do \ + base=$${img%%:*}; \ + docker tag $$base:latest-$(ARCH) $$img; \ + done + @for img in $(KIND_CALICO_ENTERPRISE_IMAGES); do \ + base=$${img%%:*}; \ + docker tag $$base:latest-$(ARCH) $$img; \ + done + +# Create a kind cluster and deploy Calico on it via Helm. Assumes images are +# already built and tagged as test-build in the local Docker daemon. If a +# cluster already exists (stamp file present), the creation step is skipped. +# Default to v3 CRDs for kind clusters. Override with KIND_CALICO_API_GROUP=crd.projectcalico.org/v1 if needed. +KIND_CALICO_API_GROUP ?= projectcalico.org/v3 + +# Load images, install Calico via Helm, and wait for readiness on an existing +# kind cluster. Use kind-up for end-to-end bringup (images + cluster + deploy). +.PHONY: kind-deploy +kind-deploy: + $(MAKE) -C $(REPO_ROOT) chart CALICO_API_GROUP=$(KIND_CALICO_API_GROUP) + REPO_ROOT=$(REPO_ROOT) \ + KUBECONFIG=$(KIND_KUBECONFIG) \ + KIND=$(KIND) \ + KIND_NAME=$(KIND_NAME) \ + ARCH=$(ARCH) \ + GIT_VERSION=$(GIT_VERSION) \ + CALICO_API_GROUP=$(KIND_CALICO_API_GROUP) \ + CLUSTER_ROUTING=$(CLUSTER_ROUTING) \ + TSEE_TEST_LICENSE=$(TSEE_TEST_LICENSE) \ + GCR_IO_PULL_SECRET=$(GCR_IO_PULL_SECRET) \ + KIND_IMAGES="$(KIND_IMAGES)" \ + $(REPO_ROOT)/hack/test/kind/deploy_resources.sh + +# Rebuild any images whose source files have changed, load onto the kind +# cluster, and restart pods. +.PHONY: kind-reload +kind-reload: kind-build-images + KIND=$(KIND) KIND_NAME=$(KIND_NAME) $(REPO_ROOT)/hack/test/kind/load_images.sh $(KIND_IMAGES) + KUBECONFIG=$(KIND_KUBECONFIG) $(KUBECTL) delete pods -n calico-system --all + KUBECONFIG=$(KIND_KUBECONFIG) $(KUBECTL) apply -f $(KIND_INFRA_DIR)/calicoctl.yaml + +# Enterprise-only image build rules. + +# Locally-built enterprise components with deps.txt: these use the same +# .image.created pattern as OSS components so Make tracks source changes. +$(REPO_ROOT)/egress-gateway/.image.created-$(ARCH): $(call local-deps-go-files,egress-gateway) + $(MAKE) -C $(REPO_ROOT)/egress-gateway image + +$(REPO_ROOT)/queryserver/.image.created-$(ARCH): $(call local-deps-go-files,queryserver) + $(MAKE) -C $(REPO_ROOT)/queryserver image + +$(REPO_ROOT)/prometheus-service/.image.created-$(ARCH): $(call local-deps-go-files,prometheus-service) + $(MAKE) -C $(REPO_ROOT)/prometheus-service image + +$(REPO_ROOT)/fluentd/.image.created-$(ARCH): $(call local-deps-go-files,fluentd) + $(MAKE) -C $(REPO_ROOT)/fluentd image THIRD_PARTY_REGISTRY=$(THIRD_PARTY_REGISTRY_CD) + +$(REPO_ROOT)/policy-recommendation/.image.created-$(ARCH): $(call local-deps-go-files,policy-recommendation) + $(MAKE) -C $(REPO_ROOT)/policy-recommendation image + +$(REPO_ROOT)/voltron/.image.created-$(ARCH): $(call local-deps-go-files,voltron) + $(MAKE) -C $(REPO_ROOT)/voltron image + +$(REPO_ROOT)/ui-apis/.image.created-$(ARCH): $(call local-deps-go-files,ui-apis) + $(MAKE) -C $(REPO_ROOT)/ui-apis image + +$(REPO_ROOT)/es-gateway/.image.created-$(ARCH): $(call local-deps-go-files,es-gateway) + $(MAKE) -C $(REPO_ROOT)/es-gateway image + +$(REPO_ROOT)/linseed/.image.created-$(ARCH): $(call local-deps-go-files,linseed) + $(MAKE) -C $(REPO_ROOT)/linseed image + +$(REPO_ROOT)/intrusion-detection-controller/.image.created-$(ARCH): $(call local-deps-go-files,intrusion-detection-controller) + $(MAKE) -C $(REPO_ROOT)/intrusion-detection-controller image + +$(REPO_ROOT)/webhooks-processor/.image.created-$(ARCH): $(call local-deps-go-files,webhooks-processor) + $(MAKE) -C $(REPO_ROOT)/webhooks-processor image + +$(REPO_ROOT)/intrusion-detection-controller/.dashboards-installer.image.created-$(ARCH): $(call local-deps-go-files,intrusion-detection-controller) + $(MAKE) -C $(REPO_ROOT)/intrusion-detection-controller intrusion-detection-job-installer + +$(REPO_ROOT)/elasticsearch-metrics/.image.created-$(ARCH): $(call local-deps-go-files,elasticsearch-metrics) + $(MAKE) -C $(REPO_ROOT)/elasticsearch-metrics image + +# Third-party locally-built images: use find for source deps since these +# don't have deps.txt. The component Makefiles create the marker files. +$(REPO_ROOT)/third_party/prometheus-operator/.prometheus-operator.created-$(ARCH): $(shell find $(REPO_ROOT)/third_party/prometheus-operator -name '*.go') + $(MAKE) -C $(REPO_ROOT)/third_party/prometheus-operator image + +$(REPO_ROOT)/third_party/prometheus-operator/.prometheus-config-reloader.created-$(ARCH): $(shell find $(REPO_ROOT)/third_party/prometheus-operator -name '*.go') + $(MAKE) -C $(REPO_ROOT)/third_party/prometheus-operator image + +$(REPO_ROOT)/third_party/eck-operator/.eck-operator.created-$(ARCH): $(shell find $(REPO_ROOT)/third_party/eck-operator -name '*.go') + $(MAKE) -C $(REPO_ROOT)/third_party/eck-operator image + +# GCR-pulled images: no local source deps, use stamp files. Tag as +# latest-$(ARCH) so the common tagging loop handles the final tag. +$(REPO_ROOT)/.stamp.prometheus: + # TODO: Build this. We cannot at the moment because it relies on the host OS npm / go toolchain. + docker pull gcr.io/unique-caldron-775/cnx/tigera/prometheus:$(THIRD_PARTY_RELEASE_BRANCH) + docker tag gcr.io/unique-caldron-775/cnx/tigera/prometheus:$(THIRD_PARTY_RELEASE_BRANCH) tigera/prometheus:latest-$(ARCH) + touch $@ + +$(REPO_ROOT)/.stamp.alertmanager: + # TODO: Build this. + docker pull gcr.io/unique-caldron-775/cnx/tigera/alertmanager:$(THIRD_PARTY_RELEASE_BRANCH) + docker tag gcr.io/unique-caldron-775/cnx/tigera/alertmanager:$(THIRD_PARTY_RELEASE_BRANCH) tigera/alertmanager:latest-$(ARCH) + touch $@ + +$(REPO_ROOT)/.stamp.manager: + # TODO + docker pull gcr.io/unique-caldron-775/cnx/tigera/manager:$(THIRD_PARTY_RELEASE_BRANCH) + docker tag gcr.io/unique-caldron-775/cnx/tigera/manager:$(THIRD_PARTY_RELEASE_BRANCH) tigera/manager:latest-$(ARCH) + touch $@ + +$(REPO_ROOT)/.stamp.kibana: + # TODO: Can we run without kibana? Requires operator change. + docker pull gcr.io/unique-caldron-775/cnx/tigera/kibana:$(THIRD_PARTY_RELEASE_BRANCH) + docker tag gcr.io/unique-caldron-775/cnx/tigera/kibana:$(THIRD_PARTY_RELEASE_BRANCH) tigera/kibana:latest-$(ARCH) + touch $@ + +$(REPO_ROOT)/.stamp.elastic: + # TODO: Build this. + docker pull gcr.io/unique-caldron-775/cnx/tigera/elasticsearch:$(THIRD_PARTY_RELEASE_BRANCH) + docker tag gcr.io/unique-caldron-775/cnx/tigera/elasticsearch:$(THIRD_PARTY_RELEASE_BRANCH) tigera/elasticsearch:latest-$(ARCH) + touch $@ + +############################################################################### +# Common functions for setting up a local envtest environment. +############################################################################### +ENVTEST_DIR := $(REPO_ROOT)/hack/test/envtest +ENVTEST_CONTAINER_DIR := /go/src/github.com/projectcalico/calico/hack/test/envtest +# Derive major.minor from K8S_VERSION (e.g. v1.34.3 -> 1.34.x) for setup-envtest. +# Envtest publishes binaries per minor version, not per patch, so we use a wildcard. +ENVTEST_K8S_VERSION ?= $(shell echo $(K8S_VERSION) | sed 's/^v//' | cut -d. -f1,2).x +ENVTEST_ASSETS_MARKER := $(ENVTEST_DIR)/.envtest-$(ENVTEST_K8S_VERSION) + +## Download envtest binaries (kube-apiserver, etcd) for use by tests that use controller-runtime envtest. +.PHONY: setup-envtest +setup-envtest: $(ENVTEST_ASSETS_MARKER) +$(ENVTEST_ASSETS_MARKER): + @echo "Setting up envtest binaries for Kubernetes $(ENVTEST_K8S_VERSION)..." + mkdir -p $(ENVTEST_DIR) + rm -f $(ENVTEST_DIR)/.envtest-* + $(DOCKER_GO_BUILD) sh -c \ + 'go run sigs.k8s.io/controller-runtime/tools/setup-envtest@latest \ + use --bin-dir $(ENVTEST_CONTAINER_DIR) -p path $(ENVTEST_K8S_VERSION)' + touch $@ + +# Minimum supported Kubernetes version for CEL XValidation (GA in 1.29). +MIN_K8S_VERSION ?= v1.29.0 +ENVTEST_MIN_K8S_VERSION ?= $(shell echo $(MIN_K8S_VERSION) | sed 's/^v//' | cut -d. -f1,2).x +# Major.minor prefix for globbing the downloaded envtest directory (e.g. "1.29"). +ENVTEST_MIN_K8S_MINOR := $(shell echo $(MIN_K8S_VERSION) | sed 's/^v//' | cut -d. -f1,2) +ENVTEST_MIN_ASSETS_MARKER := $(ENVTEST_DIR)/.envtest-min-$(ENVTEST_MIN_K8S_VERSION) + +## Download envtest binaries for the minimum supported Kubernetes version. +.PHONY: setup-envtest-min +setup-envtest-min: $(ENVTEST_MIN_ASSETS_MARKER) +$(ENVTEST_MIN_ASSETS_MARKER): + @echo "Setting up envtest binaries for minimum K8s $(ENVTEST_MIN_K8S_VERSION)..." + mkdir -p $(ENVTEST_DIR) + $(DOCKER_GO_BUILD) sh -c \ + 'go run sigs.k8s.io/controller-runtime/tools/setup-envtest@latest \ + use --bin-dir $(ENVTEST_CONTAINER_DIR) -p path $(ENVTEST_MIN_K8S_VERSION)' + touch $@ + ############################################################################### # Common functions for launching a local etcd instance. ############################################################################### diff --git a/metadata.mk b/metadata.mk index c686fc11..95d65d32 100644 --- a/metadata.mk +++ b/metadata.mk @@ -94,11 +94,11 @@ LIBBPF_VERSION=v1.6.2 BPFTOOL_IMAGE=calico/bpftool:v7.5.0 # The operator branch corresponding to this branch. -OPERATOR_BRANCH ?= master +OPERATOR_BRANCH ?= release-v1.42 OPERATOR_ORGANIZATION ?= tigera OPERATOR_GIT_REPO ?= operator # The manager branch corresponding to this branch. -MANAGER_BRANCH ?= master +MANAGER_BRANCH ?= release-calient-v3.23 # quay.io expiry time for hashrelease/dev images QUAY_EXPIRE_DAYS=90 diff --git a/pkg/apis/projectcalico/v3/bgpconfig.go b/pkg/apis/projectcalico/v3/bgpconfig.go index 9468a5ba..9cf50208 100644 --- a/pkg/apis/projectcalico/v3/bgpconfig.go +++ b/pkg/apis/projectcalico/v3/bgpconfig.go @@ -66,6 +66,8 @@ const ( ) // BGPConfigurationSpec contains the values of the BGP configuration. +// +kubebuilder:validation:XValidation:rule="!has(self.nodeMeshPassword) || !has(self.nodeToNodeMeshEnabled) || self.nodeToNodeMeshEnabled == true",message="nodeMeshPassword cannot be set when nodeToNodeMeshEnabled is false",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="!has(self.nodeMeshMaxRestartTime) || !has(self.nodeToNodeMeshEnabled) || self.nodeToNodeMeshEnabled == true",message="nodeMeshMaxRestartTime cannot be set when nodeToNodeMeshEnabled is false",reason=FieldValueForbidden type BGPConfigurationSpec struct { // LogSeverityScreen is the log severity above which logs are sent to the stdout. [Default: Info] // +kubebuilder:default=Info @@ -157,6 +159,24 @@ type BGPConfigurationSpec struct { // +kubebuilder:validation:Enum=Enabled;Disabled // +optional ProgramClusterRoutes *string `json:"programClusterRoutes,omitempty"` + + // IPv4NormalRoutePriority is the normal route priority (metric) that Felix uses for IPv4 + // workload routes. This must match the value configured in FelixConfiguration. BIRD uses + // this to identify elevated-priority routes during live migration and to override local + // workload routes with higher-priority BGP-learned routes. [Default: 1024] + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=2147483646 + // +optional + IPv4NormalRoutePriority *int `json:"ipv4NormalRoutePriority,omitempty" validate:"omitempty,gte=1,lte=2147483646"` + + // IPv6NormalRoutePriority is the normal route priority (metric) that Felix uses for IPv6 + // workload routes. This must match the value configured in FelixConfiguration. BIRD uses + // this to identify elevated-priority routes during live migration and to override local + // workload routes with higher-priority BGP-learned routes. [Default: 1024] + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=2147483646 + // +optional + IPv6NormalRoutePriority *int `json:"ipv6NormalRoutePriority,omitempty" validate:"omitempty,gte=1,lte=2147483646"` } // ServiceLoadBalancerIPBlock represents a single allowed LoadBalancer IP CIDR block. diff --git a/pkg/apis/projectcalico/v3/bgpfilter.go b/pkg/apis/projectcalico/v3/bgpfilter.go index 92e93de0..141c03a0 100644 --- a/pkg/apis/projectcalico/v3/bgpfilter.go +++ b/pkg/apis/projectcalico/v3/bgpfilter.go @@ -15,6 +15,7 @@ package v3 import ( + "github.com/tigera/api/pkg/lib/numorstring" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -61,38 +62,156 @@ type BGPFilterSpec struct { ImportV6 []BGPFilterRuleV6 `json:"importV6,omitempty" validate:"omitempty,dive"` } -// BGPFilterRuleV4 defines a BGP filter rule consisting a single IPv4 CIDR block and a filter action for this CIDR. +// BGPFilterRuleV4 defines a BGP filter rule consisting of match criteria, a terminal action, +// and optional operations to apply to matching routes. // +mapType=atomic +// +kubebuilder:validation:XValidation:rule="(has(self.cidr) && size(self.cidr) > 0) == (has(self.matchOperator) && size(self.matchOperator) > 0)",message="cidr and matchOperator must both be set or both be empty",reason=FieldValueInvalid +// +kubebuilder:validation:XValidation:rule="!has(self.prefixLength) || (has(self.cidr) && size(self.cidr) > 0)",message="cidr is required when prefixLength is set",reason=FieldValueInvalid +// +kubebuilder:validation:XValidation:rule="!has(self.operations) || size(self.operations) == 0 || self.action == 'Accept'",message="operations may only be used with action Accept" type BGPFilterRuleV4 struct { + // If non-empty, this filter rule will only apply when the route being exported or imported + // "matches" the given CIDR - where the definition of "matches" is according to + // MatchOperator and PrefixLength. CIDR should be in conventional CIDR notation, + // /. // +kubebuilder:validation:Format=cidr + // +kubebuilder:validation:MaxLength=18 CIDR string `json:"cidr,omitempty" validate:"omitempty,netv4"` + // PrefixLength further constrains the CIDR match by restricting the range of allowed + // prefix lengths. For example, CIDR "10.0.0.0/8" with MatchOperator "In" and + // PrefixLength {min: 16, max: 24} matches any route within 10.0.0.0/8 whose prefix + // length is between /16 and /24. Requires CIDR to be set; if CIDR is omitted, + // PrefixLength is ignored. If PrefixLength is nil and CIDR is set, the CIDR's own + // prefix length is used as the minimum and /32 (for V4) as the maximum. + // +optional PrefixLength *BGPFilterPrefixLengthV4 `json:"prefixLength,omitempty" validate:"omitempty"` + // If set to "RemotePeers": for export rules, this filter rule will only apply to routes + // learned from BGP peers (i.e. re-advertised routes), not locally originated routes. + // For import rules, this field is redundant because imported routes are by definition + // from BGP peers. Source BGPFilterMatchSource `json:"source,omitempty" validate:"omitempty,oneof=RemotePeers"` + // If non-empty, this filter rule will only apply to routes with an outgoing interface that + // matches Interface. Interface string `json:"interface,omitempty" validate:"omitempty,bgpFilterInterface"` + // MatchOperator defines how the route's prefix is compared against CIDR. "Equal" requires + // an exact prefix match, "In" requires the route to be contained within the CIDR (or equal), + // "NotEqual" and "NotIn" are their negations. Only meaningful when CIDR is also specified. + // Required when CIDR is set. MatchOperator BGPFilterMatchOperator `json:"matchOperator,omitempty" validate:"omitempty,matchOperator"` + // If non-empty, this filter rule will only apply to routes being imported from or exported + // to a BGP peer of the specified type. If empty, the rule applies to all peers. + // +optional + PeerType BGPFilterPeerType `json:"peerType,omitempty" validate:"omitempty,oneof=eBGP iBGP"` + + // If set, this filter rule will only apply to routes that carry the specified BGP + // community. On import, this matches communities set by the remote peer. On export, + // this matches communities already present on the route, whether received from a BGP + // peer (e.g. on a route reflector re-advertising to an eBGP peer) or added locally + // by an import filter or an earlier export rule's AddCommunity operation. + // +optional + Communities *BGPFilterCommunityMatch `json:"communities,omitempty" validate:"omitempty"` + + // If non-empty, this filter rule will only apply to routes whose AS path begins with the + // specified sequence of AS numbers. + // +optional + ASPathPrefix []numorstring.ASNumber `json:"asPathPrefix,omitempty" validate:"omitempty"` + + // If set, this filter rule will only apply to routes with the given priority, in the + // same units as the ...RoutePriority fields in FelixConfiguration. + // +optional + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=2147483646 + Priority *int `json:"priority,omitempty" validate:"omitempty,gte=1,lte=2147483646"` + Action BGPFilterAction `json:"action" validate:"required,filterAction"` + + // Operations is an ordered list of route modifications to apply to matching routes before + // accepting them. Only valid when Action is "Accept"; specifying operations with "Reject" + // is rejected by validation. Each entry must set exactly one operation field. + // +optional + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + Operations []BGPFilterOperation `json:"operations,omitempty" validate:"omitempty,dive"` } -// BGPFilterRuleV6 defines a BGP filter rule consisting a single IPv6 CIDR block and a filter action for this CIDR. +// BGPFilterRuleV6 defines a BGP filter rule consisting of match criteria, a terminal action, +// and optional operations to apply to matching routes. // +mapType=atomic +// +kubebuilder:validation:XValidation:rule="(has(self.cidr) && size(self.cidr) > 0) == (has(self.matchOperator) && size(self.matchOperator) > 0)",message="cidr and matchOperator must both be set or both be empty",reason=FieldValueInvalid +// +kubebuilder:validation:XValidation:rule="!has(self.prefixLength) || (has(self.cidr) && size(self.cidr) > 0)",message="cidr is required when prefixLength is set",reason=FieldValueInvalid +// +kubebuilder:validation:XValidation:rule="!has(self.operations) || size(self.operations) == 0 || self.action == 'Accept'",message="operations may only be used with action Accept" type BGPFilterRuleV6 struct { + // If non-empty, this filter rule will only apply when the route being exported or imported + // "matches" the given CIDR - where the definition of "matches" is according to + // MatchOperator and PrefixLength. CIDR should be in conventional CIDR notation, + // /. // +kubebuilder:validation:Format=cidr + // +kubebuilder:validation:MaxLength=43 CIDR string `json:"cidr,omitempty" validate:"omitempty,netv6"` + // PrefixLength further constrains the CIDR match by restricting the range of allowed + // prefix lengths. For example, CIDR "fd00::/8" with MatchOperator "In" and + // PrefixLength {min: 48, max: 64} matches any route within fd00::/8 whose prefix + // length is between /48 and /64. Requires CIDR to be set; if CIDR is omitted, + // PrefixLength is ignored. If PrefixLength is nil and CIDR is set, the CIDR's own + // prefix length is used as the minimum and /128 (for V6) as the maximum. + // +optional PrefixLength *BGPFilterPrefixLengthV6 `json:"prefixLength,omitempty" validate:"omitempty"` + // If set to "RemotePeers": for export rules, this filter rule will only apply to routes + // learned from BGP peers (i.e. re-advertised routes), not locally originated routes. + // For import rules, this field is redundant because imported routes are by definition + // from BGP peers. Source BGPFilterMatchSource `json:"source,omitempty" validate:"omitempty,oneof=RemotePeers"` + // If non-empty, this filter rule will only apply to routes with an outgoing interface that + // matches Interface. Interface string `json:"interface,omitempty" validate:"omitempty,bgpFilterInterface"` + // MatchOperator defines how the route's prefix is compared against CIDR. "Equal" requires + // an exact prefix match, "In" requires the route to be contained within the CIDR (or equal), + // "NotEqual" and "NotIn" are their negations. Only meaningful when CIDR is also specified. + // Required when CIDR is set. MatchOperator BGPFilterMatchOperator `json:"matchOperator,omitempty" validate:"omitempty,matchOperator"` + // If non-empty, this filter rule will only apply to routes being imported from or exported + // to a BGP peer of the specified type. If empty, the rule applies to all peers. + // +optional + PeerType BGPFilterPeerType `json:"peerType,omitempty" validate:"omitempty,oneof=eBGP iBGP"` + + // If set, this filter rule will only apply to routes that carry the specified BGP + // community. On import, this matches communities set by the remote peer. On export, + // this matches communities already present on the route, whether received from a BGP + // peer (e.g. on a route reflector re-advertising to an eBGP peer) or added locally + // by an import filter or an earlier export rule's AddCommunity operation. + // +optional + Communities *BGPFilterCommunityMatch `json:"communities,omitempty" validate:"omitempty"` + + // If non-empty, this filter rule will only apply to routes whose AS path begins with the + // specified sequence of AS numbers. + // +optional + ASPathPrefix []numorstring.ASNumber `json:"asPathPrefix,omitempty" validate:"omitempty"` + + // If set, this filter rule will only apply to routes with the given priority, in the + // same units as the ...RoutePriority fields in FelixConfiguration. + // +optional + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=2147483646 + Priority *int `json:"priority,omitempty" validate:"omitempty,gte=1,lte=2147483646"` + Action BGPFilterAction `json:"action" validate:"required,filterAction"` + + // Operations is an ordered list of route modifications to apply to matching routes before + // accepting them. Only valid when Action is "Accept"; specifying operations with "Reject" + // is rejected by validation. Each entry must set exactly one operation field. + // +optional + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + Operations []BGPFilterOperation `json:"operations,omitempty" validate:"omitempty,dive"` } // +mapType=atomic @@ -126,10 +245,10 @@ const ( type BGPFilterMatchOperator string const ( - Equal BGPFilterMatchOperator = "Equal" - NotEqual BGPFilterMatchOperator = "NotEqual" - In BGPFilterMatchOperator = "In" - NotIn BGPFilterMatchOperator = "NotIn" + MatchOperatorEqual BGPFilterMatchOperator = "Equal" + MatchOperatorNotEqual BGPFilterMatchOperator = "NotEqual" + MatchOperatorIn BGPFilterMatchOperator = "In" + MatchOperatorNotIn BGPFilterMatchOperator = "NotIn" ) // +kubebuilder:validation:Enum=Accept;Reject @@ -140,6 +259,81 @@ const ( Reject BGPFilterAction = "Reject" ) +// BGPFilterPeerType specifies which type of BGP peer a filter rule applies to. +// +kubebuilder:validation:Enum=eBGP;iBGP +type BGPFilterPeerType string + +const ( + BGPFilterPeerTypeEBGP BGPFilterPeerType = "eBGP" + BGPFilterPeerTypeIBGP BGPFilterPeerType = "iBGP" +) + +// BGPCommunityValue is a BGP community string in `aa:nn` (standard) or `aa:nn:mm` (large) format. +// For standard communities, each component must be a 16-bit value (0-65535). +// For large communities, each component must be a 32-bit value (0-4294967295). +// +kubebuilder:validation:MaxLength=32 +// +kubebuilder:validation:Pattern=`^(([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]):([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])|([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[0-1][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[0-1][0-9]{2}|42949672[0-8][0-9]|429496729[0-5]):([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[0-1][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[0-1][0-9]{2}|42949672[0-8][0-9]|429496729[0-5]):([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[0-1][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[0-1][0-9]{2}|42949672[0-8][0-9]|429496729[0-5]))$` +type BGPCommunityValue string + +// BGPFilterCommunityMatch specifies community-based match criteria for a BGP filter rule. +// Currently only a single community value is supported. A MatchOperator field may be +// introduced in the future to support anyOf/allOf semantics with multiple values. +// +mapType=atomic +type BGPFilterCommunityMatch struct { + // Values is a list of BGP community values to match against. + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=1 + Values []BGPCommunityValue `json:"values" validate:"required"` +} + +// BGPFilterOperation is a discriminated union representing a single route modification. +// Exactly one field must be set. +// +mapType=atomic +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:MaxProperties=1 +type BGPFilterOperation struct { + // AddCommunity adds the specified BGP community to the route. + // +optional + AddCommunity *BGPFilterAddCommunity `json:"addCommunity,omitempty" validate:"omitempty"` + + // PrependASPath prepends the specified AS numbers to the route's AS path. + // +optional + PrependASPath *BGPFilterPrependASPath `json:"prependASPath,omitempty" validate:"omitempty"` + + // SetPriority sets the route's priority (metric), in the same units as the + // ...RoutePriority fields in FelixConfiguration. + // +optional + SetPriority *BGPFilterSetPriority `json:"setPriority,omitempty" validate:"omitempty"` +} + +// BGPFilterAddCommunity specifies a BGP community to add to a route. +// +mapType=atomic +type BGPFilterAddCommunity struct { + // Value is the BGP community to add. + Value *BGPCommunityValue `json:"value" validate:"required"` +} + +// BGPFilterPrependASPath specifies AS numbers to prepend to a route's AS path. +// +mapType=atomic +type BGPFilterPrependASPath struct { + // Prefix is the sequence of AS numbers to prepend to the route's AS path. + // The resulting path starts with these AS numbers in the order listed; + // e.g. [65000, 65001] produces the path "65000 65001 ". + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=10 + Prefix []numorstring.ASNumber `json:"prefix" validate:"required"` +} + +// BGPFilterSetPriority specifies a route priority to set. +// +mapType=atomic +type BGPFilterSetPriority struct { + // Value is the priority to set, in the same units as FelixConfiguration's + // ...RoutePriority fields. + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=2147483646 + Value *int `json:"value" validate:"required,gte=1,lte=2147483646"` +} + // New BGPFilter creates a new (zeroed) BGPFilter struct with the TypeMetadata // initialized to the current version. func NewBGPFilter() *BGPFilter { diff --git a/pkg/apis/projectcalico/v3/bgppeer.go b/pkg/apis/projectcalico/v3/bgppeer.go index 01e46408..0264be8c 100644 --- a/pkg/apis/projectcalico/v3/bgppeer.go +++ b/pkg/apis/projectcalico/v3/bgppeer.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017,2020-2021 Tigera, Inc. All rights reserved. +// Copyright (c) 2017,2020-2021,2026 Tigera, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -54,16 +54,24 @@ type BGPPeer struct { } // BGPPeerSpec contains the specification for a BGPPeer resource. +// +kubebuilder:validation:XValidation:rule="(!has(self.node) || size(self.node) == 0) || (!has(self.nodeSelector) || size(self.nodeSelector) == 0)",message="node and nodeSelector cannot both be set",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.peerIP) || size(self.peerIP) == 0) || (!has(self.peerSelector) || size(self.peerSelector) == 0)",message="peerIP and peerSelector cannot both be set",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.peerSelector) || size(self.peerSelector) == 0) || !has(self.asNumber) || self.asNumber == 0",message="asNumber must be empty when peerSelector is set",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.localWorkloadSelector) || size(self.localWorkloadSelector) == 0) || (!has(self.peerIP) || size(self.peerIP) == 0)",message="peerIP must be empty when localWorkloadSelector is set",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.localWorkloadSelector) || size(self.localWorkloadSelector) == 0) || (!has(self.peerSelector) || size(self.peerSelector) == 0)",message="peerSelector must be empty when localWorkloadSelector is set",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.localWorkloadSelector) || size(self.localWorkloadSelector) == 0) || (has(self.asNumber) && self.asNumber != 0)",message="asNumber is required when localWorkloadSelector is set",reason=FieldValueInvalid type BGPPeerSpec struct { // The node name identifying the Calico node instance that is targeted by this peer. // If this is not set, and no nodeSelector is specified, then this BGP peer selects all // nodes in the cluster. // +optional + // +kubebuilder:validation:MaxLength=253 Node string `json:"node,omitempty" validate:"omitempty,name"` // Selector for the nodes that should have this peering. When this is set, the Node // field must be empty. // +optional + // +kubebuilder:validation:MaxLength=4096 NodeSelector string `json:"nodeSelector,omitempty" validate:"omitempty,selector"` // The IP address of the peer followed by an optional port number to peer with. @@ -71,6 +79,7 @@ type BGPPeerSpec struct { // If optional port number is not set, and this peer IP and ASNumber belongs to a calico/node // with ListenPort set in BGPConfiguration, then we use that port to peer. // +optional + // +kubebuilder:validation:MaxLength=64 PeerIP string `json:"peerIP,omitempty" validate:"omitempty,IP:port"` // The AS Number of the peer. @@ -93,6 +102,7 @@ type BGPPeerSpec struct { // NodeBGPSpec.IPv6Address specified. The remote AS number comes from the remote // node's NodeBGPSpec.ASNumber, or the global default if that is not set. // +optional + // +kubebuilder:validation:MaxLength=4096 PeerSelector string `json:"peerSelector,omitempty" validate:"omitempty,selector"` // Option to keep the original nexthop field when routes are sent to a BGP Peer. @@ -175,6 +185,7 @@ type BGPPeerSpec struct { // Selector for the local workload that the node should peer with. When this is set, the peerSelector and peerIP fields must be empty, // and the ASNumber must not be empty. // +optional + // +kubebuilder:validation:MaxLength=4096 LocalWorkloadSelector string `json:"localWorkloadSelector,omitempty" validate:"omitempty,selector"` // ReversePeering, for peerings between Calico nodes controls whether diff --git a/pkg/apis/projectcalico/v3/felixconfig.go b/pkg/apis/projectcalico/v3/felixconfig.go index 2b9a86a8..c77fd73d 100644 --- a/pkg/apis/projectcalico/v3/felixconfig.go +++ b/pkg/apis/projectcalico/v3/felixconfig.go @@ -202,6 +202,7 @@ const ( ) // FelixConfigurationSpec contains the values of the Felix configuration. +// +kubebuilder:validation:XValidation:rule="!has(self.routeTableRange) || !has(self.routeTableRanges)",message="routeTableRange and routeTableRanges cannot both be set",reason=FieldValueForbidden type FelixConfigurationSpec struct { // UseInternalDataplaneDriver, if true, Felix will use its internal dataplane programming logic. If false, it // will launch an external dataplane driver and communicate with it over protobuf. @@ -868,8 +869,9 @@ type FelixConfigurationSpec struct { BPFKubeProxyMinSyncPeriod *metav1.Duration `json:"bpfKubeProxyMinSyncPeriod,omitempty" validate:"omitempty" configv1timescale:"seconds"` // BPFKubeProxyHealthzPort, in BPF mode, controls the port that Felix's embedded kube-proxy health check server binds to. - // The health check server is used by external load balancers to determine if this node should receive traffic. [Default: 10256] - BPFKubeProxyHealthzPort *int `json:"bpfKubeProxyHealthzPort,omitempty" validate:"omitempty,gte=1,lte=65535" confignamev1:"BPFKubeProxyHealthzPort"` + // The health check server is used by external load balancers to determine if this node should receive traffic. + // Set to 0 to disable the health check server. [Default: 10256] + BPFKubeProxyHealthzPort *int `json:"bpfKubeProxyHealthzPort,omitempty" validate:"omitempty,gte=0,lte=65535" confignamev1:"BPFKubeProxyHealthzPort"` // BPFPSNATPorts sets the range from which we randomly pick a port if there is a source port // collision. This should be within the ephemeral range as defined by RFC 6056 (1024–65535) and @@ -1464,6 +1466,29 @@ type FelixConfigurationSpec struct { // ExternalNetworkRoutingRulePriority controls the priority value to use for the external network routing rule. [Default: 102] ExternalNetworkRoutingRulePriority *int `json:"externalNetworkRoutingRulePriority,omitempty" validate:"omitempty,gt=0,lt=32766"` + // Route Priority value for a normal priority Calico-programmed IPv4 route. Note, higher + // values mean lower priority. [Default: 1024] + IPv4NormalRoutePriority *int `json:"ipv4NormalRoutePriority,omitempty" validate:"omitempty,gte=1,lte=2147483646"` + // Route Priority value for an elevated priority Calico-programmed IPv4 route. Note, higher + // values mean lower priority. Elevated priority is used during VM live migration, and for + // optimal behaviour IPv4ElevatedRoutePriority must be less than IPv4NormalRoutePriority + // [Default: 512] + IPv4ElevatedRoutePriority *int `json:"ipv4ElevatedRoutePriority,omitempty" validate:"omitempty,gte=1,lte=2147483646"` + // Route Priority value for a normal priority Calico-programmed IPv6 route. Note, higher + // values mean lower priority. [Default: 1024] + IPv6NormalRoutePriority *int `json:"ipv6NormalRoutePriority,omitempty" validate:"omitempty,gte=1,lte=2147483646"` + // Route Priority value for an elevated priority Calico-programmed IPv6 route. Note, higher + // values mean lower priority. Elevated priority is used during VM live migration, and for + // optimal behaviour IPv6ElevatedRoutePriority must be less than IPv6NormalRoutePriority + // [Default: 512] + IPv6ElevatedRoutePriority *int `json:"ipv6ElevatedRoutePriority,omitempty" validate:"omitempty,gte=1,lte=2147483646"` + // LiveMigrationRouteConvergenceTime is the time to keep elevated route priority after a + // VM live migration completes. This allows routes to converge across the cluster before + // reverting to normal priority. [Default: 30s] + // +kubebuilder:validation:Type=string + // +kubebuilder:validation:Pattern=`^([0-9]+(\.[0-9]+)?(ms|s|m|h))*$` + LiveMigrationRouteConvergenceTime *metav1.Duration `json:"liveMigrationRouteConvergenceTime,omitempty" configv1timescale:"seconds"` + // WireguardEnabled controls whether Wireguard is enabled for IPv4 (encapsulating IPv4 traffic over an IPv4 underlay network). [Default: false] WireguardEnabled *bool `json:"wireguardEnabled,omitempty"` diff --git a/pkg/apis/projectcalico/v3/globalnetworkpolicy.go b/pkg/apis/projectcalico/v3/globalnetworkpolicy.go index ca728e85..91a4493c 100644 --- a/pkg/apis/projectcalico/v3/globalnetworkpolicy.go +++ b/pkg/apis/projectcalico/v3/globalnetworkpolicy.go @@ -25,7 +25,6 @@ const ( // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +kubebuilder:selectablefield:JSONPath=`.spec.tier` // +kubebuilder:resource:scope=Cluster // GlobalNetworkPolicyList is a list of Policy objects. @@ -40,6 +39,7 @@ type GlobalNetworkPolicyList struct { // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:scope=Cluster,shortName={gnp,cgnp} +// +kubebuilder:selectablefield:JSONPath=`.spec.tier` // +kubebuilder:printcolumn:name="Tier",type=string,JSONPath=`.spec.tier` // +kubebuilder:printcolumn:name="Order",type=number,JSONPath=`.spec.order` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` @@ -51,6 +51,10 @@ type GlobalNetworkPolicy struct { Spec GlobalNetworkPolicySpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` } +// +kubebuilder:validation:XValidation:rule="!((has(self.doNotTrack) && self.doNotTrack) && (has(self.preDNAT) && self.preDNAT))",message="preDNAT and doNotTrack cannot both be true",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.preDNAT) || !self.preDNAT) || !has(self.egress) || size(self.egress) == 0",message="preDNAT policy cannot have any egress rules",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.preDNAT) || !self.preDNAT) || !has(self.types) || !self.types.exists(t, t == 'Egress')",message="preDNAT policy cannot have 'Egress' type",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(has(self.applyOnForward) && self.applyOnForward) || ((!has(self.doNotTrack) || !self.doNotTrack) && (!has(self.preDNAT) || !self.preDNAT))",message="applyOnForward must be true if either preDNAT or doNotTrack is true",reason=FieldValueInvalid type GlobalNetworkPolicySpec struct { // The name of the tier that this policy belongs to. If this is omitted, the default // tier (name is "default") is assumed. The specified tier must exist in order to create diff --git a/pkg/apis/projectcalico/v3/hostendpoint.go b/pkg/apis/projectcalico/v3/hostendpoint.go index 73e3567c..1d9ba835 100644 --- a/pkg/apis/projectcalico/v3/hostendpoint.go +++ b/pkg/apis/projectcalico/v3/hostendpoint.go @@ -50,8 +50,11 @@ type HostEndpoint struct { } // HostEndpointSpec contains the specification for a HostEndpoint resource. +// +kubebuilder:validation:XValidation:rule="(has(self.interfaceName) && size(self.interfaceName) > 0) || (has(self.expectedIPs) && size(self.expectedIPs) > 0)",message="at least one of interfaceName or expectedIPs must be specified",reason=FieldValueInvalid +// +kubebuilder:validation:XValidation:rule="has(self.node) && size(self.node) > 0",message="node must be specified",reason=FieldValueInvalid type HostEndpointSpec struct { // The node name identifying the Calico node instance. + // +kubebuilder:validation:MaxLength=253 Node string `json:"node,omitempty" validate:"omitempty,name"` // Either "*", or the name of a specific Linux interface to apply policy to; or empty. "*" @@ -68,6 +71,7 @@ type HostEndpointSpec struct { // // Note: Only some kinds of policy are implemented for "*" HostEndpoints; initially just // pre-DNAT policy. Please check Calico documentation for the latest position. + // +kubebuilder:validation:MaxLength=15 InterfaceName string `json:"interfaceName,omitempty" validate:"omitempty,interface"` // The expected IP addresses (IPv4 and IPv6) of the endpoint. @@ -96,7 +100,7 @@ type EndpointPort struct { Name string `json:"name" validate:"portName"` Protocol numorstring.Protocol `json:"protocol"` - // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Minimum=1 // +kubebuilder:validation:Maximum=65535 Port uint16 `json:"port" validate:"gt=0"` } diff --git a/pkg/apis/projectcalico/v3/ipamblocks.go b/pkg/apis/projectcalico/v3/ipamblocks.go index d20c9a7a..6c026968 100644 --- a/pkg/apis/projectcalico/v3/ipamblocks.go +++ b/pkg/apis/projectcalico/v3/ipamblocks.go @@ -88,8 +88,9 @@ type IPAMBlockSpec struct { } type AllocationAttribute struct { - AttrPrimary *string `json:"handle_id,omitempty"` - AttrSecondary map[string]string `json:"secondary,omitempty"` + HandleID *string `json:"handle_id,omitempty"` + ActiveOwnerAttrs map[string]string `json:"secondary,omitempty"` + AlternateOwnerAttrs map[string]string `json:"alternateOwnerAttrs,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/projectcalico/v3/ipamconfig.go b/pkg/apis/projectcalico/v3/ipamconfig.go index ee6f56b4..24de61ea 100644 --- a/pkg/apis/projectcalico/v3/ipamconfig.go +++ b/pkg/apis/projectcalico/v3/ipamconfig.go @@ -51,6 +51,15 @@ type IPAMConfiguration struct { Spec IPAMConfigurationSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` } +// VMAddressPersistence controls whether KubeVirt VirtualMachine workloads +// maintain persistent IP addresses across VM lifecycle events. +type VMAddressPersistence string + +const ( + VMAddressPersistenceEnabled VMAddressPersistence = "Enabled" + VMAddressPersistenceDisabled VMAddressPersistence = "Disabled" +) + // IPAMConfigurationSpec contains the specification for an IPAMConfiguration resource. type IPAMConfigurationSpec struct { // When StrictAffinity is true, borrowing IP addresses is not allowed. @@ -69,6 +78,19 @@ type IPAMConfigurationSpec struct { // Whether or not to auto allocate blocks to hosts. // +kubebuilder:default=true AutoAllocateBlocks bool `json:"autoAllocateBlocks"` + + // KubeVirtVMAddressPersistence controls whether KubeVirt VirtualMachine workloads + // maintain persistent IP addresses across VM lifecycle events (reboot, migration, pod eviction). + // When Enabled, Calico automatically ensures that KubeVirt VMs retain their IP addresses + // when their underlying pods are recreated during VM operations. + // When Disabled, VMs receive new IP addresses whenever their pods are recreated, + // and creating a live migration target pod is not supported because the migration + // target pod requires the same IP as the source pod, which is only possible with + // address persistence. + // Defaults to Enabled if not specified. + // +kubebuilder:validation:Enum=Enabled;Disabled + // +optional + KubeVirtVMAddressPersistence *VMAddressPersistence `json:"kubeVirtVMAddressPersistence,omitempty"` } // NewIPAMConfiguration creates a new (zeroed) IPAMConfiguration struct with the TypeMetadata initialised to the current diff --git a/pkg/apis/projectcalico/v3/ippool.go b/pkg/apis/projectcalico/v3/ippool.go index a5c6dd6e..10f346f5 100644 --- a/pkg/apis/projectcalico/v3/ippool.go +++ b/pkg/apis/projectcalico/v3/ippool.go @@ -81,10 +81,16 @@ type IPPoolStatus struct { } // IPPoolSpec contains the specification for an IPPool resource. +// +kubebuilder:validation:XValidation:rule="!has(self.ipipMode) || !has(self.vxlanMode) || self.ipipMode == 'Never' || self.vxlanMode == 'Never' || size(self.ipipMode) == 0 || size(self.vxlanMode) == 0",message="ipipMode and vxlanMode cannot both be enabled",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="!has(self.allowedUses) || !self.allowedUses.exists(u, u == 'LoadBalancer') || (!has(self.ipipMode) || size(self.ipipMode) == 0 || self.ipipMode == 'Never') && (!has(self.vxlanMode) || size(self.vxlanMode) == 0 || self.vxlanMode == 'Never')",message="LoadBalancer IP pool cannot have IPIP or VXLAN enabled",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="!has(self.allowedUses) || !self.allowedUses.exists(u, u == 'LoadBalancer') || !self.allowedUses.exists(u, u == 'Workload' || u == 'Tunnel')",message="LoadBalancer cannot be combined with Workload or Tunnel allowed uses",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="!self.cidr.contains(':') || !has(self.ipipMode) || self.ipipMode == 'Never' || size(self.ipipMode) == 0",message="IPIP is not supported on IPv6 pools",reason=FieldValueForbidden type IPPoolSpec struct { // The pool CIDR. // +kubebuilder:validation:Required // +kubebuilder:validation:Format=cidr + // +kubebuilder:validation:MaxLength=48 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="CIDR cannot be changed; follow IP pool migration guide to avoid corruption.",reason=FieldValueInvalid CIDR string `json:"cidr" validate:"net"` // Contains configuration for VXLAN tunneling for this pool. @@ -109,6 +115,7 @@ type IPPoolSpec struct { // or equal to the size of the pool CIDR. // +kubebuilder:validation:Minimum=0 // +kubebuilder:validation:Maximum=128 + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Block size cannot be changed; follow IP pool migration guide to avoid corruption.",reason=FieldValueInvalid BlockSize int `json:"blockSize,omitempty"` // Allows IPPool to allocate for a specific node by label selector. @@ -120,6 +127,7 @@ type IPPoolSpec struct { // AllowedUse controls what the IP pool will be used for. If not specified or empty, defaults to // ["Tunnel", "Workload"] for back-compatibility + // +kubebuilder:validation:MaxItems=10 // +listType=set AllowedUses []IPPoolAllowedUse `json:"allowedUses,omitempty" validate:"omitempty"` diff --git a/pkg/apis/projectcalico/v3/policy_common.go b/pkg/apis/projectcalico/v3/policy_common.go index 8d69fc25..929075c8 100644 --- a/pkg/apis/projectcalico/v3/policy_common.go +++ b/pkg/apis/projectcalico/v3/policy_common.go @@ -34,6 +34,9 @@ const ( // Each positive match criteria has a negated version, prefixed with "Not". All the match // criteria within a rule must be satisfied for a packet to match. A single rule can contain // the positive and negative version of a match and both must be satisfied for the rule to match. +// +kubebuilder:validation:XValidation:rule="!has(self.http) || !has(self.protocol) || self.protocol == 'TCP' || self.protocol == 6",message="rules with HTTP match must have protocol TCP or unset",reason=FieldValueInvalid +// +kubebuilder:validation:XValidation:rule="self.action == 'Allow' || !has(self.http)",message="HTTP match is only valid on Allow rules",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="!has(self.destination) || !has(self.destination.services) || (!has(self.destination.ports) || size(self.destination.ports) == 0) && (!has(self.destination.notPorts) || size(self.destination.notPorts) == 0)",message="ports and notPorts cannot be specified with services",reason=FieldValueForbidden type Rule struct { Action Action `json:"action" validate:"action"` @@ -81,7 +84,9 @@ type Rule struct { // exact: : which matches the path exactly or // prefix: : which matches the path prefix type HTTPPath struct { - Exact string `json:"exact,omitempty" validate:"omitempty"` + // +kubebuilder:validation:MaxLength=1024 + Exact string `json:"exact,omitempty" validate:"omitempty"` + // +kubebuilder:validation:MaxLength=1024 Prefix string `json:"prefix,omitempty" validate:"omitempty"` } @@ -91,6 +96,7 @@ type HTTPMatch struct { // Methods is an optional field that restricts the rule to apply only to HTTP requests that use one of the listed // HTTP Methods (e.g. GET, PUT, etc.) // Multiple methods are OR'd together. + // +kubebuilder:validation:MaxItems=20 Methods []string `json:"methods,omitempty" validate:"omitempty"` // Paths is an optional field that restricts the rule to apply to HTTP requests that use one of the listed // HTTP Paths. @@ -99,6 +105,7 @@ type HTTPMatch struct { // - exact: /foo // - prefix: /bar // NOTE: Each entry may ONLY specify either a `exact` or a `prefix` match. The validator will check for it. + // +kubebuilder:validation:MaxItems=20 Paths []HTTPPath `json:"paths,omitempty" validate:"omitempty"` // Headers is an optional field that restricts the rule to apply to HTTP headers. // Multiple headers criteria are AND'd together. @@ -114,11 +121,12 @@ type HTTPHeaderCriteria struct { } // ICMPFields defines structure for ICMP and NotICMP sub-struct for ICMP code and type +// +kubebuilder:validation:XValidation:rule="!has(self.code) || has(self.type)",message="ICMP code specified without an ICMP type",reason=FieldValueInvalid type ICMPFields struct { // Match on a specific ICMP type. For example a value of 8 refers to ICMP Echo Request // (i.e. pings). // +kubebuilder:validation:Minimum=0 - // +kubebuilder:validation:Maximum=255 + // +kubebuilder:validation:Maximum=254 // +optional Type *int `json:"type,omitempty" validate:"omitempty,gte=0,lte=254"` diff --git a/pkg/apis/projectcalico/v3/stagedglobalnetworkpolicy.go b/pkg/apis/projectcalico/v3/stagedglobalnetworkpolicy.go index e2ae85db..750568d4 100644 --- a/pkg/apis/projectcalico/v3/stagedglobalnetworkpolicy.go +++ b/pkg/apis/projectcalico/v3/stagedglobalnetworkpolicy.go @@ -39,8 +39,13 @@ type StagedGlobalNetworkPolicy struct { Spec StagedGlobalNetworkPolicySpec `json:"spec"` } +// +kubebuilder:validation:XValidation:rule="!((has(self.doNotTrack) && self.doNotTrack) && (has(self.preDNAT) && self.preDNAT))",message="preDNAT and doNotTrack cannot both be true",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.preDNAT) || !self.preDNAT) || !has(self.egress) || size(self.egress) == 0",message="preDNAT policy cannot have any egress rules",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(!has(self.preDNAT) || !self.preDNAT) || !has(self.types) || !self.types.exists(t, t == 'Egress')",message="preDNAT policy cannot have 'Egress' type",reason=FieldValueForbidden +// +kubebuilder:validation:XValidation:rule="(has(self.applyOnForward) && self.applyOnForward) || ((!has(self.doNotTrack) || !self.doNotTrack) && (!has(self.preDNAT) || !self.preDNAT))",message="applyOnForward must be true if either preDNAT or doNotTrack is true",reason=FieldValueInvalid type StagedGlobalNetworkPolicySpec struct { // The staged action. If this is omitted, the default is Set. + // +kubebuilder:default=Set StagedAction StagedAction `json:"stagedAction,omitempty" validate:"omitempty,stagedAction"` // The name of the tier that this policy belongs to. If this is omitted, the default diff --git a/pkg/apis/projectcalico/v3/stagedkubernetesnetworkpolicy.go b/pkg/apis/projectcalico/v3/stagedkubernetesnetworkpolicy.go index 5e790035..8864d6c2 100644 --- a/pkg/apis/projectcalico/v3/stagedkubernetesnetworkpolicy.go +++ b/pkg/apis/projectcalico/v3/stagedkubernetesnetworkpolicy.go @@ -38,6 +38,7 @@ type StagedKubernetesNetworkPolicy struct { type StagedKubernetesNetworkPolicySpec struct { // The staged action. If this is omitted, the default is Set. + // +kubebuilder:default=Set StagedAction StagedAction `json:"stagedAction,omitempty" validate:"omitempty,stagedAction"` // Selects the pods to which this NetworkPolicy object applies. The array of diff --git a/pkg/apis/projectcalico/v3/stagednetworkpolicy.go b/pkg/apis/projectcalico/v3/stagednetworkpolicy.go index 98a69f72..39b5eea3 100644 --- a/pkg/apis/projectcalico/v3/stagednetworkpolicy.go +++ b/pkg/apis/projectcalico/v3/stagednetworkpolicy.go @@ -41,6 +41,7 @@ type StagedNetworkPolicy struct { type StagedNetworkPolicySpec struct { // The staged action. If this is omitted, the default is Set. + // +kubebuilder:default=Set StagedAction StagedAction `json:"stagedAction,omitempty" validate:"omitempty,stagedAction"` // The name of the tier that this policy belongs to. If this is omitted, the default diff --git a/pkg/apis/projectcalico/v3/tier.go b/pkg/apis/projectcalico/v3/tier.go index dee49e8b..7a4cd590 100644 --- a/pkg/apis/projectcalico/v3/tier.go +++ b/pkg/apis/projectcalico/v3/tier.go @@ -1,4 +1,4 @@ -// Copyright (c) 2024-2025 Tigera, Inc. All rights reserved. +// Copyright (c) 2024-2026 Tigera, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,20 +25,23 @@ const ( // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:scope=Cluster +// +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Order",type="integer",JSONPath=".spec.order",description="Order in which the tier is applied" // +kubebuilder:printcolumn:name="DefaultAction",type="string",JSONPath=".spec.defaultAction",description="Default action for the tier" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].reason",description="Current status of the tier" // Tier contains a set of policies that are applied to packets. Multiple tiers may // be created and each tier is applied in the order specified in the tier specification. // Tier is globally-scoped (i.e. not Namespaced). // -// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'kube-admin' ? self.spec.defaultAction == 'Pass' : true", message="The 'kube-admin' tier must have default action 'Pass'" -// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'kube-baseline' ? self.spec.defaultAction == 'Pass' : true", message="The 'kube-baseline' tier must have default action 'Pass'" -// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'default' ? self.spec.defaultAction == 'Deny' : true", message="The 'default' tier must have default action 'Deny'" +// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'kube-admin' ? (has(self.spec.defaultAction) && self.spec.defaultAction == 'Pass') : true", message="The 'kube-admin' tier must have default action 'Pass'",reason=FieldValueInvalid +// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'kube-baseline' ? (has(self.spec.defaultAction) && self.spec.defaultAction == 'Pass') : true", message="The 'kube-baseline' tier must have default action 'Pass'",reason=FieldValueInvalid +// +kubebuilder:validation:XValidation:rule="self.metadata.name == 'default' ? (has(self.spec.defaultAction) && self.spec.defaultAction == 'Deny') : true", message="The 'default' tier must have default action 'Deny'",reason=FieldValueInvalid type Tier struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata" protobuf:"bytes,1,opt,name=metadata"` - Spec TierSpec `json:"spec" protobuf:"bytes,2,rep,name=spec"` + Spec TierSpec `json:"spec" protobuf:"bytes,2,rep,name=spec"` + Status TierStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` } const ( @@ -47,8 +50,18 @@ const ( DefaultTierOrder = float64(1_000_000) // 1Million BaselineAdminNetworkPolicyTierOrder = float64(10_000_000) // 10Million KubeBaselineTierOrder = float64(10_000_000) // 10Million + + // TierFinalizer is set on tiers to ensure policies are cleaned up before the tier is deleted. + TierFinalizer = "projectcalico.org/tier-controller" ) +// TierStatus contains the status of a Tier resource. +type TierStatus struct { + // Conditions represents the latest observed set of conditions for the resource. A tier with a + // "Ready" condition set to "True" is operating as expected. + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + // TierSpec contains the specification for a security policy tier resource. type TierSpec struct { // Order is an optional field that specifies the order in which the tier is applied. @@ -60,6 +73,7 @@ type TierSpec struct { // DefaultAction specifies the action applied to workloads selected by a policy in the tier, // but not rule matched the workload's traffic. // [Default: Deny] + // +kubebuilder:default=Deny // +kubebuilder:validation:Enum=Pass;Deny DefaultAction *Action `json:"defaultAction,omitempty" validate:"omitempty,oneof=Deny Pass"` } diff --git a/pkg/apis/projectcalico/v3/zz_generated.deepcopy.go b/pkg/apis/projectcalico/v3/zz_generated.deepcopy.go index 0eb83e66..3898e1a1 100644 --- a/pkg/apis/projectcalico/v3/zz_generated.deepcopy.go +++ b/pkg/apis/projectcalico/v3/zz_generated.deepcopy.go @@ -117,13 +117,20 @@ func (in *AlertExceptionStatus) DeepCopy() *AlertExceptionStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AllocationAttribute) DeepCopyInto(out *AllocationAttribute) { *out = *in - if in.AttrPrimary != nil { - in, out := &in.AttrPrimary, &out.AttrPrimary + if in.HandleID != nil { + in, out := &in.HandleID, &out.HandleID *out = new(string) **out = **in } - if in.AttrSecondary != nil { - in, out := &in.AttrSecondary, &out.AttrSecondary + if in.ActiveOwnerAttrs != nil { + in, out := &in.ActiveOwnerAttrs, &out.ActiveOwnerAttrs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.AlternateOwnerAttrs != nil { + in, out := &in.AlternateOwnerAttrs, &out.AlternateOwnerAttrs *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val @@ -682,6 +689,16 @@ func (in *BGPConfigurationSpec) DeepCopyInto(out *BGPConfigurationSpec) { *out = new(string) **out = **in } + if in.IPv4NormalRoutePriority != nil { + in, out := &in.IPv4NormalRoutePriority, &out.IPv4NormalRoutePriority + *out = new(int) + **out = **in + } + if in.IPv6NormalRoutePriority != nil { + in, out := &in.IPv6NormalRoutePriority, &out.IPv6NormalRoutePriority + *out = new(int) + **out = **in + } return } @@ -738,6 +755,48 @@ func (in *BGPFilter) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPFilterAddCommunity) DeepCopyInto(out *BGPFilterAddCommunity) { + *out = *in + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(BGPCommunityValue) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPFilterAddCommunity. +func (in *BGPFilterAddCommunity) DeepCopy() *BGPFilterAddCommunity { + if in == nil { + return nil + } + out := new(BGPFilterAddCommunity) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPFilterCommunityMatch) DeepCopyInto(out *BGPFilterCommunityMatch) { + *out = *in + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = make([]BGPCommunityValue, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPFilterCommunityMatch. +func (in *BGPFilterCommunityMatch) DeepCopy() *BGPFilterCommunityMatch { + if in == nil { + return nil + } + out := new(BGPFilterCommunityMatch) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BGPFilterList) DeepCopyInto(out *BGPFilterList) { *out = *in @@ -771,6 +830,37 @@ func (in *BGPFilterList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPFilterOperation) DeepCopyInto(out *BGPFilterOperation) { + *out = *in + if in.AddCommunity != nil { + in, out := &in.AddCommunity, &out.AddCommunity + *out = new(BGPFilterAddCommunity) + (*in).DeepCopyInto(*out) + } + if in.PrependASPath != nil { + in, out := &in.PrependASPath, &out.PrependASPath + *out = new(BGPFilterPrependASPath) + (*in).DeepCopyInto(*out) + } + if in.SetPriority != nil { + in, out := &in.SetPriority, &out.SetPriority + *out = new(BGPFilterSetPriority) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPFilterOperation. +func (in *BGPFilterOperation) DeepCopy() *BGPFilterOperation { + if in == nil { + return nil + } + out := new(BGPFilterOperation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BGPFilterPrefixLengthV4) DeepCopyInto(out *BGPFilterPrefixLengthV4) { *out = *in @@ -823,6 +913,27 @@ func (in *BGPFilterPrefixLengthV6) DeepCopy() *BGPFilterPrefixLengthV6 { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPFilterPrependASPath) DeepCopyInto(out *BGPFilterPrependASPath) { + *out = *in + if in.Prefix != nil { + in, out := &in.Prefix, &out.Prefix + *out = make([]numorstring.ASNumber, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPFilterPrependASPath. +func (in *BGPFilterPrependASPath) DeepCopy() *BGPFilterPrependASPath { + if in == nil { + return nil + } + out := new(BGPFilterPrependASPath) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BGPFilterRuleV4) DeepCopyInto(out *BGPFilterRuleV4) { *out = *in @@ -831,6 +942,28 @@ func (in *BGPFilterRuleV4) DeepCopyInto(out *BGPFilterRuleV4) { *out = new(BGPFilterPrefixLengthV4) (*in).DeepCopyInto(*out) } + if in.Communities != nil { + in, out := &in.Communities, &out.Communities + *out = new(BGPFilterCommunityMatch) + (*in).DeepCopyInto(*out) + } + if in.ASPathPrefix != nil { + in, out := &in.ASPathPrefix, &out.ASPathPrefix + *out = make([]numorstring.ASNumber, len(*in)) + copy(*out, *in) + } + if in.Priority != nil { + in, out := &in.Priority, &out.Priority + *out = new(int) + **out = **in + } + if in.Operations != nil { + in, out := &in.Operations, &out.Operations + *out = make([]BGPFilterOperation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -852,6 +985,28 @@ func (in *BGPFilterRuleV6) DeepCopyInto(out *BGPFilterRuleV6) { *out = new(BGPFilterPrefixLengthV6) (*in).DeepCopyInto(*out) } + if in.Communities != nil { + in, out := &in.Communities, &out.Communities + *out = new(BGPFilterCommunityMatch) + (*in).DeepCopyInto(*out) + } + if in.ASPathPrefix != nil { + in, out := &in.ASPathPrefix, &out.ASPathPrefix + *out = make([]numorstring.ASNumber, len(*in)) + copy(*out, *in) + } + if in.Priority != nil { + in, out := &in.Priority, &out.Priority + *out = new(int) + **out = **in + } + if in.Operations != nil { + in, out := &in.Operations, &out.Operations + *out = make([]BGPFilterOperation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } @@ -865,6 +1020,27 @@ func (in *BGPFilterRuleV6) DeepCopy() *BGPFilterRuleV6 { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPFilterSetPriority) DeepCopyInto(out *BGPFilterSetPriority) { + *out = *in + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(int) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPFilterSetPriority. +func (in *BGPFilterSetPriority) DeepCopy() *BGPFilterSetPriority { + if in == nil { + return nil + } + out := new(BGPFilterSetPriority) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BGPFilterSpec) DeepCopyInto(out *BGPFilterSpec) { *out = *in @@ -3613,6 +3789,31 @@ func (in *FelixConfigurationSpec) DeepCopyInto(out *FelixConfigurationSpec) { *out = new(int) **out = **in } + if in.IPv4NormalRoutePriority != nil { + in, out := &in.IPv4NormalRoutePriority, &out.IPv4NormalRoutePriority + *out = new(int) + **out = **in + } + if in.IPv4ElevatedRoutePriority != nil { + in, out := &in.IPv4ElevatedRoutePriority, &out.IPv4ElevatedRoutePriority + *out = new(int) + **out = **in + } + if in.IPv6NormalRoutePriority != nil { + in, out := &in.IPv6NormalRoutePriority, &out.IPv6NormalRoutePriority + *out = new(int) + **out = **in + } + if in.IPv6ElevatedRoutePriority != nil { + in, out := &in.IPv6ElevatedRoutePriority, &out.IPv6ElevatedRoutePriority + *out = new(int) + **out = **in + } + if in.LiveMigrationRouteConvergenceTime != nil { + in, out := &in.LiveMigrationRouteConvergenceTime, &out.LiveMigrationRouteConvergenceTime + *out = new(v1.Duration) + **out = **in + } if in.WireguardEnabled != nil { in, out := &in.WireguardEnabled, &out.WireguardEnabled *out = new(bool) @@ -4851,7 +5052,7 @@ func (in *IPAMConfiguration) DeepCopyInto(out *IPAMConfiguration) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) return } @@ -4909,6 +5110,11 @@ func (in *IPAMConfigurationList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPAMConfigurationSpec) DeepCopyInto(out *IPAMConfigurationSpec) { *out = *in + if in.KubeVirtVMAddressPersistence != nil { + in, out := &in.KubeVirtVMAddressPersistence, &out.KubeVirtVMAddressPersistence + *out = new(VMAddressPersistence) + **out = **in + } return } @@ -7530,6 +7736,7 @@ func (in *Tier) DeepCopyInto(out *Tier) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) return } @@ -7610,6 +7817,29 @@ func (in *TierSpec) DeepCopy() *TierSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TierStatus) DeepCopyInto(out *TierStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TierStatus. +func (in *TierStatus) DeepCopy() *TierStatus { + if in == nil { + return nil + } + out := new(TierStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UIDashboard) DeepCopyInto(out *UIDashboard) { *out = *in diff --git a/pkg/client/clientset_generated/clientset/typed/projectcalico/v3/tier.go b/pkg/client/clientset_generated/clientset/typed/projectcalico/v3/tier.go index 8635bb9d..fa03c1fe 100644 --- a/pkg/client/clientset_generated/clientset/typed/projectcalico/v3/tier.go +++ b/pkg/client/clientset_generated/clientset/typed/projectcalico/v3/tier.go @@ -25,6 +25,8 @@ type TiersGetter interface { type TierInterface interface { Create(ctx context.Context, tier *projectcalicov3.Tier, opts v1.CreateOptions) (*projectcalicov3.Tier, error) Update(ctx context.Context, tier *projectcalicov3.Tier, opts v1.UpdateOptions) (*projectcalicov3.Tier, error) + // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + UpdateStatus(ctx context.Context, tier *projectcalicov3.Tier, opts v1.UpdateOptions) (*projectcalicov3.Tier, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error Get(ctx context.Context, name string, opts v1.GetOptions) (*projectcalicov3.Tier, error) diff --git a/pkg/openapi/generated.openapi.go b/pkg/openapi/generated.openapi.go index 4e261867..e179e14c 100644 --- a/pkg/openapi/generated.openapi.go +++ b/pkg/openapi/generated.openapi.go @@ -44,11 +44,16 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPConfigurationSpec": schema_pkg_apis_projectcalico_v3_BGPConfigurationSpec(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPDaemonStatus": schema_pkg_apis_projectcalico_v3_BGPDaemonStatus(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilter": schema_pkg_apis_projectcalico_v3_BGPFilter(ref), + "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterAddCommunity": schema_pkg_apis_projectcalico_v3_BGPFilterAddCommunity(ref), + "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterCommunityMatch": schema_pkg_apis_projectcalico_v3_BGPFilterCommunityMatch(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterList": schema_pkg_apis_projectcalico_v3_BGPFilterList(ref), + "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterOperation": schema_pkg_apis_projectcalico_v3_BGPFilterOperation(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV4": schema_pkg_apis_projectcalico_v3_BGPFilterPrefixLengthV4(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV6": schema_pkg_apis_projectcalico_v3_BGPFilterPrefixLengthV6(ref), + "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrependASPath": schema_pkg_apis_projectcalico_v3_BGPFilterPrependASPath(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterRuleV4": schema_pkg_apis_projectcalico_v3_BGPFilterRuleV4(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterRuleV6": schema_pkg_apis_projectcalico_v3_BGPFilterRuleV6(ref), + "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterSetPriority": schema_pkg_apis_projectcalico_v3_BGPFilterSetPriority(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterSpec": schema_pkg_apis_projectcalico_v3_BGPFilterSpec(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPPassword": schema_pkg_apis_projectcalico_v3_BGPPassword(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPPeer": schema_pkg_apis_projectcalico_v3_BGPPeer(ref), @@ -257,6 +262,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/tigera/api/pkg/apis/projectcalico/v3.Tier": schema_pkg_apis_projectcalico_v3_Tier(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.TierList": schema_pkg_apis_projectcalico_v3_TierList(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.TierSpec": schema_pkg_apis_projectcalico_v3_TierSpec(ref), + "github.com/tigera/api/pkg/apis/projectcalico/v3.TierStatus": schema_pkg_apis_projectcalico_v3_TierStatus(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.UIDashboard": schema_pkg_apis_projectcalico_v3_UIDashboard(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.UIGraphLayer": schema_pkg_apis_projectcalico_v3_UIGraphLayer(ref), "github.com/tigera/api/pkg/apis/projectcalico/v3.UIGraphNode": schema_pkg_apis_projectcalico_v3_UIGraphNode(ref), @@ -780,6 +786,21 @@ func schema_pkg_apis_projectcalico_v3_AllocationAttribute(ref common.ReferenceCa }, }, }, + "alternateOwnerAttrs": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, }, }, @@ -1795,6 +1816,20 @@ func schema_pkg_apis_projectcalico_v3_BGPConfigurationSpec(ref common.ReferenceC Format: "", }, }, + "ipv4NormalRoutePriority": { + SchemaProps: spec.SchemaProps{ + Description: "IPv4NormalRoutePriority is the normal route priority (metric) that Felix uses for IPv4 workload routes. This must match the value configured in FelixConfiguration. BIRD uses this to identify elevated-priority routes during live migration and to override local workload routes with higher-priority BGP-learned routes. [Default: 1024]", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "ipv6NormalRoutePriority": { + SchemaProps: spec.SchemaProps{ + Description: "IPv6NormalRoutePriority is the normal route priority (metric) that Felix uses for IPv6 workload routes. This must match the value configured in FelixConfiguration. BIRD uses this to identify elevated-priority routes during live migration and to override local workload routes with higher-priority BGP-learned routes. [Default: 1024]", + Type: []string{"integer"}, + Format: "int32", + }, + }, }, }, }, @@ -1892,6 +1927,66 @@ func schema_pkg_apis_projectcalico_v3_BGPFilter(ref common.ReferenceCallback) co } } +func schema_pkg_apis_projectcalico_v3_BGPFilterAddCommunity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BGPFilterAddCommunity specifies a BGP community to add to a route.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the BGP community to add.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"value"}, + }, + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-map-type": "atomic", + }, + }, + }, + } +} + +func schema_pkg_apis_projectcalico_v3_BGPFilterCommunityMatch(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BGPFilterCommunityMatch specifies community-based match criteria for a BGP filter rule. Currently only a single community value is supported. A MatchOperator field may be introduced in the future to support anyOf/allOf semantics with multiple values.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "values": { + SchemaProps: spec.SchemaProps{ + Description: "Values is a list of BGP community values to match against.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"values"}, + }, + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-map-type": "atomic", + }, + }, + }, + } +} + func schema_pkg_apis_projectcalico_v3_BGPFilterList(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1941,6 +2036,44 @@ func schema_pkg_apis_projectcalico_v3_BGPFilterList(ref common.ReferenceCallback } } +func schema_pkg_apis_projectcalico_v3_BGPFilterOperation(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BGPFilterOperation is a discriminated union representing a single route modification. Exactly one field must be set.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "addCommunity": { + SchemaProps: spec.SchemaProps{ + Description: "AddCommunity adds the specified BGP community to the route.", + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterAddCommunity"), + }, + }, + "prependASPath": { + SchemaProps: spec.SchemaProps{ + Description: "PrependASPath prepends the specified AS numbers to the route's AS path.", + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrependASPath"), + }, + }, + "setPriority": { + SchemaProps: spec.SchemaProps{ + Description: "SetPriority sets the route's priority (metric), in the same units as the ...RoutePriority fields in FelixConfiguration.", + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterSetPriority"), + }, + }, + }, + }, + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-map-type": "atomic", + }, + }, + }, + Dependencies: []string{ + "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterAddCommunity", "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrependASPath", "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterSetPriority"}, + } +} + func schema_pkg_apis_projectcalico_v3_BGPFilterPrefixLengthV4(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1999,40 +2132,114 @@ func schema_pkg_apis_projectcalico_v3_BGPFilterPrefixLengthV6(ref common.Referen } } +func schema_pkg_apis_projectcalico_v3_BGPFilterPrependASPath(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BGPFilterPrependASPath specifies AS numbers to prepend to a route's AS path.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "prefix": { + SchemaProps: spec.SchemaProps{ + Description: "Prefix is the sequence of AS numbers to prepend to the route's AS path. The resulting path starts with these AS numbers in the order listed; e.g. [65000, 65001] produces the path \"65000 65001 \".", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + }, + Required: []string{"prefix"}, + }, + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-map-type": "atomic", + }, + }, + }, + } +} + func schema_pkg_apis_projectcalico_v3_BGPFilterRuleV4(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BGPFilterRuleV4 defines a BGP filter rule consisting a single IPv4 CIDR block and a filter action for this CIDR.", + Description: "BGPFilterRuleV4 defines a BGP filter rule consisting of match criteria, a terminal action, and optional operations to apply to matching routes.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "cidr": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "If non-empty, this filter rule will only apply when the route being exported or imported \"matches\" the given CIDR - where the definition of \"matches\" is according to MatchOperator and PrefixLength. CIDR should be in conventional CIDR notation, /.", + Type: []string{"string"}, + Format: "", }, }, "prefixLength": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV4"), + Description: "PrefixLength further constrains the CIDR match by restricting the range of allowed prefix lengths. For example, CIDR \"10.0.0.0/8\" with MatchOperator \"In\" and PrefixLength {min: 16, max: 24} matches any route within 10.0.0.0/8 whose prefix length is between /16 and /24. Requires CIDR to be set; if CIDR is omitted, PrefixLength is ignored. If PrefixLength is nil and CIDR is set, the CIDR's own prefix length is used as the minimum and /32 (for V4) as the maximum.", + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV4"), }, }, "source": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "If set to \"RemotePeers\": for export rules, this filter rule will only apply to routes learned from BGP peers (i.e. re-advertised routes), not locally originated routes. For import rules, this field is redundant because imported routes are by definition from BGP peers.", + Type: []string{"string"}, + Format: "", }, }, "interface": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "If non-empty, this filter rule will only apply to routes with an outgoing interface that matches Interface.", + Type: []string{"string"}, + Format: "", }, }, "matchOperator": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "MatchOperator defines how the route's prefix is compared against CIDR. \"Equal\" requires an exact prefix match, \"In\" requires the route to be contained within the CIDR (or equal), \"NotEqual\" and \"NotIn\" are their negations. Only meaningful when CIDR is also specified. Required when CIDR is set.", + Type: []string{"string"}, + Format: "", + }, + }, + "peerType": { + SchemaProps: spec.SchemaProps{ + Description: "If non-empty, this filter rule will only apply to routes being imported from or exported to a BGP peer of the specified type. If empty, the rule applies to all peers.", + Type: []string{"string"}, + Format: "", + }, + }, + "communities": { + SchemaProps: spec.SchemaProps{ + Description: "If set, this filter rule will only apply to routes that carry the specified BGP community. On import, this matches communities set by the remote peer. On export, this matches communities already present on the route, whether received from a BGP peer (e.g. on a route reflector re-advertising to an eBGP peer) or added locally by an import filter or an earlier export rule's AddCommunity operation.", + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterCommunityMatch"), + }, + }, + "asPathPrefix": { + SchemaProps: spec.SchemaProps{ + Description: "If non-empty, this filter rule will only apply to routes whose AS path begins with the specified sequence of AS numbers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + "priority": { + SchemaProps: spec.SchemaProps{ + Description: "If set, this filter rule will only apply to routes with the given priority, in the same units as the ...RoutePriority fields in FelixConfiguration.", + Type: []string{"integer"}, + Format: "int32", }, }, "action": { @@ -2042,6 +2249,20 @@ func schema_pkg_apis_projectcalico_v3_BGPFilterRuleV4(ref common.ReferenceCallba Format: "", }, }, + "operations": { + SchemaProps: spec.SchemaProps{ + Description: "Operations is an ordered list of route modifications to apply to matching routes before accepting them. Only valid when Action is \"Accept\"; specifying operations with \"Reject\" is rejected by validation. Each entry must set exactly one operation field.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterOperation"), + }, + }, + }, + }, + }, }, Required: []string{"action"}, }, @@ -2052,7 +2273,7 @@ func schema_pkg_apis_projectcalico_v3_BGPFilterRuleV4(ref common.ReferenceCallba }, }, Dependencies: []string{ - "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV4"}, + "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterCommunityMatch", "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterOperation", "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV4"}, } } @@ -2060,36 +2281,76 @@ func schema_pkg_apis_projectcalico_v3_BGPFilterRuleV6(ref common.ReferenceCallba return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BGPFilterRuleV6 defines a BGP filter rule consisting a single IPv6 CIDR block and a filter action for this CIDR.", + Description: "BGPFilterRuleV6 defines a BGP filter rule consisting of match criteria, a terminal action, and optional operations to apply to matching routes.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "cidr": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "If non-empty, this filter rule will only apply when the route being exported or imported \"matches\" the given CIDR - where the definition of \"matches\" is according to MatchOperator and PrefixLength. CIDR should be in conventional CIDR notation, /.", + Type: []string{"string"}, + Format: "", }, }, "prefixLength": { SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV6"), + Description: "PrefixLength further constrains the CIDR match by restricting the range of allowed prefix lengths. For example, CIDR \"fd00::/8\" with MatchOperator \"In\" and PrefixLength {min: 48, max: 64} matches any route within fd00::/8 whose prefix length is between /48 and /64. Requires CIDR to be set; if CIDR is omitted, PrefixLength is ignored. If PrefixLength is nil and CIDR is set, the CIDR's own prefix length is used as the minimum and /128 (for V6) as the maximum.", + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV6"), }, }, "source": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "If set to \"RemotePeers\": for export rules, this filter rule will only apply to routes learned from BGP peers (i.e. re-advertised routes), not locally originated routes. For import rules, this field is redundant because imported routes are by definition from BGP peers.", + Type: []string{"string"}, + Format: "", }, }, "interface": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "If non-empty, this filter rule will only apply to routes with an outgoing interface that matches Interface.", + Type: []string{"string"}, + Format: "", }, }, "matchOperator": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "MatchOperator defines how the route's prefix is compared against CIDR. \"Equal\" requires an exact prefix match, \"In\" requires the route to be contained within the CIDR (or equal), \"NotEqual\" and \"NotIn\" are their negations. Only meaningful when CIDR is also specified. Required when CIDR is set.", + Type: []string{"string"}, + Format: "", + }, + }, + "peerType": { + SchemaProps: spec.SchemaProps{ + Description: "If non-empty, this filter rule will only apply to routes being imported from or exported to a BGP peer of the specified type. If empty, the rule applies to all peers.", + Type: []string{"string"}, + Format: "", + }, + }, + "communities": { + SchemaProps: spec.SchemaProps{ + Description: "If set, this filter rule will only apply to routes that carry the specified BGP community. On import, this matches communities set by the remote peer. On export, this matches communities already present on the route, whether received from a BGP peer (e.g. on a route reflector re-advertising to an eBGP peer) or added locally by an import filter or an earlier export rule's AddCommunity operation.", + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterCommunityMatch"), + }, + }, + "asPathPrefix": { + SchemaProps: spec.SchemaProps{ + Description: "If non-empty, this filter rule will only apply to routes whose AS path begins with the specified sequence of AS numbers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + "priority": { + SchemaProps: spec.SchemaProps{ + Description: "If set, this filter rule will only apply to routes with the given priority, in the same units as the ...RoutePriority fields in FelixConfiguration.", + Type: []string{"integer"}, + Format: "int32", }, }, "action": { @@ -2099,6 +2360,20 @@ func schema_pkg_apis_projectcalico_v3_BGPFilterRuleV6(ref common.ReferenceCallba Format: "", }, }, + "operations": { + SchemaProps: spec.SchemaProps{ + Description: "Operations is an ordered list of route modifications to apply to matching routes before accepting them. Only valid when Action is \"Accept\"; specifying operations with \"Reject\" is rejected by validation. Each entry must set exactly one operation field.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterOperation"), + }, + }, + }, + }, + }, }, Required: []string{"action"}, }, @@ -2109,7 +2384,33 @@ func schema_pkg_apis_projectcalico_v3_BGPFilterRuleV6(ref common.ReferenceCallba }, }, Dependencies: []string{ - "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV6"}, + "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterCommunityMatch", "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterOperation", "github.com/tigera/api/pkg/apis/projectcalico/v3.BGPFilterPrefixLengthV6"}, + } +} + +func schema_pkg_apis_projectcalico_v3_BGPFilterSetPriority(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BGPFilterSetPriority specifies a route priority to set.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the priority to set, in the same units as FelixConfiguration's ...RoutePriority fields.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"value"}, + }, + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-map-type": "atomic", + }, + }, + }, } } @@ -6281,7 +6582,7 @@ func schema_pkg_apis_projectcalico_v3_FelixConfigurationSpec(ref common.Referenc }, "bpfKubeProxyHealthzPort": { SchemaProps: spec.SchemaProps{ - Description: "BPFKubeProxyHealthzPort, in BPF mode, controls the port that Felix's embedded kube-proxy health check server binds to. The health check server is used by external load balancers to determine if this node should receive traffic. [Default: 10256]", + Description: "BPFKubeProxyHealthzPort, in BPF mode, controls the port that Felix's embedded kube-proxy health check server binds to. The health check server is used by external load balancers to determine if this node should receive traffic. Set to 0 to disable the health check server. [Default: 10256]", Type: []string{"integer"}, Format: "int32", }, @@ -7213,6 +7514,40 @@ func schema_pkg_apis_projectcalico_v3_FelixConfigurationSpec(ref common.Referenc Format: "int32", }, }, + "ipv4NormalRoutePriority": { + SchemaProps: spec.SchemaProps{ + Description: "Route Priority value for a normal priority Calico-programmed IPv4 route. Note, higher values mean lower priority. [Default: 1024]", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "ipv4ElevatedRoutePriority": { + SchemaProps: spec.SchemaProps{ + Description: "Route Priority value for an elevated priority Calico-programmed IPv4 route. Note, higher values mean lower priority. Elevated priority is used during VM live migration, and for optimal behaviour IPv4ElevatedRoutePriority must be less than IPv4NormalRoutePriority [Default: 512]", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "ipv6NormalRoutePriority": { + SchemaProps: spec.SchemaProps{ + Description: "Route Priority value for a normal priority Calico-programmed IPv6 route. Note, higher values mean lower priority. [Default: 1024]", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "ipv6ElevatedRoutePriority": { + SchemaProps: spec.SchemaProps{ + Description: "Route Priority value for an elevated priority Calico-programmed IPv6 route. Note, higher values mean lower priority. Elevated priority is used during VM live migration, and for optimal behaviour IPv6ElevatedRoutePriority must be less than IPv6NormalRoutePriority [Default: 512]", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "liveMigrationRouteConvergenceTime": { + SchemaProps: spec.SchemaProps{ + Description: "LiveMigrationRouteConvergenceTime is the time to keep elevated route priority after a VM live migration completes. This allows routes to converge across the cluster before reverting to normal priority. [Default: 30s]", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, "wireguardEnabled": { SchemaProps: spec.SchemaProps{ Description: "WireguardEnabled controls whether Wireguard is enabled for IPv4 (encapsulating IPv4 traffic over an IPv4 underlay network). [Default: false]", @@ -9459,6 +9794,13 @@ func schema_pkg_apis_projectcalico_v3_IPAMConfigurationSpec(ref common.Reference Format: "", }, }, + "kubeVirtVMAddressPersistence": { + SchemaProps: spec.SchemaProps{ + Description: "KubeVirtVMAddressPersistence controls whether KubeVirt VirtualMachine workloads maintain persistent IP addresses across VM lifecycle events (reboot, migration, pod eviction). When Enabled, Calico automatically ensures that KubeVirt VMs retain their IP addresses when their underlying pods are recreated during VM operations. When Disabled, VMs receive new IP addresses whenever their pods are recreated, and creating a live migration target pod is not supported because the migration target pod requires the same IP as the source pod, which is only possible with address persistence. Defaults to Enabled if not specified.", + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"strictAffinity", "autoAllocateBlocks"}, }, @@ -14052,12 +14394,18 @@ func schema_pkg_apis_projectcalico_v3_Tier(ref common.ReferenceCallback) common. Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.TierSpec"), }, }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tigera/api/pkg/apis/projectcalico/v3.TierStatus"), + }, + }, }, Required: []string{"metadata", "spec"}, }, }, Dependencies: []string{ - "github.com/tigera/api/pkg/apis/projectcalico/v3.TierSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/tigera/api/pkg/apis/projectcalico/v3.TierSpec", "github.com/tigera/api/pkg/apis/projectcalico/v3.TierStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -14137,6 +14485,35 @@ func schema_pkg_apis_projectcalico_v3_TierSpec(ref common.ReferenceCallback) com } } +func schema_pkg_apis_projectcalico_v3_TierStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TierStatus contains the status of a Tier resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditions": { + SchemaProps: spec.SchemaProps{ + Description: "Conditions represents the latest observed set of conditions for the resource. A tier with a \"Ready\" condition set to \"True\" is operating as expected.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Condition"}, + } +} + func schema_pkg_apis_projectcalico_v3_UIDashboard(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{