From 3c673c8ba1a5273d1cfca8dd29eeda72dd65d3cd Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 12 Nov 2024 11:33:44 -0600 Subject: [PATCH 01/19] Initialize project with kubebuilder init. Command executed: kubebuilder init --domain datumapis.com \ --repo go.datum.net/network-services-operator \ --license none \ --owner 'Datum Technology, Inc.' \ --project-name network-services-operator --- .devcontainer/devcontainer.json | 25 ++ .devcontainer/post-install.sh | 23 ++ .dockerignore | 3 + .github/workflows/lint.yml | 23 ++ .github/workflows/test-e2e.yml | 35 +++ .github/workflows/test.yml | 23 ++ .golangci.yml | 47 +++ Dockerfile | 33 ++ Makefile | 212 +++++++++++++ PROJECT | 10 + README.md | 3 +- cmd/main.go | 141 +++++++++ config/default/kustomization.yaml | 177 +++++++++++ config/default/manager_metrics_patch.yaml | 4 + config/default/metrics_service.yaml | 17 + config/manager/kustomization.yaml | 2 + config/manager/manager.yaml | 95 ++++++ .../network-policy/allow-metrics-traffic.yaml | 26 ++ config/network-policy/kustomization.yaml | 2 + config/prometheus/kustomization.yaml | 2 + config/prometheus/monitor.yaml | 30 ++ config/rbac/kustomization.yaml | 20 ++ config/rbac/leader_election_role.yaml | 40 +++ config/rbac/leader_election_role_binding.yaml | 15 + config/rbac/metrics_auth_role.yaml | 17 + config/rbac/metrics_auth_role_binding.yaml | 12 + config/rbac/metrics_reader_role.yaml | 9 + config/rbac/role.yaml | 11 + config/rbac/role_binding.yaml | 15 + config/rbac/service_account.yaml | 8 + go.mod | 98 ++++++ go.sum | 251 +++++++++++++++ test/e2e/e2e_suite_test.go | 104 +++++++ test/e2e/e2e_test.go | 291 ++++++++++++++++++ test/utils/utils.go | 235 ++++++++++++++ 35 files changed, 2057 insertions(+), 2 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/post-install.sh create mode 100644 .dockerignore create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/test-e2e.yml create mode 100644 .github/workflows/test.yml create mode 100644 .golangci.yml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 PROJECT create mode 100644 cmd/main.go create mode 100644 config/default/kustomization.yaml create mode 100644 config/default/manager_metrics_patch.yaml create mode 100644 config/default/metrics_service.yaml create mode 100644 config/manager/kustomization.yaml create mode 100644 config/manager/manager.yaml create mode 100644 config/network-policy/allow-metrics-traffic.yaml create mode 100644 config/network-policy/kustomization.yaml create mode 100644 config/prometheus/kustomization.yaml create mode 100644 config/prometheus/monitor.yaml create mode 100644 config/rbac/kustomization.yaml create mode 100644 config/rbac/leader_election_role.yaml create mode 100644 config/rbac/leader_election_role_binding.yaml create mode 100644 config/rbac/metrics_auth_role.yaml create mode 100644 config/rbac/metrics_auth_role_binding.yaml create mode 100644 config/rbac/metrics_reader_role.yaml create mode 100644 config/rbac/role.yaml create mode 100644 config/rbac/role_binding.yaml create mode 100644 config/rbac/service_account.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 test/e2e/e2e_suite_test.go create mode 100644 test/e2e/e2e_test.go create mode 100644 test/utils/utils.go diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e2cdc09 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +{ + "name": "Kubebuilder DevContainer", + "image": "golang:1.22", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers/features/git:1": {} + }, + + "runArgs": ["--network=host"], + + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + "extensions": [ + "ms-kubernetes-tools.vscode-kubernetes-tools", + "ms-azuretools.vscode-docker" + ] + } + }, + + "onCreateCommand": "bash .devcontainer/post-install.sh" +} + diff --git a/.devcontainer/post-install.sh b/.devcontainer/post-install.sh new file mode 100644 index 0000000..265c43e --- /dev/null +++ b/.devcontainer/post-install.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -x + +curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 +chmod +x ./kind +mv ./kind /usr/local/bin/kind + +curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/amd64 +chmod +x kubebuilder +mv kubebuilder /usr/local/bin/ + +KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt) +curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl" +chmod +x kubectl +mv kubectl /usr/local/bin/kubectl + +docker network create -d=bridge --subnet=172.19.0.0/24 kind + +kind version +kubebuilder version +docker --version +go version +kubectl version --client diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a3aab7a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file +# Ignore build and test binaries. +bin/ diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..445e2be --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint + +on: + push: + pull_request: + +jobs: + lint: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '~1.23' + + - name: Run linter + uses: golangci/golangci-lint-action@v6 + with: + version: v1.61 diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml new file mode 100644 index 0000000..a86ebdd --- /dev/null +++ b/.github/workflows/test-e2e.yml @@ -0,0 +1,35 @@ +name: E2E Tests + +on: + push: + pull_request: + +jobs: + test-e2e: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '~1.23' + + - name: Install the latest version of kind + run: | + curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + + - name: Verify kind installation + run: kind version + + - name: Create kind cluster + run: kind create cluster + + - name: Running Test e2e + run: | + go mod tidy + make test-e2e diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..5781f67 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Tests + +on: + push: + pull_request: + +jobs: + test: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '~1.23' + + - name: Running Tests + run: | + go mod tidy + make test diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..6b29746 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,47 @@ +run: + timeout: 5m + allow-parallel-runners: true + +issues: + # don't skip warning about doc comments + # don't exclude the default set of lint + exclude-use-default: false + # restore some of the defaults + # (fill in the rest as needed) + exclude-rules: + - path: "api/*" + linters: + - lll + - path: "internal/*" + linters: + - dupl + - lll +linters: + disable-all: true + enable: + - dupl + - errcheck + - copyloopvar + - ginkgolinter + - goconst + - gocyclo + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - lll + - misspell + - nakedret + - prealloc + - revive + - staticcheck + - typecheck + - unconvert + - unparam + - unused + +linters-settings: + revive: + rules: + - name: comment-spacings diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5c73c7f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +# Build the manager binary +FROM golang:1.23 AS builder +ARG TARGETOS +ARG TARGETARCH + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY cmd/main.go cmd/main.go +COPY api/ api/ +COPY internal/ internal/ + +# Build +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..263ce35 --- /dev/null +++ b/Makefile @@ -0,0 +1,212 @@ +# Image URL to use all building/pushing image targets +IMG ?= controller:latest +# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. +ENVTEST_K8S_VERSION = 1.31.0 + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# CONTAINER_TOOL defines the container tool to be used for building images. +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) +CONTAINER_TOOL ?= docker + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +.PHONY: all +all: build + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk command is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development + +.PHONY: manifests +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object paths="./..." + +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: test +test: manifests generate fmt vet envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# Prometheus and CertManager are installed by default; skip with: +# - PROMETHEUS_INSTALL_SKIP=true +# - CERT_MANAGER_INSTALL_SKIP=true +.PHONY: test-e2e +test-e2e: manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. + @command -v kind >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @kind get clusters | grep -q 'kind' || { \ + echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + exit 1; \ + } + go test ./test/e2e/ -v -ginkgo.v + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix + +##@ Build + +.PHONY: build +build: manifests generate fmt vet ## Build manager binary. + go build -o bin/manager cmd/main.go + +.PHONY: run +run: manifests generate fmt vet ## Run a controller from your host. + go run ./cmd/main.go + +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +.PHONY: docker-build +docker-build: ## Build docker image with the manager. + $(CONTAINER_TOOL) build -t ${IMG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + $(CONTAINER_TOOL) push ${IMG} + +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name network-services-operator-builder + $(CONTAINER_TOOL) buildx use network-services-operator-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm network-services-operator-builder + rm Dockerfile.cross + +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml + +##@ Deployment + +ifndef ignore-not-found + ignore-not-found = false +endif + +.PHONY: install +install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - + +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: deploy +deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + +.PHONY: undeploy +undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +##@ Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUBECTL ?= kubectl +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.5.0 +CONTROLLER_TOOLS_VERSION ?= v0.16.4 +ENVTEST_VERSION ?= release-0.19 +GOLANGCI_LINT_VERSION ?= v1.61.0 + +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. +$(KUSTOMIZE): $(LOCALBIN) + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. +$(CONTROLLER_GEN): $(LOCALBIN) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + +.PHONY: envtest +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. +$(ENVTEST): $(LOCALBIN) + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + +.PHONY: golangci-lint +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) + +# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# $1 - target path with name of binary +# $2 - package url which can be installed +# $3 - specific version of package +define go-install-tool +@[ -f "$(1)-$(3)" ] || { \ +set -e; \ +package=$(2)@$(3) ;\ +echo "Downloading $${package}" ;\ +rm -f $(1) || true ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) +endef diff --git a/PROJECT b/PROJECT new file mode 100644 index 0000000..1b4fa94 --- /dev/null +++ b/PROJECT @@ -0,0 +1,10 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +domain: datumapis.com +layout: +- go.kubebuilder.io/v4 +projectName: network-services-operator +repo: go.datum.net/network-services-operator +version: "3" diff --git a/README.md b/README.md index ff3693c..27970be 100644 --- a/README.md +++ b/README.md @@ -1,2 +1 @@ -# network-services-operator -Datum Network Services +# Datum Network Services diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..adb4f84 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,141 @@ +package main + +import ( + "crypto/tls" + "flag" + "os" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/metrics/filters" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + // +kubebuilder:scaffold:scheme +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + var probeAddr string + var secureMetrics bool + var enableHTTP2 bool + var tlsOpts []func(*tls.Config) + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ + "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.BoolVar(&secureMetrics, "metrics-secure", true, + "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.BoolVar(&enableHTTP2, "enable-http2", false, + "If set, HTTP/2 will be enabled for the metrics and webhook servers") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + // if the enable-http2 flag is false (the default), http/2 should be disabled + // due to its vulnerabilities. More specifically, disabling http/2 will + // prevent from being vulnerable to the HTTP/2 Stream Cancellation and + // Rapid Reset CVEs. For more information see: + // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 + // - https://github.com/advisories/GHSA-4374-p667-p6c8 + disableHTTP2 := func(c *tls.Config) { + setupLog.Info("disabling http/2") + c.NextProtos = []string{"http/1.1"} + } + + if !enableHTTP2 { + tlsOpts = append(tlsOpts, disableHTTP2) + } + + webhookServer := webhook.NewServer(webhook.Options{ + TLSOpts: tlsOpts, + }) + + // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. + // More info: + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/server + // - https://book.kubebuilder.io/reference/metrics.html + metricsServerOptions := metricsserver.Options{ + BindAddress: metricsAddr, + SecureServing: secureMetrics, + TLSOpts: tlsOpts, + } + + if secureMetrics { + // FilterProvider is used to protect the metrics endpoint with authn/authz. + // These configurations ensure that only authorized users and service accounts + // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/filters#WithAuthenticationAndAuthorization + metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + + // TODO(user): If CertDir, CertName, and KeyName are not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + Metrics: metricsServerOptions, + WebhookServer: webhookServer, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "6a7d51cc.datumapis.com", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + // +kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml new file mode 100644 index 0000000..840d4f1 --- /dev/null +++ b/config/default/kustomization.yaml @@ -0,0 +1,177 @@ +# Adds namespace to all resources. +namespace: network-services-operator-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: network-services-operator- + +# Labels to add to all resources and selectors. +#labels: +#- includeSelectors: true +# pairs: +# someName: someValue + +resources: +#- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus +# [METRICS] Expose the controller manager metrics service. +- metrics_service.yaml +# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. +# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. +# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will +# be able to communicate with the Webhook Server. +#- ../network-policy + +# Uncomment the patches line if you enable Metrics, and/or are using webhooks and cert-manager +patches: +# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. +# More info: https://book.kubebuilder.io/reference/metrics +- path: manager_metrics_patch.yaml + target: + kind: Deployment + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- path: manager_webhook_patch.yaml + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +# Uncomment the following replacements to add the cert-manager CA injection annotations +#replacements: +# - source: # Uncomment the following block if you have any webhook +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # Name of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # Namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.name +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: CustomResourceDefinition +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.name +# targets: +# - select: +# kind: CustomResourceDefinition +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true diff --git a/config/default/manager_metrics_patch.yaml b/config/default/manager_metrics_patch.yaml new file mode 100644 index 0000000..2aaef65 --- /dev/null +++ b/config/default/manager_metrics_patch.yaml @@ -0,0 +1,4 @@ +# This patch adds the args to allow exposing the metrics endpoint using HTTPS +- op: add + path: /spec/template/spec/containers/0/args/0 + value: --metrics-bind-address=:8443 diff --git a/config/default/metrics_service.yaml b/config/default/metrics_service.yaml new file mode 100644 index 0000000..c9e401e --- /dev/null +++ b/config/default/metrics_service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: 8443 + selector: + control-plane: controller-manager diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml new file mode 100644 index 0000000..5c5f0b8 --- /dev/null +++ b/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml new file mode 100644 index 0000000..5515d8d --- /dev/null +++ b/config/manager/manager.yaml @@ -0,0 +1,95 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize +spec: + selector: + matchLabels: + control-plane: controller-manager + replicas: 1 + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + control-plane: controller-manager + spec: + # TODO(user): Uncomment the following code to configure the nodeAffinity expression + # according to the platforms which are supported by your solution. + # It is considered best practice to support multiple architectures. You can + # build your manager image using the makefile target docker-buildx. + # affinity: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/arch + # operator: In + # values: + # - amd64 + # - arm64 + # - ppc64le + # - s390x + # - key: kubernetes.io/os + # operator: In + # values: + # - linux + securityContext: + runAsNonRoot: true + # TODO(user): For common cases that do not require escalating privileges + # it is recommended to ensure that all your Pods/Containers are restrictive. + # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + # Please uncomment the following code if your project does NOT have to work on old Kubernetes + # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). + # seccompProfile: + # type: RuntimeDefault + containers: + - command: + - /manager + args: + - --leader-elect + - --health-probe-bind-address=:8081 + image: controller:latest + name: manager + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + # TODO(user): Configure the resources accordingly based on the project requirements. + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + serviceAccountName: controller-manager + terminationGracePeriodSeconds: 10 diff --git a/config/network-policy/allow-metrics-traffic.yaml b/config/network-policy/allow-metrics-traffic.yaml new file mode 100644 index 0000000..09c5efd --- /dev/null +++ b/config/network-policy/allow-metrics-traffic.yaml @@ -0,0 +1,26 @@ +# This NetworkPolicy allows ingress traffic +# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those +# namespaces are able to gathering data from the metrics endpoint. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: allow-metrics-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label metrics: enabled + - from: + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label + ports: + - port: 8443 + protocol: TCP diff --git a/config/network-policy/kustomization.yaml b/config/network-policy/kustomization.yaml new file mode 100644 index 0000000..ec0fb5e --- /dev/null +++ b/config/network-policy/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- allow-metrics-traffic.yaml diff --git a/config/prometheus/kustomization.yaml b/config/prometheus/kustomization.yaml new file mode 100644 index 0000000..ed13716 --- /dev/null +++ b/config/prometheus/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- monitor.yaml diff --git a/config/prometheus/monitor.yaml b/config/prometheus/monitor.yaml new file mode 100644 index 0000000..822619d --- /dev/null +++ b/config/prometheus/monitor.yaml @@ -0,0 +1,30 @@ +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https # Ensure this is the name of the port that exposes HTTPS metrics + scheme: https + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + tlsConfig: + # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables + # certificate verification. This poses a significant security risk by making the system vulnerable to + # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between + # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, + # compromising the integrity and confidentiality of the information. + # Please use the following options for secure configurations: + # caFile: /etc/metrics-certs/ca.crt + # certFile: /etc/metrics-certs/tls.crt + # keyFile: /etc/metrics-certs/tls.key + insecureSkipVerify: true + selector: + matchLabels: + control-plane: controller-manager diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml new file mode 100644 index 0000000..5619aa0 --- /dev/null +++ b/config/rbac/kustomization.yaml @@ -0,0 +1,20 @@ +resources: +# All RBAC will be applied under this service account in +# the deployment namespace. You may comment out this resource +# if your manager will use a service account that exists at +# runtime. Be sure to update RoleBinding and ClusterRoleBinding +# subjects if changing service account names. +- service_account.yaml +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# The following RBAC configurations are used to protect +# the metrics endpoint with authn/authz. These configurations +# ensure that only authorized users and service accounts +# can access the metrics endpoint. Comment the following +# permissions if you want to disable this protection. +# More info: https://book.kubebuilder.io/reference/metrics.html +- metrics_auth_role.yaml +- metrics_auth_role_binding.yaml +- metrics_reader_role.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml new file mode 100644 index 0000000..86bf6c4 --- /dev/null +++ b/config/rbac/leader_election_role.yaml @@ -0,0 +1,40 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 0000000..6933429 --- /dev/null +++ b/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/metrics_auth_role.yaml b/config/rbac/metrics_auth_role.yaml new file mode 100644 index 0000000..32d2e4e --- /dev/null +++ b/config/rbac/metrics_auth_role.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-auth-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create diff --git a/config/rbac/metrics_auth_role_binding.yaml b/config/rbac/metrics_auth_role_binding.yaml new file mode 100644 index 0000000..e775d67 --- /dev/null +++ b/config/rbac/metrics_auth_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: metrics-auth-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metrics-auth-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/metrics_reader_role.yaml b/config/rbac/metrics_reader_role.yaml new file mode 100644 index 0000000..51a75db --- /dev/null +++ b/config/rbac/metrics_reader_role.yaml @@ -0,0 +1,9 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: +- nonResourceURLs: + - "/metrics" + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml new file mode 100644 index 0000000..226f266 --- /dev/null +++ b/config/rbac/role.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: manager-role +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml new file mode 100644 index 0000000..29aaca1 --- /dev/null +++ b/config/rbac/role_binding.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml new file mode 100644 index 0000000..ffcac6e --- /dev/null +++ b/config/rbac/service_account.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager + namespace: system diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fbff839 --- /dev/null +++ b/go.mod @@ -0,0 +1,98 @@ +module go.datum.net/network-services-operator + +go 1.23.0 + +require ( + github.com/onsi/ginkgo/v2 v2.19.0 + github.com/onsi/gomega v1.33.1 + k8s.io/apimachinery v0.31.0 + k8s.io/client-go v0.31.0 + sigs.k8s.io/controller-runtime v0.19.1 +) + +require ( + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.31.0 // indirect + k8s.io/apiextensions-apiserver v0.31.0 // indirect + k8s.io/apiserver v0.31.0 // indirect + k8s.io/component-base v0.31.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0958667 --- /dev/null +++ b/go.sum @@ -0,0 +1,251 @@ +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= +k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= +k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= +k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= +k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= +k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= +k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= +k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= +k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk= +sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go new file mode 100644 index 0000000..503f937 --- /dev/null +++ b/test/e2e/e2e_suite_test.go @@ -0,0 +1,104 @@ +package e2e + +import ( + "fmt" + "os" + "os/exec" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "go.datum.net/network-services-operator/test/utils" +) + +var ( + // Optional Environment Variables: + // - PROMETHEUS_INSTALL_SKIP=true: Skips Prometheus Operator installation during test setup. + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if Prometheus or CertManager is already installed, avoiding + // re-installation and conflicts. + skipPrometheusInstall = os.Getenv("PROMETHEUS_INSTALL_SKIP") == "true" + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + // isPrometheusOperatorAlreadyInstalled will be set true when prometheus CRDs be found on the cluster + isPrometheusOperatorAlreadyInstalled = false + // isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster + isCertManagerAlreadyInstalled = false + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/network-services-operator:v0.0.1" +) + +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager and Prometheus. +func TestE2E(t *testing.T) { + RegisterFailHandler(Fail) + _, _ = fmt.Fprintf(GinkgoWriter, "Starting network-services-operator integration test suite\n") + RunSpecs(t, "e2e suite") +} + +var _ = BeforeSuite(func() { + By("Ensure that Prometheus is enabled") + _ = utils.UncommentCode("config/default/kustomization.yaml", "#- ../prometheus", "#") + + By("generating files") + cmd := exec.Command("make", "generate") + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make generate") + + By("generating manifests") + cmd = exec.Command("make", "manifests") + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to run make manifests") + + By("building the manager(Operator) image") + cmd = exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing. + // To prevent errors when tests run in environments with Prometheus or CertManager already installed, + // we check for their presence before execution. + // Setup Prometheus and CertManager before the suite if not skipped and if not already installed + if !skipPrometheusInstall { + By("checking if prometheus is installed already") + isPrometheusOperatorAlreadyInstalled = utils.IsPrometheusCRDsInstalled() + if !isPrometheusOperatorAlreadyInstalled { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing Prometheus Operator...\n") + Expect(utils.InstallPrometheusOperator()).To(Succeed(), "Failed to install Prometheus Operator") + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: Prometheus Operator is already installed. Skipping installation...\n") + } + } + if !skipCertManagerInstall { + By("checking if cert manager is installed already") + isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled() + if !isCertManagerAlreadyInstalled { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: CertManager is already installed. Skipping installation...\n") + } + } +}) + +var _ = AfterSuite(func() { + // Teardown Prometheus and CertManager after the suite if not skipped and if they were not already installed + if !skipPrometheusInstall && !isPrometheusOperatorAlreadyInstalled { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling Prometheus Operator...\n") + utils.UninstallPrometheusOperator() + } + if !skipCertManagerInstall && !isCertManagerAlreadyInstalled { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go new file mode 100644 index 0000000..8846f0f --- /dev/null +++ b/test/e2e/e2e_test.go @@ -0,0 +1,291 @@ +package e2e + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "go.datum.net/network-services-operator/test/utils" +) + +// namespace where the project is deployed in +const namespace = "network-services-operator-system" + +// serviceAccountName created for the project +const serviceAccountName = "network-services-operator-controller-manager" + +// metricsServiceName is the name of the metrics service of the project +const metricsServiceName = "network-services-operator-controller-manager-metrics-service" + +// metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data +const metricsRoleBindingName = "network-services-operator-metrics-binding" + +var _ = Describe("Manager", Ordered, func() { + var controllerPodName string + + // Before running the tests, set up the environment by creating the namespace, + // installing CRDs, and deploying the controller. + BeforeAll(func() { + By("creating manager namespace") + cmd := exec.Command("kubectl", "create", "ns", namespace) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") + }) + + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. + AfterAll(func() { + By("cleaning up the curl pod for metrics") + cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace) + _, _ = utils.Run(cmd) + + By("undeploying the controller-manager") + cmd = exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) + + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) + + By("removing manager namespace") + cmd = exec.Command("kubectl", "delete", "ns", namespace) + _, _ = utils.Run(cmd) + }) + + // After each test, check for failures and collect logs, events, + // and pod descriptions for debugging. + AfterEach(func() { + specReport := CurrentSpecReport() + if specReport.Failed() { + By("Fetching controller manager pod logs") + cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) + controllerLogs, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Controller logs:\n %s", controllerLogs)) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Controller logs: %s", err)) + } + + By("Fetching Kubernetes events") + cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp") + eventsOutput, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Kubernetes events:\n%s", eventsOutput)) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Kubernetes events: %s", err)) + } + + By("Fetching curl-metrics logs") + cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) + metricsOutput, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Metrics logs:\n %s", metricsOutput)) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get curl-metrics logs: %s", err)) + } + + By("Fetching controller manager pod description") + cmd = exec.Command("kubectl", "describe", "pod", controllerPodName, "-n", namespace) + podDescription, err := utils.Run(cmd) + if err == nil { + fmt.Println("Pod description:\n", podDescription) + } else { + fmt.Println("Failed to describe controller pod") + } + } + }) + + SetDefaultEventuallyTimeout(2 * time.Minute) + SetDefaultEventuallyPollingInterval(time.Second) + + Context("Manager", func() { + It("should run successfully", func() { + By("validating that the controller-manager pod is running as expected") + verifyControllerUp := func(g Gomega) { + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", + "pods", "-l", "control-plane=controller-manager", + "-o", "go-template={{ range .items }}"+ + "{{ if not .metadata.deletionTimestamp }}"+ + "{{ .metadata.name }}"+ + "{{ \"\\n\" }}{{ end }}{{ end }}", + "-n", namespace, + ) + + podOutput, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") + podNames := utils.GetNonEmptyLines(podOutput) + g.Expect(podNames).To(HaveLen(1), "expected 1 controller pod running") + controllerPodName = podNames[0] + g.Expect(controllerPodName).To(ContainSubstring("controller-manager")) + + // Validate the pod's status + cmd = exec.Command("kubectl", "get", + "pods", controllerPodName, "-o", "jsonpath={.status.phase}", + "-n", namespace, + ) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal("Running"), "Incorrect controller-manager pod status") + } + Eventually(verifyControllerUp).Should(Succeed()) + }) + + It("should ensure the metrics endpoint is serving metrics", func() { + By("creating a ClusterRoleBinding for the service account to allow access to metrics") + cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName, + "--clusterrole=network-services-operator-metrics-reader", + fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName), + ) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding") + + By("validating that the metrics service is available") + cmd = exec.Command("kubectl", "get", "service", metricsServiceName, "-n", namespace) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Metrics service should exist") + + By("validating that the ServiceMonitor for Prometheus is applied in the namespace") + cmd = exec.Command("kubectl", "get", "ServiceMonitor", "-n", namespace) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "ServiceMonitor should exist") + + By("getting the service account token") + token, err := serviceAccountToken() + Expect(err).NotTo(HaveOccurred()) + Expect(token).NotTo(BeEmpty()) + + By("waiting for the metrics endpoint to be ready") + verifyMetricsEndpointReady := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "endpoints", metricsServiceName, "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(ContainSubstring("8443"), "Metrics endpoint is not ready") + } + Eventually(verifyMetricsEndpointReady).Should(Succeed()) + + By("verifying that the controller manager is serving the metrics server") + verifyMetricsServerStarted := func(g Gomega) { + cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(ContainSubstring("controller-runtime.metrics\tServing metrics server"), + "Metrics server not yet started") + } + Eventually(verifyMetricsServerStarted).Should(Succeed()) + + By("creating the curl-metrics pod to access the metrics endpoint") + cmd = exec.Command("kubectl", "run", "curl-metrics", "--restart=Never", + "--namespace", namespace, + "--image=curlimages/curl:7.78.0", + "--", "/bin/sh", "-c", fmt.Sprintf( + "curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics", + token, metricsServiceName, namespace)) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod") + + By("waiting for the curl-metrics pod to complete.") + verifyCurlUp := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "pods", "curl-metrics", + "-o", "jsonpath={.status.phase}", + "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal("Succeeded"), "curl pod in wrong status") + } + Eventually(verifyCurlUp, 5*time.Minute).Should(Succeed()) + + By("getting the metrics by checking curl-metrics logs") + metricsOutput := getMetricsOutput() + Expect(metricsOutput).To(ContainSubstring( + "controller_runtime_reconcile_total", + )) + }) + + // +kubebuilder:scaffold:e2e-webhooks-checks + + // TODO: Customize the e2e test suite with scenarios specific to your project. + // Consider applying sample/CR(s) and check their status and/or verifying + // the reconciliation by using the metrics, i.e.: + // metricsOutput := getMetricsOutput() + // Expect(metricsOutput).To(ContainSubstring( + // fmt.Sprintf(`controller_runtime_reconcile_total{controller="%s",result="success"} 1`, + // strings.ToLower(), + // )) + }) +}) + +// serviceAccountToken returns a token for the specified service account in the given namespace. +// It uses the Kubernetes TokenRequest API to generate a token by directly sending a request +// and parsing the resulting token from the API response. +func serviceAccountToken() (string, error) { + const tokenRequestRawString = `{ + "apiVersion": "authentication.k8s.io/v1", + "kind": "TokenRequest" + }` + + // Temporary file to store the token request + secretName := fmt.Sprintf("%s-token-request", serviceAccountName) + tokenRequestFile := filepath.Join("/tmp", secretName) + err := os.WriteFile(tokenRequestFile, []byte(tokenRequestRawString), os.FileMode(0o644)) + if err != nil { + return "", err + } + + var out string + verifyTokenCreation := func(g Gomega) { + // Execute kubectl command to create the token + cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf( + "/api/v1/namespaces/%s/serviceaccounts/%s/token", + namespace, + serviceAccountName, + ), "-f", tokenRequestFile) + + output, err := cmd.CombinedOutput() + g.Expect(err).NotTo(HaveOccurred()) + + // Parse the JSON output to extract the token + var token tokenRequest + err = json.Unmarshal([]byte(output), &token) + g.Expect(err).NotTo(HaveOccurred()) + + out = token.Status.Token + } + Eventually(verifyTokenCreation).Should(Succeed()) + + return out, err +} + +// getMetricsOutput retrieves and returns the logs from the curl pod used to access the metrics endpoint. +func getMetricsOutput() string { + By("getting the curl-metrics logs") + cmd := exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) + metricsOutput, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod") + Expect(metricsOutput).To(ContainSubstring("< HTTP/1.1 200 OK")) + return metricsOutput +} + +// tokenRequest is a simplified representation of the Kubernetes TokenRequest API response, +// containing only the token field that we need to extract. +type tokenRequest struct { + Status struct { + Token string `json:"token"` + } `json:"status"` +} diff --git a/test/utils/utils.go b/test/utils/utils.go new file mode 100644 index 0000000..ac40e50 --- /dev/null +++ b/test/utils/utils.go @@ -0,0 +1,235 @@ +package utils + +import ( + "bufio" + "bytes" + "fmt" + "os" + "os/exec" + "strings" + + . "github.com/onsi/ginkgo/v2" //nolint:golint,revive +) + +const ( + prometheusOperatorVersion = "v0.77.1" + prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" + + "releases/download/%s/bundle.yaml" + + certmanagerVersion = "v1.16.0" + certmanagerURLTmpl = "https://github.com/jetstack/cert-manager/releases/download/%s/cert-manager.yaml" +) + +func warnError(err error) { + _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err) +} + +// Run executes the provided command within this context +func Run(cmd *exec.Cmd) (string, error) { + dir, _ := GetProjectDir() + cmd.Dir = dir + + if err := os.Chdir(cmd.Dir); err != nil { + _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %s\n", err) + } + + cmd.Env = append(os.Environ(), "GO111MODULE=on") + command := strings.Join(cmd.Args, " ") + _, _ = fmt.Fprintf(GinkgoWriter, "running: %s\n", command) + output, err := cmd.CombinedOutput() + if err != nil { + return string(output), fmt.Errorf("%s failed with error: (%v) %s", command, err, string(output)) + } + + return string(output), nil +} + +// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics. +func InstallPrometheusOperator() error { + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "create", "-f", url) + _, err := Run(cmd) + return err +} + +// UninstallPrometheusOperator uninstalls the prometheus +func UninstallPrometheusOperator() { + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "delete", "-f", url) + if _, err := Run(cmd); err != nil { + warnError(err) + } +} + +// IsPrometheusCRDsInstalled checks if any Prometheus CRDs are installed +// by verifying the existence of key CRDs related to Prometheus. +func IsPrometheusCRDsInstalled() bool { + // List of common Prometheus CRDs + prometheusCRDs := []string{ + "prometheuses.monitoring.coreos.com", + "prometheusrules.monitoring.coreos.com", + "prometheusagents.monitoring.coreos.com", + } + + cmd := exec.Command("kubectl", "get", "crds", "-o", "custom-columns=NAME:.metadata.name") + output, err := Run(cmd) + if err != nil { + return false + } + crdList := GetNonEmptyLines(string(output)) + for _, crd := range prometheusCRDs { + for _, line := range crdList { + if strings.Contains(line, crd) { + return true + } + } + } + + return false +} + +// UninstallCertManager uninstalls the cert manager +func UninstallCertManager() { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + cmd := exec.Command("kubectl", "delete", "-f", url) + if _, err := Run(cmd); err != nil { + warnError(err) + } +} + +// InstallCertManager installs the cert manager bundle. +func InstallCertManager() error { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + cmd := exec.Command("kubectl", "apply", "-f", url) + if _, err := Run(cmd); err != nil { + return err + } + // Wait for cert-manager-webhook to be ready, which can take time if cert-manager + // was re-installed after uninstalling on a cluster. + cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook", + "--for", "condition=Available", + "--namespace", "cert-manager", + "--timeout", "5m", + ) + + _, err := Run(cmd) + return err +} + +// IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed +// by verifying the existence of key CRDs related to Cert Manager. +func IsCertManagerCRDsInstalled() bool { + // List of common Cert Manager CRDs + certManagerCRDs := []string{ + "certificates.cert-manager.io", + "issuers.cert-manager.io", + "clusterissuers.cert-manager.io", + "certificaterequests.cert-manager.io", + "orders.acme.cert-manager.io", + "challenges.acme.cert-manager.io", + } + + // Execute the kubectl command to get all CRDs + cmd := exec.Command("kubectl", "get", "crds") + output, err := Run(cmd) + if err != nil { + return false + } + + // Check if any of the Cert Manager CRDs are present + crdList := GetNonEmptyLines(string(output)) + for _, crd := range certManagerCRDs { + for _, line := range crdList { + if strings.Contains(line, crd) { + return true + } + } + } + + return false +} + +// LoadImageToKindClusterWithName loads a local docker image to the kind cluster +func LoadImageToKindClusterWithName(name string) error { + cluster := "kind" + if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { + cluster = v + } + kindOptions := []string{"load", "docker-image", name, "--name", cluster} + cmd := exec.Command("kind", kindOptions...) + _, err := Run(cmd) + return err +} + +// GetNonEmptyLines converts given command output string into individual objects +// according to line breakers, and ignores the empty elements in it. +func GetNonEmptyLines(output string) []string { + var res []string + elements := strings.Split(output, "\n") + for _, element := range elements { + if element != "" { + res = append(res, element) + } + } + + return res +} + +// GetProjectDir will return the directory where the project is +func GetProjectDir() (string, error) { + wd, err := os.Getwd() + if err != nil { + return wd, err + } + wd = strings.Replace(wd, "/test/e2e", "", -1) + return wd, nil +} + +// UncommentCode searches for target in the file and remove the comment prefix +// of the target content. The target content may span multiple lines. +func UncommentCode(filename, target, prefix string) error { + // false positive + // nolint:gosec + content, err := os.ReadFile(filename) + if err != nil { + return err + } + strContent := string(content) + + idx := strings.Index(strContent, target) + if idx < 0 { + return fmt.Errorf("unable to find the code %s to be uncomment", target) + } + + out := new(bytes.Buffer) + _, err = out.Write(content[:idx]) + if err != nil { + return err + } + + scanner := bufio.NewScanner(bytes.NewBufferString(target)) + if !scanner.Scan() { + return nil + } + for { + _, err := out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)) + if err != nil { + return err + } + // Avoid writing a newline in case the previous line was the last in target. + if !scanner.Scan() { + break + } + if _, err := out.WriteString("\n"); err != nil { + return err + } + } + + _, err = out.Write(content[idx+len(target):]) + if err != nil { + return err + } + // false positive + // nolint:gosec + return os.WriteFile(filename, out.Bytes(), 0644) +} From 2fb10f99c2fce88d0b6539d859fc63ea60e62d40 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 12 Nov 2024 17:47:21 +0000 Subject: [PATCH 02/19] Add internal path as required for builds. --- internal/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 internal/.gitkeep diff --git a/internal/.gitkeep b/internal/.gitkeep new file mode 100644 index 0000000..e69de29 From 85b6a27a10e3ce2dd41fa168189d9593765c751f Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 12 Nov 2024 22:21:49 +0000 Subject: [PATCH 03/19] Bumped Golang version in dev container, updated golangci-lint version, addressed linter violations. --- .devcontainer/devcontainer.json | 10 ++++------ .gitignore | 2 ++ Makefile | 2 +- test/e2e/e2e_test.go | 14 +++++++------- test/utils/utils.go | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e2cdc09..aa1480a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,13 +1,13 @@ { "name": "Kubebuilder DevContainer", - "image": "golang:1.22", + "image": "golang:1.23", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/devcontainers/features/git:1": {} }, - - "runArgs": ["--network=host"], - + "runArgs": [ + "--network=host" + ], "customizations": { "vscode": { "settings": { @@ -19,7 +19,5 @@ ] } }, - "onCreateCommand": "bash .devcontainer/post-install.sh" } - diff --git a/.gitignore b/.gitignore index 6f72f89..2b0c6e4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ go.work.sum # env file .env + +bin/ diff --git a/Makefile b/Makefile index 263ce35..bb3ee77 100644 --- a/Makefile +++ b/Makefile @@ -173,7 +173,7 @@ GOLANGCI_LINT = $(LOCALBIN)/golangci-lint KUSTOMIZE_VERSION ?= v5.5.0 CONTROLLER_TOOLS_VERSION ?= v0.16.4 ENVTEST_VERSION ?= release-0.19 -GOLANGCI_LINT_VERSION ?= v1.61.0 +GOLANGCI_LINT_VERSION ?= v1.62.0 .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 8846f0f..8625529 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -77,27 +77,27 @@ var _ = Describe("Manager", Ordered, func() { cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) controllerLogs, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Controller logs:\n %s", controllerLogs)) + _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Controller logs: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s", err) } By("Fetching Kubernetes events") cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp") eventsOutput, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Kubernetes events:\n%s", eventsOutput)) + _, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n%s", eventsOutput) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get Kubernetes events: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Kubernetes events: %s", err) } By("Fetching curl-metrics logs") cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) metricsOutput, err := utils.Run(cmd) if err == nil { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Metrics logs:\n %s", metricsOutput)) + _, _ = fmt.Fprintf(GinkgoWriter, "Metrics logs:\n %s", metricsOutput) } else { - _, _ = fmt.Fprintf(GinkgoWriter, fmt.Sprintf("Failed to get curl-metrics logs: %s", err)) + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err) } By("Fetching controller manager pod description") @@ -262,7 +262,7 @@ func serviceAccountToken() (string, error) { // Parse the JSON output to extract the token var token tokenRequest - err = json.Unmarshal([]byte(output), &token) + err = json.Unmarshal(output, &token) g.Expect(err).NotTo(HaveOccurred()) out = token.Status.Token diff --git a/test/utils/utils.go b/test/utils/utils.go index ac40e50..e44ad29 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -76,7 +76,7 @@ func IsPrometheusCRDsInstalled() bool { if err != nil { return false } - crdList := GetNonEmptyLines(string(output)) + crdList := GetNonEmptyLines(output) for _, crd := range prometheusCRDs { for _, line := range crdList { if strings.Contains(line, crd) { @@ -137,7 +137,7 @@ func IsCertManagerCRDsInstalled() bool { } // Check if any of the Cert Manager CRDs are present - crdList := GetNonEmptyLines(string(output)) + crdList := GetNonEmptyLines(output) for _, crd := range certManagerCRDs { for _, line := range crdList { if strings.Contains(line, crd) { From 062943c38cb6140dcb9e72b6995fa10f1ea14a81 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 12 Nov 2024 22:38:16 +0000 Subject: [PATCH 04/19] Disable e2e suite until types and controllers are defined. --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index bb3ee77..1274fc8 100644 --- a/Makefile +++ b/Makefile @@ -70,15 +70,15 @@ test: manifests generate fmt vet envtest ## Run tests. # - CERT_MANAGER_INSTALL_SKIP=true .PHONY: test-e2e test-e2e: manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. - @command -v kind >/dev/null 2>&1 || { \ - echo "Kind is not installed. Please install Kind manually."; \ - exit 1; \ - } - @kind get clusters | grep -q 'kind' || { \ - echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ - exit 1; \ - } - go test ./test/e2e/ -v -ginkgo.v + # @command -v kind >/dev/null 2>&1 || { \ + # echo "Kind is not installed. Please install Kind manually."; \ + # exit 1; \ + # } + # @kind get clusters | grep -q 'kind' || { \ + # echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ + # exit 1; \ + # } + # go test ./test/e2e/ -v -ginkgo.v .PHONY: lint lint: golangci-lint ## Run golangci-lint linter From 6c379953bdeede499c16848833af4d4a26e5f582 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 12 Nov 2024 22:50:39 +0000 Subject: [PATCH 05/19] Update devcontainer to include zsh shell and act tool. --- .devcontainer/devcontainer.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index aa1480a..20c5d9b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,7 +3,15 @@ "image": "golang:1.23", "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, - "ghcr.io/devcontainers/features/git:1": {} + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/common-utils": { + "installOhMyZsh": true, + "configureZshAsDefaultShell": true, + "installOhMyZshConfig": true, + "installZsh": true, + "upgradePackages": true + }, + "ghcr.io/dhoeric/features/act": {} }, "runArgs": [ "--network=host" From 0e6196663ecb000008275066e3b3965483fc6068 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Wed, 13 Nov 2024 18:00:44 +0000 Subject: [PATCH 06/19] Initial commit of Datum Networking APIs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Network` - The logical “VPC Network” for Datum. Instances and other services can reference networks they’d like to attach to. - A Network will be created in the upstream control plane by users, or platform defaulting controllers (creating a default network). - `NetworkBinding` - An association between a network and a resource which desires to interact with it in some way. A NetworkBinding defines a topology similar to a NetworkContext, and is associated with a NetworkContext of a matching topology. - This type will be created in the upstream control plane by `workload-operator` when a `WorkloadDeployment` is associated with a cluster. Future services that need to attach to networks may also create NetworkBindings to signal this need. - `NetworkContext` - A part of a network specific to a certain topology. Topology is defined as a map of string keys and values, where the hash of the keys and values defines a unique context within the network. - This type will be created in the upstream control plane by `network-services-operator` as a result of observing `NetworkBindings` - Example topology keys are: - `topology.datum.net/city-code` - The IATA airport code for where the context exists. - `topology.datum.net/cluster-name` - The name of the cluster the context is associated with. - `topology.datum.net/cluster-namespace` - The namespace for the cluster the context is associated with. - `NetworkPolicy` - Not yet fully defined for namespace scoped network policies, but types exist to be leveraged by types defined in other controllers such as a network interface's network policy. - `Subnet` - A subnet is associated with a `NetworkContext`. Each subnet will have a subnet class to indicate the type of prefix that should be issued. Subnets include topology information, which is used when allocating `SubnetClaims`. - This type will be created in the upstream control plane by `network-services-operator` as the result of `SubnetClaim` observations. External controllers, such as `infra-provider-gcp` will observe these subnets, and program the network as required. - `SubnetClaim` - A request for a subnet to be issued, including the network context, subnet class, and topology information such as `gcp.topology.datum.net/region`. - This type will be created in the upstream control plane by external controllers such as `infra-provider-gcp` when a subnet is required to provision requested resources. --- PROJECT | 55 ++ api/v1alpha/groupversion_info.go | 22 + api/v1alpha/network_types.go | 118 +++ api/v1alpha/networkbinding_types.go | 87 ++ api/v1alpha/networkcontext_types.go | 78 ++ api/v1alpha/networkpolicy_types.go | 121 +++ api/v1alpha/subnet_types.go | 82 ++ api/v1alpha/subnetclaim_types.go | 81 ++ api/v1alpha/zz_generated.deepcopy.go | 873 ++++++++++++++++++ ...working.datumapis.com_networkbindings.yaml | 150 +++ ...working.datumapis.com_networkcontexts.yaml | 129 +++ ...working.datumapis.com_networkpolicies.yaml | 49 + .../networking.datumapis.com_networks.yaml | 150 +++ ...networking.datumapis.com_subnetclaims.yaml | 159 ++++ .../networking.datumapis.com_subnets.yaml | 152 +++ config/crd/kustomization.yaml | 25 + config/crd/kustomizeconfig.yaml | 19 + config/default/kustomization.yaml | 2 +- config/rbac/kustomization.yaml | 17 + config/rbac/network_editor_role.yaml | 27 + config/rbac/network_viewer_role.yaml | 23 + config/rbac/networkbinding_editor_role.yaml | 27 + config/rbac/networkbinding_viewer_role.yaml | 23 + config/rbac/networkcontext_editor_role.yaml | 27 + config/rbac/networkcontext_viewer_role.yaml | 23 + config/rbac/networkpolicy_editor_role.yaml | 27 + config/rbac/networkpolicy_viewer_role.yaml | 23 + config/rbac/role.yaml | 48 +- config/rbac/subnet_editor_role.yaml | 27 + config/rbac/subnet_viewer_role.yaml | 23 + config/rbac/subnetclaim_editor_role.yaml | 27 + config/rbac/subnetclaim_viewer_role.yaml | 23 + config/samples/kustomization.yaml | 9 + .../samples/networking_v1alpha_network.yaml | 9 + .../networking_v1alpha_networkbinding.yaml | 9 + .../networking_v1alpha_networkcontext.yaml | 9 + .../networking_v1alpha_networkpolicy.yaml | 9 + config/samples/networking_v1alpha_subnet.yaml | 9 + .../networking_v1alpha_subnetclaim.yaml | 9 + go.mod | 2 +- hack/boilerplate.go.txt | 1 + 41 files changed, 2775 insertions(+), 8 deletions(-) create mode 100644 api/v1alpha/groupversion_info.go create mode 100644 api/v1alpha/network_types.go create mode 100644 api/v1alpha/networkbinding_types.go create mode 100644 api/v1alpha/networkcontext_types.go create mode 100644 api/v1alpha/networkpolicy_types.go create mode 100644 api/v1alpha/subnet_types.go create mode 100644 api/v1alpha/subnetclaim_types.go create mode 100644 api/v1alpha/zz_generated.deepcopy.go create mode 100644 config/crd/bases/networking.datumapis.com_networkbindings.yaml create mode 100644 config/crd/bases/networking.datumapis.com_networkcontexts.yaml create mode 100644 config/crd/bases/networking.datumapis.com_networkpolicies.yaml create mode 100644 config/crd/bases/networking.datumapis.com_networks.yaml create mode 100644 config/crd/bases/networking.datumapis.com_subnetclaims.yaml create mode 100644 config/crd/bases/networking.datumapis.com_subnets.yaml create mode 100644 config/crd/kustomization.yaml create mode 100644 config/crd/kustomizeconfig.yaml create mode 100644 config/rbac/network_editor_role.yaml create mode 100644 config/rbac/network_viewer_role.yaml create mode 100644 config/rbac/networkbinding_editor_role.yaml create mode 100644 config/rbac/networkbinding_viewer_role.yaml create mode 100644 config/rbac/networkcontext_editor_role.yaml create mode 100644 config/rbac/networkcontext_viewer_role.yaml create mode 100644 config/rbac/networkpolicy_editor_role.yaml create mode 100644 config/rbac/networkpolicy_viewer_role.yaml create mode 100644 config/rbac/subnet_editor_role.yaml create mode 100644 config/rbac/subnet_viewer_role.yaml create mode 100644 config/rbac/subnetclaim_editor_role.yaml create mode 100644 config/rbac/subnetclaim_viewer_role.yaml create mode 100644 config/samples/kustomization.yaml create mode 100644 config/samples/networking_v1alpha_network.yaml create mode 100644 config/samples/networking_v1alpha_networkbinding.yaml create mode 100644 config/samples/networking_v1alpha_networkcontext.yaml create mode 100644 config/samples/networking_v1alpha_networkpolicy.yaml create mode 100644 config/samples/networking_v1alpha_subnet.yaml create mode 100644 config/samples/networking_v1alpha_subnetclaim.yaml create mode 100644 hack/boilerplate.go.txt diff --git a/PROJECT b/PROJECT index 1b4fa94..168107a 100644 --- a/PROJECT +++ b/PROJECT @@ -7,4 +7,59 @@ layout: - go.kubebuilder.io/v4 projectName: network-services-operator repo: go.datum.net/network-services-operator +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: datumapis.com + group: networking + kind: Network + path: go.datum.net/network-services-operator/api/v1alpha + version: v1alpha +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: datumapis.com + group: networking + kind: NetworkBinding + path: go.datum.net/network-services-operator/api/v1alpha + version: v1alpha +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: datumapis.com + group: networking + kind: NetworkContext + path: go.datum.net/network-services-operator/api/v1alpha + version: v1alpha +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: datumapis.com + group: networking + kind: NetworkPolicy + path: go.datum.net/network-services-operator/api/v1alpha + version: v1alpha +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: datumapis.com + group: networking + kind: Subnet + path: go.datum.net/network-services-operator/api/v1alpha + version: v1alpha +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: datumapis.com + group: networking + kind: SubnetClaim + path: go.datum.net/network-services-operator/api/v1alpha + version: v1alpha version: "3" diff --git a/api/v1alpha/groupversion_info.go b/api/v1alpha/groupversion_info.go new file mode 100644 index 0000000..44256aa --- /dev/null +++ b/api/v1alpha/groupversion_info.go @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Package v1alpha contains API Schema definitions for the networking v1alpha API group. +// +kubebuilder:object:generate=true +// +groupName=networking.datumapis.com +package v1alpha + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "networking.datumapis.com", Version: "v1alpha"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/v1alpha/network_types.go b/api/v1alpha/network_types.go new file mode 100644 index 0000000..fd482e4 --- /dev/null +++ b/api/v1alpha/network_types.go @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package v1alpha + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type IPFamily string + +const ( + IPv4Protocol IPFamily = "IPv4" + IPv6Protocol IPFamily = "IPv6" +) + +// NetworkSpec defines the desired state of a Network +type NetworkSpec struct { + + // IPAM settings for the network. + // + // +kubebuilder:validation:Required + IPAM NetworkIPAM `json:"ipam,omitempty"` + + // IP Families to permit on a network. Defaults to IPv4. + // + // +kubebuilder:validation:Optional + // +kubebuilder:default={IPv4} + // +kubebuilder:validation:Enum=IPv4;IPv6 + IPFamilies []IPFamily `json:"ipFamilies,omitempty"` + + // Network MTU. May be between 1300 and 8856. + // + // +kubebuilder:validation:Minimum=1300 + // +kubebuilder:validation:Maximum=8856 + // +kubebuilder:default=1460 + MTU int32 `json:"mtu"` +} + +type NetworkIPAMMode string + +const ( + // Automatically allocate subnets in the network + NetworkIPAMModeAuto NetworkIPAMMode = "Auto" + + // Leverage allocation policies or manually created subnets + NetworkIPAMModePolicy NetworkIPAMMode = "Policy" +) + +type NetworkIPAM struct { + // IPAM mode + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:Enum=Auto;Policy + Mode NetworkIPAMMode `json:"mode"` + + // IPv4 range to use in auto mode networks. Defaults to 10.128.0.0/9. + // + // +kubebuilder:validation:Optional + IPV4Range *string `json:"ipv4Range,omitempty"` + + // IPv6 range to use in auto mode networks. Defaults to a /48 allocated from `fd20::/20`. + // + // +kubebuilder:validation:Optional + IPV6Range *string `json:"ipv6Range,omitempty"` +} + +// NetworkStatus defines the observed state of Network +type NetworkStatus struct { + // Represents the observations of a network's current state. + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Network is the Schema for the networks API +type Network struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +kubebuilder:validation:Required + Spec NetworkSpec `json:"spec,omitempty"` + Status NetworkStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// NetworkList contains a list of Network +type NetworkList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Network `json:"items"` +} + +type NetworkRef struct { + // The network namespace. + // + // Defaults to the namespace for the type the reference is embedded in. + // + // +kubebuilder:validation:Optional + Namespace string `json:"namespace"` + + // The network name + // + // +kubebuilder:validation:Required + Name string `json:"name"` +} + +type LocalNetworkRef struct { + // The network name + // + // +kubebuilder:validation:Required + Name string `json:"name"` +} + +func init() { + SchemeBuilder.Register(&Network{}, &NetworkList{}) +} diff --git a/api/v1alpha/networkbinding_types.go b/api/v1alpha/networkbinding_types.go new file mode 100644 index 0000000..cc6a70b --- /dev/null +++ b/api/v1alpha/networkbinding_types.go @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package v1alpha + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NetworkBindingSpec defines the desired state of NetworkBinding +type NetworkBindingSpec struct { + // The network that the binding is for. + // + // +kubebuilder:validation:Required + Network NetworkRef `json:"network"` + + // The topology of where this binding exists + // + // This may contain arbitrary topology keys. Some keys may be well known, such + // as: + // - topology.datum.net/cityCode + // - topology.datum.net/cluster + // + // Each unique value of this field across bindings in the namespace will result + // in a NetworkAttachment to be created. + // + // +kubebuilder:validation:Required + Topology map[string]string `json:"topology"` +} + +// NetworkBindingObjectReference containts sufficient information for +// controllers to leverage unstructured or structured clients to interact with +// the bound resources. +type NetworkBindingObjectReference struct { + // API version of the referent. + // + // +kubebuilder:validation:Required + APIVersion string `json:"apiVersion"` + + // Kind of the referent. + // + // +kubebuilder:validation:Required + Kind string `json:"kind,omitempty"` + + // Namespace of the referent. + // + // +kubebuilder:validation:Required + Namespace string `json:"namespace,omitempty"` + + // Name of the referent. + // + // +kubebuilder:validation:Required + Name string `json:"name,omitempty"` +} + +// NetworkBindingStatus defines the observed state of NetworkBinding +type NetworkBindingStatus struct { + NetworkContextRef *NetworkContextRef `json:"networkContextRef,omitempty"` + + // Represents the observations of a network binding's current state. + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// NetworkBinding is the Schema for the networkbindings API +type NetworkBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +kubebuilder:validation:Required + Spec NetworkBindingSpec `json:"spec,omitempty"` + Status NetworkBindingStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// NetworkBindingList contains a list of NetworkBinding +type NetworkBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NetworkBinding `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NetworkBinding{}, &NetworkBindingList{}) +} diff --git a/api/v1alpha/networkcontext_types.go b/api/v1alpha/networkcontext_types.go new file mode 100644 index 0000000..322b9a2 --- /dev/null +++ b/api/v1alpha/networkcontext_types.go @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package v1alpha + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NetworkContextSpec defines the desired state of NetworkContext +type NetworkContextSpec struct { + // The attached network + // + // +kubebuilder:validation:Required + Network LocalNetworkRef `json:"network"` + + // The topology of where this context exists + // + // This may contain arbitrary topology keys. Some keys may be well known, such + // as: + // - topology.datum.net/cityCode + // - topology.datum.net/cluster + // + // The combined keys and values MUST be unique for contexts in the same + // network. + // + // +kubebuilder:validation:Required + Topology map[string]string `json:"topology"` +} + +// NetworkContextStatus defines the observed state of NetworkContext +type NetworkContextStatus struct { + // Represents the observations of a network context's current state. + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// NetworkContext is the Schema for the networkcontexts API +type NetworkContext struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NetworkContextSpec `json:"spec,omitempty"` + Status NetworkContextStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// NetworkContextList contains a list of NetworkContext +type NetworkContextList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NetworkContext `json:"items"` +} + +type NetworkContextRef struct { + // The network context namespace + // + // +kubebuilder:validation:Required + Namespace string `json:"namespace"` + + // The network context name + // + // +kubebuilder:validation:Required + Name string `json:"name"` +} + +type LocalNetworkContextRef struct { + // The network context name + // + // +kubebuilder:validation:Required + Name string `json:"name"` +} + +func init() { + SchemeBuilder.Register(&NetworkContext{}, &NetworkContextList{}) +} diff --git a/api/v1alpha/networkpolicy_types.go b/api/v1alpha/networkpolicy_types.go new file mode 100644 index 0000000..95bff43 --- /dev/null +++ b/api/v1alpha/networkpolicy_types.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package v1alpha + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +// NetworkPolicySpec defines the desired state of NetworkPolicy +type NetworkPolicySpec struct { + // TODO(jreese) complete this - currently the ingress rule is used directly + // in an instance's interface policy. +} + +// See k8s network policy types for inspiration here +type NetworkPolicyIngressRule struct { + // ports is a list of ports which should be made accessible on the instances selected for + // this rule. Each item in this list is combined using a logical OR. If this field is + // empty or missing, this rule matches all ports (traffic not restricted by port). + // If this field is present and contains at least one item, then this rule allows + // traffic only if the traffic matches at least one port in the list. + // + // +kubebuilder:validation:Optional + // +listType=atomic + Ports []NetworkPolicyPort `json:"ports,omitempty"` + + // from is a list of sources which should be able to access the instances selected for this rule. + // Items in this list are combined using a logical OR operation. If this field is + // empty or missing, this rule matches all sources (traffic not restricted by + // source). If this field is present and contains at least one item, this rule + // allows traffic only if the traffic matches at least one item in the from list. + // + // +kubebuilder:validation:Optional + // +listType=atomic + From []NetworkPolicyPeer `json:"from,omitempty"` +} + +// NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of +// fields are allowed +type NetworkPolicyPeer struct { + // ipBlock defines policy on a particular IPBlock. If this field is set then + // neither of the other fields can be. + // + // +kubebuilder:validation:Optional + IPBlock *IPBlock `json:"ipBlock,omitempty"` +} + +// NetworkPolicyPort describes a port to allow traffic on +type NetworkPolicyPort struct { + // protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match. + // If not specified, this field defaults to TCP. + // + // +kubebuilder:validation:Optional + Protocol *corev1.Protocol `json:"protocol,omitempty"` + + // port represents the port on the given protocol. This can either be a numerical or named + // port on an instance. If this field is not provided, this matches all port names and + // numbers. + // If present, only traffic on the specified protocol AND port will be matched. + // + // +kubebuilder:validation:Optional + Port *intstr.IntOrString `json:"port,omitempty"` + + // endPort indicates that the range of ports from port to endPort if set, inclusive, + // should be allowed by the policy. This field cannot be defined if the port field + // is not defined or if the port field is defined as a named (string) port. + // The endPort must be equal or greater than port. + // + // +kubebuilder:validation:Optional + EndPort *int32 `json:"endPort,omitempty"` +} + +// IPBlock describes a particular CIDR (Ex. "192.168.1.0/24","2001:db8::/64") +// that is allowed to the targets matched by a network policy. The except entry +// describes CIDRs that should not be included within this rule. +type IPBlock struct { + // cidr is a string representing the IPBlock + // Valid examples are "192.168.1.0/24" or "2001:db8::/64" + // + // +kubebuilder:validation:Required + CIDR string `json:"cidr"` + + // except is a slice of CIDRs that should not be included within an IPBlock + // Valid examples are "192.168.1.0/24" or "2001:db8::/64" + // Except values will be rejected if they are outside the cidr range + // + // +listType=atomic + // +kubebuilder:validation:Optional + Except []string `json:"except,omitempty"` +} + +// NetworkPolicyStatus defines the observed state of NetworkPolicy +type NetworkPolicyStatus struct { +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// NetworkPolicy is the Schema for the networkpolicies API +type NetworkPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NetworkPolicySpec `json:"spec,omitempty"` + Status NetworkPolicyStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// NetworkPolicyList contains a list of NetworkPolicy +type NetworkPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NetworkPolicy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NetworkPolicy{}, &NetworkPolicyList{}) +} diff --git a/api/v1alpha/subnet_types.go b/api/v1alpha/subnet_types.go new file mode 100644 index 0000000..9874056 --- /dev/null +++ b/api/v1alpha/subnet_types.go @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package v1alpha + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// SubnetSpec defines the desired state of Subnet +type SubnetSpec struct { + // The class of subnet + // + // +kubebuilder:validation:Required + SubnetClass string `json:"subnetClass"` + + // A subnet's network context + // + // +kubebuilder:validation:Required + NetworkContext LocalNetworkContextRef `json:"networkContext"` + + // The topology which a subnet is associated with + // + // +kubebuilder:validation:Required + Topology map[string]string `json:"topology"` + + // The IP family of a subnet + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:Enum=IPv4;IPv6 + IPFamily IPFamily `json:"ipFamily"` + + // The start address of a subnet + // + // +kubebuilder:validation:Required + StartAddress string `json:"startAddress"` + + // The prefix length of a subnet + // + // +kubebuilder:validation:Required + PrefixLength int32 `json:"prefixLength"` +} + +// SubnetStatus defines the observed state of a Subnet +type SubnetStatus struct { + // The start address of a subnet + StartAddress *string `json:"startAddress,omitempty"` + + // The prefix length of a subnet + PrefixLength *int32 `json:"prefixLength,omitempty"` + + // Represents the observations of a subnet's current state. + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Subnet is the Schema for the subnets API +type Subnet struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SubnetSpec `json:"spec,omitempty"` + Status SubnetStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// SubnetList contains a list of Subnet +type SubnetList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Subnet `json:"items"` +} + +type LocalSubnetReference struct { + Name string `json:"name"` +} + +func init() { + SchemeBuilder.Register(&Subnet{}, &SubnetList{}) +} diff --git a/api/v1alpha/subnetclaim_types.go b/api/v1alpha/subnetclaim_types.go new file mode 100644 index 0000000..2bd0e61 --- /dev/null +++ b/api/v1alpha/subnetclaim_types.go @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package v1alpha + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// SubnetClaimSpec defines the desired state of SubnetClaim +type SubnetClaimSpec struct { + // The class of subnet required + // + // +kubebuilder:validation:Required + SubnetClass string `json:"subnetClass"` + + // The network context to claim a subnet in + // + // +kubebuilder:validation:Required + NetworkContext LocalNetworkContextRef `json:"networkContext"` + + // The topology which the subnet is associated with + // + // +kubebuilder:validation:Required + Topology map[string]string `json:"topology"` + + // The IP family of a subnet claim + // + // +kubebuilder:validation:Required + // +kubebuilder:validation:Enum=IPv4;IPv6 + IPFamily IPFamily `json:"ipFamily"` + + // The start address of a subnet claim + // + // +kubebuilder:validation:Optional + StartAddress *string `json:"startAddress,omitempty"` + + // The prefix length of a subnet claim + // + // +kubebuilder:validation:Optional + PrefixLength *int32 `json:"prefixLength,omitempty"` +} + +// SubnetClaimStatus defines the observed state of SubnetClaim +type SubnetClaimStatus struct { + // The subnet which has been claimed from + SubnetRef *LocalSubnetReference `json:"subnetRef,omitempty"` + + // The start address of a subnet claim + StartAddress *string `json:"startAddress,omitempty"` + + // The prefix length of a subnet claim + PrefixLength *int32 `json:"prefixLength,omitempty"` + + // Represents the observations of a subnet claim's current state. + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// SubnetClaim is the Schema for the subnetclaims API +type SubnetClaim struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SubnetClaimSpec `json:"spec,omitempty"` + Status SubnetClaimStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// SubnetClaimList contains a list of SubnetClaim +type SubnetClaimList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []SubnetClaim `json:"items"` +} + +func init() { + SchemeBuilder.Register(&SubnetClaim{}, &SubnetClaimList{}) +} diff --git a/api/v1alpha/zz_generated.deepcopy.go b/api/v1alpha/zz_generated.deepcopy.go new file mode 100644 index 0000000..5081178 --- /dev/null +++ b/api/v1alpha/zz_generated.deepcopy.go @@ -0,0 +1,873 @@ +//go:build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IPBlock) DeepCopyInto(out *IPBlock) { + *out = *in + if in.Except != nil { + in, out := &in.Except, &out.Except + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPBlock. +func (in *IPBlock) DeepCopy() *IPBlock { + if in == nil { + return nil + } + out := new(IPBlock) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalNetworkContextRef) DeepCopyInto(out *LocalNetworkContextRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalNetworkContextRef. +func (in *LocalNetworkContextRef) DeepCopy() *LocalNetworkContextRef { + if in == nil { + return nil + } + out := new(LocalNetworkContextRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalNetworkRef) DeepCopyInto(out *LocalNetworkRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalNetworkRef. +func (in *LocalNetworkRef) DeepCopy() *LocalNetworkRef { + if in == nil { + return nil + } + out := new(LocalNetworkRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalSubnetReference) DeepCopyInto(out *LocalSubnetReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalSubnetReference. +func (in *LocalSubnetReference) DeepCopy() *LocalSubnetReference { + if in == nil { + return nil + } + out := new(LocalSubnetReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Network) DeepCopyInto(out *Network) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Network. +func (in *Network) DeepCopy() *Network { + if in == nil { + return nil + } + out := new(Network) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Network) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkBinding) DeepCopyInto(out *NetworkBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkBinding. +func (in *NetworkBinding) DeepCopy() *NetworkBinding { + if in == nil { + return nil + } + out := new(NetworkBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkBinding) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkBindingList) DeepCopyInto(out *NetworkBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NetworkBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkBindingList. +func (in *NetworkBindingList) DeepCopy() *NetworkBindingList { + if in == nil { + return nil + } + out := new(NetworkBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkBindingList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkBindingObjectReference) DeepCopyInto(out *NetworkBindingObjectReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkBindingObjectReference. +func (in *NetworkBindingObjectReference) DeepCopy() *NetworkBindingObjectReference { + if in == nil { + return nil + } + out := new(NetworkBindingObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkBindingSpec) DeepCopyInto(out *NetworkBindingSpec) { + *out = *in + out.Network = in.Network + if in.Topology != nil { + in, out := &in.Topology, &out.Topology + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkBindingSpec. +func (in *NetworkBindingSpec) DeepCopy() *NetworkBindingSpec { + if in == nil { + return nil + } + out := new(NetworkBindingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkBindingStatus) DeepCopyInto(out *NetworkBindingStatus) { + *out = *in + if in.NetworkContextRef != nil { + in, out := &in.NetworkContextRef, &out.NetworkContextRef + *out = new(NetworkContextRef) + **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]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkBindingStatus. +func (in *NetworkBindingStatus) DeepCopy() *NetworkBindingStatus { + if in == nil { + return nil + } + out := new(NetworkBindingStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkContext) DeepCopyInto(out *NetworkContext) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkContext. +func (in *NetworkContext) DeepCopy() *NetworkContext { + if in == nil { + return nil + } + out := new(NetworkContext) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkContext) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkContextList) DeepCopyInto(out *NetworkContextList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NetworkContext, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkContextList. +func (in *NetworkContextList) DeepCopy() *NetworkContextList { + if in == nil { + return nil + } + out := new(NetworkContextList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkContextList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkContextRef) DeepCopyInto(out *NetworkContextRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkContextRef. +func (in *NetworkContextRef) DeepCopy() *NetworkContextRef { + if in == nil { + return nil + } + out := new(NetworkContextRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkContextSpec) DeepCopyInto(out *NetworkContextSpec) { + *out = *in + out.Network = in.Network + if in.Topology != nil { + in, out := &in.Topology, &out.Topology + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkContextSpec. +func (in *NetworkContextSpec) DeepCopy() *NetworkContextSpec { + if in == nil { + return nil + } + out := new(NetworkContextSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkContextStatus) DeepCopyInto(out *NetworkContextStatus) { + *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]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkContextStatus. +func (in *NetworkContextStatus) DeepCopy() *NetworkContextStatus { + if in == nil { + return nil + } + out := new(NetworkContextStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkIPAM) DeepCopyInto(out *NetworkIPAM) { + *out = *in + if in.IPV4Range != nil { + in, out := &in.IPV4Range, &out.IPV4Range + *out = new(string) + **out = **in + } + if in.IPV6Range != nil { + in, out := &in.IPV6Range, &out.IPV6Range + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkIPAM. +func (in *NetworkIPAM) DeepCopy() *NetworkIPAM { + if in == nil { + return nil + } + out := new(NetworkIPAM) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkList) DeepCopyInto(out *NetworkList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Network, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkList. +func (in *NetworkList) DeepCopy() *NetworkList { + if in == nil { + return nil + } + out := new(NetworkList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkPolicy) DeepCopyInto(out *NetworkPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicy. +func (in *NetworkPolicy) DeepCopy() *NetworkPolicy { + if in == nil { + return nil + } + out := new(NetworkPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkPolicy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkPolicyIngressRule) DeepCopyInto(out *NetworkPolicyIngressRule) { + *out = *in + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]NetworkPolicyPort, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.From != nil { + in, out := &in.From, &out.From + *out = make([]NetworkPolicyPeer, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyIngressRule. +func (in *NetworkPolicyIngressRule) DeepCopy() *NetworkPolicyIngressRule { + if in == nil { + return nil + } + out := new(NetworkPolicyIngressRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkPolicyList) DeepCopyInto(out *NetworkPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NetworkPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyList. +func (in *NetworkPolicyList) DeepCopy() *NetworkPolicyList { + if in == nil { + return nil + } + out := new(NetworkPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkPolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkPolicyPeer) DeepCopyInto(out *NetworkPolicyPeer) { + *out = *in + if in.IPBlock != nil { + in, out := &in.IPBlock, &out.IPBlock + *out = new(IPBlock) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyPeer. +func (in *NetworkPolicyPeer) DeepCopy() *NetworkPolicyPeer { + if in == nil { + return nil + } + out := new(NetworkPolicyPeer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkPolicyPort) DeepCopyInto(out *NetworkPolicyPort) { + *out = *in + if in.Protocol != nil { + in, out := &in.Protocol, &out.Protocol + *out = new(corev1.Protocol) + **out = **in + } + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(intstr.IntOrString) + **out = **in + } + if in.EndPort != nil { + in, out := &in.EndPort, &out.EndPort + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyPort. +func (in *NetworkPolicyPort) DeepCopy() *NetworkPolicyPort { + if in == nil { + return nil + } + out := new(NetworkPolicyPort) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkPolicySpec) DeepCopyInto(out *NetworkPolicySpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicySpec. +func (in *NetworkPolicySpec) DeepCopy() *NetworkPolicySpec { + if in == nil { + return nil + } + out := new(NetworkPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkPolicyStatus) DeepCopyInto(out *NetworkPolicyStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyStatus. +func (in *NetworkPolicyStatus) DeepCopy() *NetworkPolicyStatus { + if in == nil { + return nil + } + out := new(NetworkPolicyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkRef) DeepCopyInto(out *NetworkRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRef. +func (in *NetworkRef) DeepCopy() *NetworkRef { + if in == nil { + return nil + } + out := new(NetworkRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { + *out = *in + in.IPAM.DeepCopyInto(&out.IPAM) + if in.IPFamilies != nil { + in, out := &in.IPFamilies, &out.IPFamilies + *out = make([]IPFamily, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkSpec. +func (in *NetworkSpec) DeepCopy() *NetworkSpec { + if in == nil { + return nil + } + out := new(NetworkSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkStatus) DeepCopyInto(out *NetworkStatus) { + *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]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkStatus. +func (in *NetworkStatus) DeepCopy() *NetworkStatus { + if in == nil { + return nil + } + out := new(NetworkStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Subnet) DeepCopyInto(out *Subnet) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subnet. +func (in *Subnet) DeepCopy() *Subnet { + if in == nil { + return nil + } + out := new(Subnet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Subnet) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetClaim) DeepCopyInto(out *SubnetClaim) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetClaim. +func (in *SubnetClaim) DeepCopy() *SubnetClaim { + if in == nil { + return nil + } + out := new(SubnetClaim) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SubnetClaim) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetClaimList) DeepCopyInto(out *SubnetClaimList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]SubnetClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetClaimList. +func (in *SubnetClaimList) DeepCopy() *SubnetClaimList { + if in == nil { + return nil + } + out := new(SubnetClaimList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SubnetClaimList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetClaimSpec) DeepCopyInto(out *SubnetClaimSpec) { + *out = *in + out.NetworkContext = in.NetworkContext + if in.Topology != nil { + in, out := &in.Topology, &out.Topology + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.StartAddress != nil { + in, out := &in.StartAddress, &out.StartAddress + *out = new(string) + **out = **in + } + if in.PrefixLength != nil { + in, out := &in.PrefixLength, &out.PrefixLength + *out = new(int32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetClaimSpec. +func (in *SubnetClaimSpec) DeepCopy() *SubnetClaimSpec { + if in == nil { + return nil + } + out := new(SubnetClaimSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetClaimStatus) DeepCopyInto(out *SubnetClaimStatus) { + *out = *in + if in.SubnetRef != nil { + in, out := &in.SubnetRef, &out.SubnetRef + *out = new(LocalSubnetReference) + **out = **in + } + if in.StartAddress != nil { + in, out := &in.StartAddress, &out.StartAddress + *out = new(string) + **out = **in + } + if in.PrefixLength != nil { + in, out := &in.PrefixLength, &out.PrefixLength + *out = new(int32) + **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]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetClaimStatus. +func (in *SubnetClaimStatus) DeepCopy() *SubnetClaimStatus { + if in == nil { + return nil + } + out := new(SubnetClaimStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetList) DeepCopyInto(out *SubnetList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Subnet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetList. +func (in *SubnetList) DeepCopy() *SubnetList { + if in == nil { + return nil + } + out := new(SubnetList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SubnetList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetSpec) DeepCopyInto(out *SubnetSpec) { + *out = *in + out.NetworkContext = in.NetworkContext + if in.Topology != nil { + in, out := &in.Topology, &out.Topology + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetSpec. +func (in *SubnetSpec) DeepCopy() *SubnetSpec { + if in == nil { + return nil + } + out := new(SubnetSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetStatus) DeepCopyInto(out *SubnetStatus) { + *out = *in + if in.StartAddress != nil { + in, out := &in.StartAddress, &out.StartAddress + *out = new(string) + **out = **in + } + if in.PrefixLength != nil { + in, out := &in.PrefixLength, &out.PrefixLength + *out = new(int32) + **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]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetStatus. +func (in *SubnetStatus) DeepCopy() *SubnetStatus { + if in == nil { + return nil + } + out := new(SubnetStatus) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/networking.datumapis.com_networkbindings.yaml b/config/crd/bases/networking.datumapis.com_networkbindings.yaml new file mode 100644 index 0000000..ace095c --- /dev/null +++ b/config/crd/bases/networking.datumapis.com_networkbindings.yaml @@ -0,0 +1,150 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: networkbindings.networking.datumapis.com +spec: + group: networking.datumapis.com + names: + kind: NetworkBinding + listKind: NetworkBindingList + plural: networkbindings + singular: networkbinding + scope: Namespaced + versions: + - name: v1alpha + schema: + openAPIV3Schema: + description: NetworkBinding is the Schema for the networkbindings API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NetworkBindingSpec defines the desired state of NetworkBinding + properties: + network: + description: The network that the binding is for. + properties: + name: + description: The network name + type: string + namespace: + description: |- + The network namespace. + + Defaults to the namespace for the type the reference is embedded in. + type: string + required: + - name + type: object + topology: + additionalProperties: + type: string + description: "The topology of where this binding exists\n\nThis may + contain arbitrary topology keys. Some keys may be well known, such\nas:\n\t- + topology.datum.net/cityCode\n\t- topology.datum.net/cluster\n\nEach + unique value of this field across bindings in the namespace will + result\nin a NetworkAttachment to be created." + type: object + required: + - network + - topology + type: object + status: + description: NetworkBindingStatus defines the observed state of NetworkBinding + properties: + conditions: + description: Represents the observations of a network binding's current + state. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + networkContextRef: + properties: + name: + description: The network context name + type: string + namespace: + description: The network context namespace + type: string + required: + - name + - namespace + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/networking.datumapis.com_networkcontexts.yaml b/config/crd/bases/networking.datumapis.com_networkcontexts.yaml new file mode 100644 index 0000000..540487a --- /dev/null +++ b/config/crd/bases/networking.datumapis.com_networkcontexts.yaml @@ -0,0 +1,129 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: networkcontexts.networking.datumapis.com +spec: + group: networking.datumapis.com + names: + kind: NetworkContext + listKind: NetworkContextList + plural: networkcontexts + singular: networkcontext + scope: Namespaced + versions: + - name: v1alpha + schema: + openAPIV3Schema: + description: NetworkContext is the Schema for the networkcontexts API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NetworkContextSpec defines the desired state of NetworkContext + properties: + network: + description: The attached network + properties: + name: + description: The network name + type: string + required: + - name + type: object + topology: + additionalProperties: + type: string + description: "The topology of where this context exists\n\nThis may + contain arbitrary topology keys. Some keys may be well known, such\nas:\n\t- + topology.datum.net/cityCode\n\t- topology.datum.net/cluster\n\nThe + combined keys and values MUST be unique for contexts in the same\nnetwork." + type: object + required: + - network + - topology + type: object + status: + description: NetworkContextStatus defines the observed state of NetworkContext + properties: + conditions: + description: Represents the observations of a network context's current + state. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/networking.datumapis.com_networkpolicies.yaml b/config/crd/bases/networking.datumapis.com_networkpolicies.yaml new file mode 100644 index 0000000..a5d8e01 --- /dev/null +++ b/config/crd/bases/networking.datumapis.com_networkpolicies.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: networkpolicies.networking.datumapis.com +spec: + group: networking.datumapis.com + names: + kind: NetworkPolicy + listKind: NetworkPolicyList + plural: networkpolicies + singular: networkpolicy + scope: Namespaced + versions: + - name: v1alpha + schema: + openAPIV3Schema: + description: NetworkPolicy is the Schema for the networkpolicies API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NetworkPolicySpec defines the desired state of NetworkPolicy + type: object + status: + description: NetworkPolicyStatus defines the observed state of NetworkPolicy + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/networking.datumapis.com_networks.yaml b/config/crd/bases/networking.datumapis.com_networks.yaml new file mode 100644 index 0000000..ad598c4 --- /dev/null +++ b/config/crd/bases/networking.datumapis.com_networks.yaml @@ -0,0 +1,150 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: networks.networking.datumapis.com +spec: + group: networking.datumapis.com + names: + kind: Network + listKind: NetworkList + plural: networks + singular: network + scope: Namespaced + versions: + - name: v1alpha + schema: + openAPIV3Schema: + description: Network is the Schema for the networks API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NetworkSpec defines the desired state of a Network + properties: + ipFamilies: + default: + - IPv4 + description: IP Families to permit on a network. Defaults to IPv4. + enum: + - IPv4 + - IPv6 + items: + type: string + type: array + ipam: + description: IPAM settings for the network. + properties: + ipv4Range: + description: IPv4 range to use in auto mode networks. Defaults + to 10.128.0.0/9. + type: string + ipv6Range: + description: IPv6 range to use in auto mode networks. Defaults + to a /48 allocated from `fd20::/20`. + type: string + mode: + description: IPAM mode + enum: + - Auto + - Policy + type: string + required: + - mode + type: object + mtu: + default: 1460 + description: Network MTU. May be between 1300 and 8856. + format: int32 + maximum: 8856 + minimum: 1300 + type: integer + required: + - ipam + - mtu + type: object + status: + description: NetworkStatus defines the observed state of Network + properties: + conditions: + description: Represents the observations of a network's current state. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/networking.datumapis.com_subnetclaims.yaml b/config/crd/bases/networking.datumapis.com_subnetclaims.yaml new file mode 100644 index 0000000..9a75284 --- /dev/null +++ b/config/crd/bases/networking.datumapis.com_subnetclaims.yaml @@ -0,0 +1,159 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: subnetclaims.networking.datumapis.com +spec: + group: networking.datumapis.com + names: + kind: SubnetClaim + listKind: SubnetClaimList + plural: subnetclaims + singular: subnetclaim + scope: Namespaced + versions: + - name: v1alpha + schema: + openAPIV3Schema: + description: SubnetClaim is the Schema for the subnetclaims API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: SubnetClaimSpec defines the desired state of SubnetClaim + properties: + ipFamily: + description: The IP family of a subnet claim + enum: + - IPv4 + - IPv6 + type: string + networkContext: + description: The network context to claim a subnet in + properties: + name: + description: The network context name + type: string + required: + - name + type: object + prefixLength: + description: The prefix length of a subnet claim + format: int32 + type: integer + startAddress: + description: The start address of a subnet claim + type: string + subnetClass: + description: The class of subnet required + type: string + topology: + additionalProperties: + type: string + description: The topology which the subnet is associated with + type: object + required: + - ipFamily + - networkContext + - subnetClass + - topology + type: object + status: + description: SubnetClaimStatus defines the observed state of SubnetClaim + properties: + conditions: + description: Represents the observations of a subnet claim's current + state. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + prefixLength: + description: The prefix length of a subnet claim + format: int32 + type: integer + startAddress: + description: The start address of a subnet claim + type: string + subnetRef: + description: The subnet which has been claimed from + properties: + name: + type: string + required: + - name + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/networking.datumapis.com_subnets.yaml b/config/crd/bases/networking.datumapis.com_subnets.yaml new file mode 100644 index 0000000..c7426ed --- /dev/null +++ b/config/crd/bases/networking.datumapis.com_subnets.yaml @@ -0,0 +1,152 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: subnets.networking.datumapis.com +spec: + group: networking.datumapis.com + names: + kind: Subnet + listKind: SubnetList + plural: subnets + singular: subnet + scope: Namespaced + versions: + - name: v1alpha + schema: + openAPIV3Schema: + description: Subnet is the Schema for the subnets API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: SubnetSpec defines the desired state of Subnet + properties: + ipFamily: + description: The IP family of a subnet + enum: + - IPv4 + - IPv6 + type: string + networkContext: + description: A subnet's network context + properties: + name: + description: The network context name + type: string + required: + - name + type: object + prefixLength: + description: The prefix length of a subnet + format: int32 + type: integer + startAddress: + description: The start address of a subnet + type: string + subnetClass: + description: The class of subnet + type: string + topology: + additionalProperties: + type: string + description: The topology which a subnet is associated with + type: object + required: + - ipFamily + - networkContext + - prefixLength + - startAddress + - subnetClass + - topology + type: object + status: + description: SubnetStatus defines the observed state of a Subnet + properties: + conditions: + description: Represents the observations of a subnet's current state. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + prefixLength: + description: The prefix length of a subnet + format: int32 + type: integer + startAddress: + description: The start address of a subnet + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml new file mode 100644 index 0000000..f3a9385 --- /dev/null +++ b/config/crd/kustomization.yaml @@ -0,0 +1,25 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/networking.datumapis.com_networks.yaml +- bases/networking.datumapis.com_networkbindings.yaml +- bases/networking.datumapis.com_networkcontexts.yaml +- bases/networking.datumapis.com_networkpolicies.yaml +- bases/networking.datumapis.com_subnets.yaml +- bases/networking.datumapis.com_subnetclaims.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patches: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# [WEBHOOK] To enable webhook, uncomment the following section +# the following config is for teaching kustomize how to do kustomization for CRDs. +#configurations: +#- kustomizeconfig.yaml diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml new file mode 100644 index 0000000..ec5c150 --- /dev/null +++ b/config/crd/kustomizeconfig.yaml @@ -0,0 +1,19 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 840d4f1..f1e1fc5 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -15,7 +15,7 @@ namePrefix: network-services-operator- # someName: someValue resources: -#- ../crd +- ../crd - ../rbac - ../manager # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 5619aa0..543983e 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -18,3 +18,20 @@ resources: - metrics_auth_role.yaml - metrics_auth_role_binding.yaml - metrics_reader_role.yaml +# For each CRD, "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the Project itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- subnetclaim_editor_role.yaml +- subnetclaim_viewer_role.yaml +- subnet_editor_role.yaml +- subnet_viewer_role.yaml +- networkpolicy_editor_role.yaml +- networkpolicy_viewer_role.yaml +- networkcontext_editor_role.yaml +- networkcontext_viewer_role.yaml +- networkbinding_editor_role.yaml +- networkbinding_viewer_role.yaml +- network_editor_role.yaml +- network_viewer_role.yaml + diff --git a/config/rbac/network_editor_role.yaml b/config/rbac/network_editor_role.yaml new file mode 100644 index 0000000..f266bab --- /dev/null +++ b/config/rbac/network_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit networks. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: network-editor-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - networks + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networks/status + verbs: + - get diff --git a/config/rbac/network_viewer_role.yaml b/config/rbac/network_viewer_role.yaml new file mode 100644 index 0000000..e03754d --- /dev/null +++ b/config/rbac/network_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view networks. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: network-viewer-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - networks + verbs: + - get + - list + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networks/status + verbs: + - get diff --git a/config/rbac/networkbinding_editor_role.yaml b/config/rbac/networkbinding_editor_role.yaml new file mode 100644 index 0000000..d3c7266 --- /dev/null +++ b/config/rbac/networkbinding_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit networkbindings. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkbinding-editor-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - networkbindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networkbindings/status + verbs: + - get diff --git a/config/rbac/networkbinding_viewer_role.yaml b/config/rbac/networkbinding_viewer_role.yaml new file mode 100644 index 0000000..2ee212a --- /dev/null +++ b/config/rbac/networkbinding_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view networkbindings. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkbinding-viewer-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - networkbindings + verbs: + - get + - list + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networkbindings/status + verbs: + - get diff --git a/config/rbac/networkcontext_editor_role.yaml b/config/rbac/networkcontext_editor_role.yaml new file mode 100644 index 0000000..f28cf5a --- /dev/null +++ b/config/rbac/networkcontext_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit networkcontexts. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkcontext-editor-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - networkcontexts + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networkcontexts/status + verbs: + - get diff --git a/config/rbac/networkcontext_viewer_role.yaml b/config/rbac/networkcontext_viewer_role.yaml new file mode 100644 index 0000000..c90ec08 --- /dev/null +++ b/config/rbac/networkcontext_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view networkcontexts. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkcontext-viewer-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - networkcontexts + verbs: + - get + - list + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networkcontexts/status + verbs: + - get diff --git a/config/rbac/networkpolicy_editor_role.yaml b/config/rbac/networkpolicy_editor_role.yaml new file mode 100644 index 0000000..0afdb87 --- /dev/null +++ b/config/rbac/networkpolicy_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit networkpolicies. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkpolicy-editor-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - networkpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networkpolicies/status + verbs: + - get diff --git a/config/rbac/networkpolicy_viewer_role.yaml b/config/rbac/networkpolicy_viewer_role.yaml new file mode 100644 index 0000000..f91b42a --- /dev/null +++ b/config/rbac/networkpolicy_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view networkpolicies. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkpolicy-viewer-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - networkpolicies + verbs: + - get + - list + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networkpolicies/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 226f266..759406e 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -1,11 +1,47 @@ +--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - labels: - app.kubernetes.io/name: network-services-operator - app.kubernetes.io/managed-by: kustomize name: manager-role rules: -- apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "watch"] +- apiGroups: + - networking.datumapis.com + resources: + - networkbindings + - networkcontexts + - networkpolicies + - networks + - subnetclaims + - subnets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.datumapis.com + resources: + - networkbindings/finalizers + - networkcontexts/finalizers + - networkpolicies/finalizers + - networks/finalizers + - subnetclaims/finalizers + - subnets/finalizers + verbs: + - update +- apiGroups: + - networking.datumapis.com + resources: + - networkbindings/status + - networkcontexts/status + - networkpolicies/status + - networks/status + - subnetclaims/status + - subnets/status + verbs: + - get + - patch + - update diff --git a/config/rbac/subnet_editor_role.yaml b/config/rbac/subnet_editor_role.yaml new file mode 100644 index 0000000..25e4328 --- /dev/null +++ b/config/rbac/subnet_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit subnets. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: subnet-editor-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - subnets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.datumapis.com + resources: + - subnets/status + verbs: + - get diff --git a/config/rbac/subnet_viewer_role.yaml b/config/rbac/subnet_viewer_role.yaml new file mode 100644 index 0000000..4ddc381 --- /dev/null +++ b/config/rbac/subnet_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view subnets. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: subnet-viewer-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - subnets + verbs: + - get + - list + - watch +- apiGroups: + - networking.datumapis.com + resources: + - subnets/status + verbs: + - get diff --git a/config/rbac/subnetclaim_editor_role.yaml b/config/rbac/subnetclaim_editor_role.yaml new file mode 100644 index 0000000..54aed0c --- /dev/null +++ b/config/rbac/subnetclaim_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit subnetclaims. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: subnetclaim-editor-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - subnetclaims + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.datumapis.com + resources: + - subnetclaims/status + verbs: + - get diff --git a/config/rbac/subnetclaim_viewer_role.yaml b/config/rbac/subnetclaim_viewer_role.yaml new file mode 100644 index 0000000..dc4ec14 --- /dev/null +++ b/config/rbac/subnetclaim_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view subnetclaims. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: subnetclaim-viewer-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - subnetclaims + verbs: + - get + - list + - watch +- apiGroups: + - networking.datumapis.com + resources: + - subnetclaims/status + verbs: + - get diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml new file mode 100644 index 0000000..fbc1216 --- /dev/null +++ b/config/samples/kustomization.yaml @@ -0,0 +1,9 @@ +## Append samples of your project ## +resources: +- networking_v1alpha_network.yaml +- networking_v1alpha_networkbinding.yaml +- networking_v1alpha_networkcontext.yaml +- networking_v1alpha_networkpolicy.yaml +- networking_v1alpha_subnet.yaml +- networking_v1alpha_subnetclaim.yaml +# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/networking_v1alpha_network.yaml b/config/samples/networking_v1alpha_network.yaml new file mode 100644 index 0000000..2799b39 --- /dev/null +++ b/config/samples/networking_v1alpha_network.yaml @@ -0,0 +1,9 @@ +apiVersion: networking.datumapis.com/v1alpha +kind: Network +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: network-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/networking_v1alpha_networkbinding.yaml b/config/samples/networking_v1alpha_networkbinding.yaml new file mode 100644 index 0000000..9a8aa87 --- /dev/null +++ b/config/samples/networking_v1alpha_networkbinding.yaml @@ -0,0 +1,9 @@ +apiVersion: networking.datumapis.com/v1alpha +kind: NetworkBinding +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkbinding-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/networking_v1alpha_networkcontext.yaml b/config/samples/networking_v1alpha_networkcontext.yaml new file mode 100644 index 0000000..b0008ab --- /dev/null +++ b/config/samples/networking_v1alpha_networkcontext.yaml @@ -0,0 +1,9 @@ +apiVersion: networking.datumapis.com/v1alpha +kind: NetworkContext +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkcontext-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/networking_v1alpha_networkpolicy.yaml b/config/samples/networking_v1alpha_networkpolicy.yaml new file mode 100644 index 0000000..855fb11 --- /dev/null +++ b/config/samples/networking_v1alpha_networkpolicy.yaml @@ -0,0 +1,9 @@ +apiVersion: networking.datumapis.com/v1alpha +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: networkpolicy-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/networking_v1alpha_subnet.yaml b/config/samples/networking_v1alpha_subnet.yaml new file mode 100644 index 0000000..5b3de8a --- /dev/null +++ b/config/samples/networking_v1alpha_subnet.yaml @@ -0,0 +1,9 @@ +apiVersion: networking.datumapis.com/v1alpha +kind: Subnet +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: subnet-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/networking_v1alpha_subnetclaim.yaml b/config/samples/networking_v1alpha_subnetclaim.yaml new file mode 100644 index 0000000..c419f75 --- /dev/null +++ b/config/samples/networking_v1alpha_subnetclaim.yaml @@ -0,0 +1,9 @@ +apiVersion: networking.datumapis.com/v1alpha +kind: SubnetClaim +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: subnetclaim-sample +spec: + # TODO(user): Add fields here diff --git a/go.mod b/go.mod index fbff839..c3a5c5f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23.0 require ( github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 + k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 sigs.k8s.io/controller-runtime v0.19.1 @@ -84,7 +85,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.31.0 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/apiserver v0.31.0 // indirect k8s.io/component-base v0.31.0 // indirect diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt new file mode 100644 index 0000000..ea8ae64 --- /dev/null +++ b/hack/boilerplate.go.txt @@ -0,0 +1 @@ +// SPDX-License-Identifier: AGPL-3.0-only From 8b90623b100f2e567020e857ccd0e4bb822dcdab Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Wed, 13 Nov 2024 18:05:32 +0000 Subject: [PATCH 07/19] Correct spelling error resulting in linter violation. --- api/v1alpha/networkbinding_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1alpha/networkbinding_types.go b/api/v1alpha/networkbinding_types.go index cc6a70b..f74e897 100644 --- a/api/v1alpha/networkbinding_types.go +++ b/api/v1alpha/networkbinding_types.go @@ -27,7 +27,7 @@ type NetworkBindingSpec struct { Topology map[string]string `json:"topology"` } -// NetworkBindingObjectReference containts sufficient information for +// NetworkBindingObjectReference contains sufficient information for // controllers to leverage unstructured or structured clients to interact with // the bound resources. type NetworkBindingObjectReference struct { From d9413e6e4ceb925ba9c649e5c8677b9cb1aefdf6 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Wed, 13 Nov 2024 18:47:02 +0000 Subject: [PATCH 08/19] Add missing omitempty on NetworkRef.Namespace field. --- api/v1alpha/network_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1alpha/network_types.go b/api/v1alpha/network_types.go index fd482e4..255645f 100644 --- a/api/v1alpha/network_types.go +++ b/api/v1alpha/network_types.go @@ -98,7 +98,7 @@ type NetworkRef struct { // Defaults to the namespace for the type the reference is embedded in. // // +kubebuilder:validation:Optional - Namespace string `json:"namespace"` + Namespace string `json:"namespace,omitempty"` // The network name // From f471542f64ba748f8fb30a9fa1500d2b9562c824 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Wed, 13 Nov 2024 20:35:50 +0000 Subject: [PATCH 09/19] Corrected IPFamily validation, added a couple Ready condition types. --- api/v1alpha/network_types.go | 2 +- api/v1alpha/networkbinding_types.go | 10 ++++++++++ api/v1alpha/networkcontext_types.go | 13 +++++++++++-- api/v1alpha/subnet_types.go | 1 - api/v1alpha/subnetclaim_types.go | 1 - .../networking.datumapis.com_networkcontexts.yaml | 5 +++-- .../bases/networking.datumapis.com_networks.yaml | 6 +++--- 7 files changed, 28 insertions(+), 10 deletions(-) diff --git a/api/v1alpha/network_types.go b/api/v1alpha/network_types.go index 255645f..b4cda61 100644 --- a/api/v1alpha/network_types.go +++ b/api/v1alpha/network_types.go @@ -6,6 +6,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// +kubebuilder:validation:Enum=IPv4;IPv6 type IPFamily string const ( @@ -25,7 +26,6 @@ type NetworkSpec struct { // // +kubebuilder:validation:Optional // +kubebuilder:default={IPv4} - // +kubebuilder:validation:Enum=IPv4;IPv6 IPFamilies []IPFamily `json:"ipFamilies,omitempty"` // Network MTU. May be between 1300 and 8856. diff --git a/api/v1alpha/networkbinding_types.go b/api/v1alpha/networkbinding_types.go index f74e897..9793093 100644 --- a/api/v1alpha/networkbinding_types.go +++ b/api/v1alpha/networkbinding_types.go @@ -60,6 +60,16 @@ type NetworkBindingStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } +// NetworkBindingConditionType defines the condition of a network binding +type NetworkBindingConditionType string + +const ( + // NetworkBindingReady indicates that the network binding has been associated + // with a NetworkContext and the owning resource should expect functional + // network features. + NetworkBindingReady NetworkBindingConditionType = "Ready" +) + // +kubebuilder:object:root=true // +kubebuilder:subresource:status diff --git a/api/v1alpha/networkcontext_types.go b/api/v1alpha/networkcontext_types.go index 322b9a2..de1e67f 100644 --- a/api/v1alpha/networkcontext_types.go +++ b/api/v1alpha/networkcontext_types.go @@ -17,8 +17,9 @@ type NetworkContextSpec struct { // // This may contain arbitrary topology keys. Some keys may be well known, such // as: - // - topology.datum.net/cityCode - // - topology.datum.net/cluster + // - topology.datum.net/city-code + // - topology.datum.net/cluster-name + // - topology.datum.net/cluster-namespace // // The combined keys and values MUST be unique for contexts in the same // network. @@ -33,6 +34,14 @@ type NetworkContextStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } +// NetworkContextConditionType defines the condition of a network context +type NetworkContextConditionType string + +const ( + // NetworkContextReady indicates that the network context is ready for use. + NetworkContextReady NetworkContextConditionType = "Ready" +) + // +kubebuilder:object:root=true // +kubebuilder:subresource:status diff --git a/api/v1alpha/subnet_types.go b/api/v1alpha/subnet_types.go index 9874056..3c50466 100644 --- a/api/v1alpha/subnet_types.go +++ b/api/v1alpha/subnet_types.go @@ -26,7 +26,6 @@ type SubnetSpec struct { // The IP family of a subnet // // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum=IPv4;IPv6 IPFamily IPFamily `json:"ipFamily"` // The start address of a subnet diff --git a/api/v1alpha/subnetclaim_types.go b/api/v1alpha/subnetclaim_types.go index 2bd0e61..31534a3 100644 --- a/api/v1alpha/subnetclaim_types.go +++ b/api/v1alpha/subnetclaim_types.go @@ -26,7 +26,6 @@ type SubnetClaimSpec struct { // The IP family of a subnet claim // // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum=IPv4;IPv6 IPFamily IPFamily `json:"ipFamily"` // The start address of a subnet claim diff --git a/config/crd/bases/networking.datumapis.com_networkcontexts.yaml b/config/crd/bases/networking.datumapis.com_networkcontexts.yaml index 540487a..faf7705 100644 --- a/config/crd/bases/networking.datumapis.com_networkcontexts.yaml +++ b/config/crd/bases/networking.datumapis.com_networkcontexts.yaml @@ -53,8 +53,9 @@ spec: type: string description: "The topology of where this context exists\n\nThis may contain arbitrary topology keys. Some keys may be well known, such\nas:\n\t- - topology.datum.net/cityCode\n\t- topology.datum.net/cluster\n\nThe - combined keys and values MUST be unique for contexts in the same\nnetwork." + topology.datum.net/city-code\n\t- topology.datum.net/cluster-name\n\t- + topology.datum.net/cluster-namespace\n\nThe combined keys and values + MUST be unique for contexts in the same\nnetwork." type: object required: - network diff --git a/config/crd/bases/networking.datumapis.com_networks.yaml b/config/crd/bases/networking.datumapis.com_networks.yaml index ad598c4..48a7325 100644 --- a/config/crd/bases/networking.datumapis.com_networks.yaml +++ b/config/crd/bases/networking.datumapis.com_networks.yaml @@ -43,10 +43,10 @@ spec: default: - IPv4 description: IP Families to permit on a network. Defaults to IPv4. - enum: - - IPv4 - - IPv6 items: + enum: + - IPv4 + - IPv6 type: string type: array ipam: From d491163e2795bff2dde6829692808798a4455126 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 19 Nov 2024 02:02:01 +0000 Subject: [PATCH 10/19] Added missing omitempty markers, remove type aliases for condition types. --- api/v1alpha/network_types.go | 7 ++++--- api/v1alpha/networkbinding_types.go | 12 +++++------- api/v1alpha/networkcontext_types.go | 5 +---- .../networking.datumapis.com_networkbindings.yaml | 7 ++++--- .../crd/bases/networking.datumapis.com_networks.yaml | 1 - 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/api/v1alpha/network_types.go b/api/v1alpha/network_types.go index b4cda61..09c70ee 100644 --- a/api/v1alpha/network_types.go +++ b/api/v1alpha/network_types.go @@ -32,8 +32,9 @@ type NetworkSpec struct { // // +kubebuilder:validation:Minimum=1300 // +kubebuilder:validation:Maximum=8856 + // +kubebuilder:validation:Optional // +kubebuilder:default=1460 - MTU int32 `json:"mtu"` + MTU int32 `json:"mtu,omitempty"` } type NetworkIPAMMode string @@ -103,14 +104,14 @@ type NetworkRef struct { // The network name // // +kubebuilder:validation:Required - Name string `json:"name"` + Name string `json:"name,omitempty"` } type LocalNetworkRef struct { // The network name // // +kubebuilder:validation:Required - Name string `json:"name"` + Name string `json:"name,omitempty"` } func init() { diff --git a/api/v1alpha/networkbinding_types.go b/api/v1alpha/networkbinding_types.go index 9793093..55eb378 100644 --- a/api/v1alpha/networkbinding_types.go +++ b/api/v1alpha/networkbinding_types.go @@ -11,14 +11,15 @@ type NetworkBindingSpec struct { // The network that the binding is for. // // +kubebuilder:validation:Required - Network NetworkRef `json:"network"` + Network NetworkRef `json:"network,omitempty"` // The topology of where this binding exists // // This may contain arbitrary topology keys. Some keys may be well known, such // as: - // - topology.datum.net/cityCode - // - topology.datum.net/cluster + // - topology.datum.net/city-code + // - topology.datum.net/cluster-name + // - topology.datum.net/cluster-namespace // // Each unique value of this field across bindings in the namespace will result // in a NetworkAttachment to be created. @@ -60,14 +61,11 @@ type NetworkBindingStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } -// NetworkBindingConditionType defines the condition of a network binding -type NetworkBindingConditionType string - const ( // NetworkBindingReady indicates that the network binding has been associated // with a NetworkContext and the owning resource should expect functional // network features. - NetworkBindingReady NetworkBindingConditionType = "Ready" + NetworkBindingReady = "Ready" ) // +kubebuilder:object:root=true diff --git a/api/v1alpha/networkcontext_types.go b/api/v1alpha/networkcontext_types.go index de1e67f..b1ab5eb 100644 --- a/api/v1alpha/networkcontext_types.go +++ b/api/v1alpha/networkcontext_types.go @@ -34,12 +34,9 @@ type NetworkContextStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } -// NetworkContextConditionType defines the condition of a network context -type NetworkContextConditionType string - const ( // NetworkContextReady indicates that the network context is ready for use. - NetworkContextReady NetworkContextConditionType = "Ready" + NetworkContextReady = "Ready" ) // +kubebuilder:object:root=true diff --git a/config/crd/bases/networking.datumapis.com_networkbindings.yaml b/config/crd/bases/networking.datumapis.com_networkbindings.yaml index ace095c..dfaccc8 100644 --- a/config/crd/bases/networking.datumapis.com_networkbindings.yaml +++ b/config/crd/bases/networking.datumapis.com_networkbindings.yaml @@ -59,9 +59,10 @@ spec: type: string description: "The topology of where this binding exists\n\nThis may contain arbitrary topology keys. Some keys may be well known, such\nas:\n\t- - topology.datum.net/cityCode\n\t- topology.datum.net/cluster\n\nEach - unique value of this field across bindings in the namespace will - result\nin a NetworkAttachment to be created." + topology.datum.net/city-code\n\t- topology.datum.net/cluster-name\n\t- + topology.datum.net/cluster-namespace\n\nEach unique value of this + field across bindings in the namespace will result\nin a NetworkAttachment + to be created." type: object required: - network diff --git a/config/crd/bases/networking.datumapis.com_networks.yaml b/config/crd/bases/networking.datumapis.com_networks.yaml index 48a7325..39c917d 100644 --- a/config/crd/bases/networking.datumapis.com_networks.yaml +++ b/config/crd/bases/networking.datumapis.com_networks.yaml @@ -78,7 +78,6 @@ spec: type: integer required: - ipam - - mtu type: object status: description: NetworkStatus defines the observed state of Network From 48d61bcc335f0fa60bff265c0b163c9b66539e2e Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 19 Nov 2024 02:05:50 +0000 Subject: [PATCH 11/19] Bootstrapped controller boilerplate, implemented very basic logic for network bindings, subnet claims, and subnets. Still much work to do around testing, and implementing controllers for other types when needed. The test suite is expected to pass from this point forward, though there are some Pending tests. --- cmd/main.go | 46 +++++ internal/.gitkeep | 0 internal/controller/network_controller.go | 49 +++++ .../controller/network_controller_test.go | 70 +++++++ .../controller/networkbinding_controller.go | 185 ++++++++++++++++++ .../networkbinding_controller_test.go | 159 +++++++++++++++ .../controller/networkcontext_controller.go | 49 +++++ .../networkcontext_controller_test.go | 70 +++++++ .../controller/networkpolicy_controller.go | 49 +++++ .../networkpolicy_controller_test.go | 70 +++++++ internal/controller/subnet_controller.go | 133 +++++++++++++ internal/controller/subnet_controller_test.go | 70 +++++++ internal/controller/subnetclaim_controller.go | 152 ++++++++++++++ .../controller/subnetclaim_controller_test.go | 70 +++++++ internal/controller/suite_test.go | 106 ++++++++++ 15 files changed, 1278 insertions(+) delete mode 100644 internal/.gitkeep create mode 100644 internal/controller/network_controller.go create mode 100644 internal/controller/network_controller_test.go create mode 100644 internal/controller/networkbinding_controller.go create mode 100644 internal/controller/networkbinding_controller_test.go create mode 100644 internal/controller/networkcontext_controller.go create mode 100644 internal/controller/networkcontext_controller_test.go create mode 100644 internal/controller/networkpolicy_controller.go create mode 100644 internal/controller/networkpolicy_controller_test.go create mode 100644 internal/controller/subnet_controller.go create mode 100644 internal/controller/subnet_controller_test.go create mode 100644 internal/controller/subnetclaim_controller.go create mode 100644 internal/controller/subnetclaim_controller_test.go create mode 100644 internal/controller/suite_test.go diff --git a/cmd/main.go b/cmd/main.go index adb4f84..9ce2816 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -18,6 +18,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/metrics/filters" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" + "go.datum.net/network-services-operator/internal/controller" // +kubebuilder:scaffold:imports ) @@ -29,6 +32,7 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(networkingv1alpha.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -122,6 +126,48 @@ func main() { os.Exit(1) } + if err = (&controller.NetworkReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Network") + os.Exit(1) + } + if err = (&controller.NetworkBindingReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "NetworkBinding") + os.Exit(1) + } + if err = (&controller.NetworkContextReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "NetworkContext") + os.Exit(1) + } + if err = (&controller.NetworkPolicyReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "NetworkPolicy") + os.Exit(1) + } + if err = (&controller.SubnetReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Subnet") + os.Exit(1) + } + if err = (&controller.SubnetClaimReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "SubnetClaim") + os.Exit(1) + } // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/internal/.gitkeep b/internal/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/internal/controller/network_controller.go b/internal/controller/network_controller.go new file mode 100644 index 0000000..cbc2168 --- /dev/null +++ b/internal/controller/network_controller.go @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +// NetworkReconciler reconciles a Network object +type NetworkReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networks,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networks/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networks/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Network object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile +func (r *NetworkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *NetworkReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&networkingv1alpha.Network{}). + Named("network"). + Complete(r) +} diff --git a/internal/controller/network_controller_test.go b/internal/controller/network_controller_test.go new file mode 100644 index 0000000..125f917 --- /dev/null +++ b/internal/controller/network_controller_test.go @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +var _ = Describe("Network Controller", Pending, func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + network := &networkingv1alpha.Network{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Network") + err := k8sClient.Get(ctx, typeNamespacedName, network) + if err != nil && errors.IsNotFound(err) { + resource := &networkingv1alpha.Network{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &networkingv1alpha.Network{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Network") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &NetworkReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/internal/controller/networkbinding_controller.go b/internal/controller/networkbinding_controller.go new file mode 100644 index 0000000..6488cec --- /dev/null +++ b/internal/controller/networkbinding_controller.go @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + "encoding/json" + "fmt" + "hash/fnv" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/rand" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +// NetworkBindingReconciler reconciles a NetworkBinding object +type NetworkBindingReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkbindings,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkbindings/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkbindings/finalizers,verbs=update + +func (r *NetworkBindingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, err error) { + logger := log.FromContext(ctx) + + // Each valid network binding should result in a NetworkAttachment being + // created for each unique `topology` that's found. + + var binding networkingv1alpha.NetworkBinding + if err := r.Client.Get(ctx, req.NamespacedName, &binding); err != nil { + if apierrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + if !binding.DeletionTimestamp.IsZero() { + return ctrl.Result{}, nil + } + + logger.Info("reconciling network binding") + defer logger.Info("reconcile complete") + + readyCondition := metav1.Condition{ + Type: networkingv1alpha.NetworkBindingReady, + Status: metav1.ConditionFalse, + Reason: "Unknown", + ObservedGeneration: binding.Generation, + Message: "Unknown state", + } + + defer func() { + if err != nil { + // Don't update the status if errors are encountered + return + } + statusChanged := apimeta.SetStatusCondition(&binding.Status.Conditions, readyCondition) + + if statusChanged { + err = r.Client.Status().Update(ctx, &binding) + } + }() + + networkNamespace := binding.Spec.Network.Namespace + + if len(networkNamespace) == 0 { + // Fall back to binding's namespace if NetworkRef does not specify one. + networkNamespace = binding.Namespace + } + + var network networkingv1alpha.Network + networkObjectKey := client.ObjectKey{ + Namespace: networkNamespace, + Name: binding.Spec.Network.Name, + } + if err := r.Client.Get(ctx, networkObjectKey, &network); err != nil { + readyCondition.Reason = "NetworkNotFound" + readyCondition.Message = "The network referenced in the binding was not found." + return ctrl.Result{}, fmt.Errorf("failed fetching network for binding: %w", err) + } + + networkContextName, err := networkContextNameForBinding(&binding) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to determine network context name: %w", err) + } + + var networkContext networkingv1alpha.NetworkContext + networkContextObjectKey := client.ObjectKey{ + Namespace: networkNamespace, + Name: networkContextName, + } + if err := r.Client.Get(ctx, networkContextObjectKey, &networkContext); client.IgnoreNotFound(err) != nil { + return ctrl.Result{}, fmt.Errorf("failed fetching network context: %w", err) + } + + if networkContext.CreationTimestamp.IsZero() { + networkContext = networkingv1alpha.NetworkContext{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: networkNamespace, + Name: networkContextName, + }, + Spec: networkingv1alpha.NetworkContextSpec{ + Network: networkingv1alpha.LocalNetworkRef{ + Name: binding.Spec.Network.Name, + }, + Topology: binding.Spec.Topology, + }, + } + + if err := controllerutil.SetControllerReference(&network, &networkContext, r.Scheme); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to set controller on network context: %w", err) + } + + if err := r.Client.Create(ctx, &networkContext); err != nil { + return ctrl.Result{}, fmt.Errorf("failed creating network context: %w", err) + } + } + + if !apimeta.IsStatusConditionTrue(networkContext.Status.Conditions, networkingv1alpha.NetworkContextReady) { + logger.Info("network context is not ready") + readyCondition.Reason = "NetworkContextNotReady" + readyCondition.Message = "Network context is not ready." + + // Choosing to requeue here instead of establishing a watch on contexts, as + // once the context is created an ready, future bindings will immediately + // become ready. + return ctrl.Result{Requeue: true}, nil + } + + binding.Status.NetworkContextRef = &networkingv1alpha.NetworkContextRef{ + Namespace: networkContext.Namespace, + Name: networkContext.Name, + } + + readyCondition.Status = metav1.ConditionTrue + readyCondition.Reason = "NetworkContextReady" + readyCondition.Message = "Network context is ready." + + if err := r.Client.Status().Update(ctx, &binding); err != nil { + return ctrl.Result{}, fmt.Errorf("failed updating binding status: %w", err) + } + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *NetworkBindingReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&networkingv1alpha.NetworkBinding{}, builder.WithPredicates( + predicate.NewPredicateFuncs(func(object client.Object) bool { + o := object.(*networkingv1alpha.NetworkBinding) + return o.Status.NetworkContextRef == nil + }), + )). + Complete(r) +} + +func networkContextNameForBinding(binding *networkingv1alpha.NetworkBinding) (string, error) { + if binding.CreationTimestamp.IsZero() { + return "", fmt.Errorf("binding has not been created") + } + topologyBytes, err := json.Marshal(binding.Spec.Topology) + if err != nil { + return "", fmt.Errorf("failed marshaling topology to json: %w", err) + } + + f := fnv.New32a() + f.Write(topologyBytes) + topologyHash := rand.SafeEncodeString(fmt.Sprint(f.Sum32())) + + return fmt.Sprintf("%s-%s", binding.Spec.Network.Name, topologyHash), nil +} diff --git a/internal/controller/networkbinding_controller_test.go b/internal/controller/networkbinding_controller_test.go new file mode 100644 index 0000000..72629a5 --- /dev/null +++ b/internal/controller/networkbinding_controller_test.go @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +var _ = Describe("NetworkBinding Controller", func() { + Context("When reconciling a new resource", Ordered, func() { + const networkName = "test-binding-network" + const bindingName = "test-binding" + + ctx := context.Background() + + networkNamespacedName := types.NamespacedName{ + Name: networkName, + Namespace: "default", + } + network := &networkingv1alpha.Network{} + + bindingNamespacedName := types.NamespacedName{ + Name: bindingName, + Namespace: "default", + } + binding := &networkingv1alpha.NetworkBinding{} + + BeforeEach(func() { + By("creating a Network") + err := k8sClient.Get(ctx, networkNamespacedName, network) + if err != nil && errors.IsNotFound(err) { + network = &networkingv1alpha.Network{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: networkName, + }, + Spec: networkingv1alpha.NetworkSpec{ + IPAM: networkingv1alpha.NetworkIPAM{ + Mode: networkingv1alpha.NetworkIPAMModeAuto, + }, + }, + } + Expect(k8sClient.Create(ctx, network)).To(Succeed()) + } + Expect(client.IgnoreNotFound(err)).To(BeNil()) + + By("creating a NetworkBinding") + err = k8sClient.Get(ctx, bindingNamespacedName, binding) + if err != nil && errors.IsNotFound(err) { + resource := &networkingv1alpha.NetworkBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: bindingName, + Namespace: "default", + }, + Spec: networkingv1alpha.NetworkBindingSpec{ + Network: networkingv1alpha.NetworkRef{ + Name: network.Name, + }, + Topology: map[string]string{ + "topo-key": "value", + }, + }, + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + Expect(client.IgnoreNotFound(err)).To(BeNil()) + + }) + + AfterEach(func() { + binding := &networkingv1alpha.NetworkBinding{} + Expect(k8sClient.Get(ctx, bindingNamespacedName, binding)).To(Succeed()) + + networkContextName, err := networkContextNameForBinding(binding) + Expect(err).To(BeNil()) + Expect(k8sClient.Delete(ctx, binding)).To(Succeed()) + + networkContext := &networkingv1alpha.NetworkContext{} + networkContextNamespacedName := types.NamespacedName{ + Name: networkContextName, + Namespace: "default", + } + Expect(k8sClient.Get(ctx, networkContextNamespacedName, networkContext)).To(Succeed()) + Expect(k8sClient.Delete(ctx, networkContext)) + + network := &networkingv1alpha.Network{} + Expect(k8sClient.Get(ctx, networkNamespacedName, network)).To(Succeed()) + Expect(k8sClient.Delete(ctx, network)).To(Succeed()) + }) + + It("should successfully create a NetworkContext", func() { + err := k8sClient.Get(ctx, bindingNamespacedName, binding) + Expect(err).To(BeNil()) + + bindingReady := apimeta.IsStatusConditionTrue(binding.Status.Conditions, networkingv1alpha.NetworkBindingReady) + Expect(bindingReady).To(BeFalse()) + + networkContextName, err := networkContextNameForBinding(binding) + Expect(err).To(BeNil()) + + var networkContext networkingv1alpha.NetworkContext + networkContextObjectKey := client.ObjectKey{ + Namespace: binding.Namespace, + Name: networkContextName, + } + + Eventually(ctx, func() error { + return k8sClient.Get(ctx, networkContextObjectKey, &networkContext) + }).Should(BeNil()) + }) + + It("should become Ready once the referenced NetworkContext is Ready", func() { + networkContextName, err := networkContextNameForBinding(binding) + Expect(err).To(BeNil()) + + var networkContext networkingv1alpha.NetworkContext + networkContextObjectKey := client.ObjectKey{ + Namespace: binding.Namespace, + Name: networkContextName, + } + + Eventually(ctx, func() error { + return k8sClient.Get(ctx, networkContextObjectKey, &networkContext) + }).Should(BeNil()) + + // We set the status manually here, as external controllers are responsible + // for updating Context readiness right now. + // + // TODO(jreese) - Consider having a `Programmed` condition that external + // controllers use, and have a NSO controller update the `Ready` condition? + + apimeta.SetStatusCondition(&networkContext.Status.Conditions, metav1.Condition{ + Type: networkingv1alpha.NetworkContextReady, + Status: metav1.ConditionTrue, + Reason: "Test", + Message: "test condition", + }) + + Expect(k8sClient.Status().Update(ctx, &networkContext)).To(Succeed()) + + Eventually(func() bool { + err := k8sClient.Get(ctx, bindingNamespacedName, binding) + Expect(err).To(BeNil()) + + return apimeta.IsStatusConditionTrue(binding.Status.Conditions, networkingv1alpha.NetworkBindingReady) + }).Should(BeTrue()) + }) + }) +}) diff --git a/internal/controller/networkcontext_controller.go b/internal/controller/networkcontext_controller.go new file mode 100644 index 0000000..344a7e6 --- /dev/null +++ b/internal/controller/networkcontext_controller.go @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +// NetworkContextReconciler reconciles a NetworkContext object +type NetworkContextReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkcontexts,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkcontexts/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkcontexts/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the NetworkContext object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile +func (r *NetworkContextReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *NetworkContextReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&networkingv1alpha.NetworkContext{}). + Named("networkcontext"). + Complete(r) +} diff --git a/internal/controller/networkcontext_controller_test.go b/internal/controller/networkcontext_controller_test.go new file mode 100644 index 0000000..2148d56 --- /dev/null +++ b/internal/controller/networkcontext_controller_test.go @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +var _ = Describe("NetworkContext Controller", Pending, func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + networkcontext := &networkingv1alpha.NetworkContext{} + + BeforeEach(func() { + By("creating the custom resource for the Kind NetworkContext") + err := k8sClient.Get(ctx, typeNamespacedName, networkcontext) + if err != nil && errors.IsNotFound(err) { + resource := &networkingv1alpha.NetworkContext{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &networkingv1alpha.NetworkContext{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance NetworkContext") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &NetworkContextReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/internal/controller/networkpolicy_controller.go b/internal/controller/networkpolicy_controller.go new file mode 100644 index 0000000..1ed0b72 --- /dev/null +++ b/internal/controller/networkpolicy_controller.go @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +// NetworkPolicyReconciler reconciles a NetworkPolicy object +type NetworkPolicyReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkpolicies,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkpolicies/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=networkpolicies/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the NetworkPolicy object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile +func (r *NetworkPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *NetworkPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&networkingv1alpha.NetworkPolicy{}). + Named("networkpolicy"). + Complete(r) +} diff --git a/internal/controller/networkpolicy_controller_test.go b/internal/controller/networkpolicy_controller_test.go new file mode 100644 index 0000000..b3449fd --- /dev/null +++ b/internal/controller/networkpolicy_controller_test.go @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +var _ = Describe("NetworkPolicy Controller", Pending, func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + networkpolicy := &networkingv1alpha.NetworkPolicy{} + + BeforeEach(func() { + By("creating the custom resource for the Kind NetworkPolicy") + err := k8sClient.Get(ctx, typeNamespacedName, networkpolicy) + if err != nil && errors.IsNotFound(err) { + resource := &networkingv1alpha.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &networkingv1alpha.NetworkPolicy{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance NetworkPolicy") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &NetworkPolicyReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/internal/controller/subnet_controller.go b/internal/controller/subnet_controller.go new file mode 100644 index 0000000..e9a4bb5 --- /dev/null +++ b/internal/controller/subnet_controller.go @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + "fmt" + + "google.golang.org/protobuf/proto" + apierrors "k8s.io/apimachinery/pkg/api/errors" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +// SubnetReconciler reconciles a Subnet object +type SubnetReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=subnets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=subnets/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=subnets/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Subnet object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile +func (r *SubnetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + var subnet networkingv1alpha.Subnet + if err := r.Client.Get(ctx, req.NamespacedName, &subnet); err != nil { + if apierrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + if !subnet.DeletionTimestamp.IsZero() { + return ctrl.Result{}, nil + } + + logger.Info("reconciling subnet") + defer logger.Info("reconcile complete") + + // TODO(jreese) finalizer work + + var networkContext networkingv1alpha.NetworkContext + networkContextObjectKey := client.ObjectKey{ + Namespace: subnet.Namespace, + Name: subnet.Spec.NetworkContext.Name, + } + if err := r.Client.Get(ctx, networkContextObjectKey, &networkContext); err != nil { + return ctrl.Result{}, fmt.Errorf("failed fetching network context: %w", err) + } + + // TODO(jreese) get topology key from well known package + cityCode, ok := networkContext.Spec.Topology["topology.datum.net/city-code"] + if !ok { + return ctrl.Result{}, fmt.Errorf("unable to find topology key: topology.datum.net/city-code") + } + + // TODO(jreese) move to proper higher level subnet allocation logic, this is + // for the rough POC! Pay attention to the subnet class, etc. + // + // GCP allocates a /20 per region. Distribution seems to be as new regions + // come online, a /20 is allocated, but there appears to be at least a /15 + // between each region's /20. For example: + // + // europe-west9 10.200.0.0/20 + // us-east5 10.202.0.0/20 + // europe-southwest1 10.204.0.0/20 + // us-south1 10.206.0.0/20 + // me-west1 10.208.0.0/20 + // + // There's a few scenarios where this isn't the case. + + var startAddress string + switch cityCode { + case "DFW": + startAddress = "10.128.0.0" + case "DLS": + startAddress = "10.130.0.0" + case "LHR": + startAddress = "10.132.0.0" + } + + subnet.Status.StartAddress = proto.String(startAddress) + subnet.Status.PrefixLength = proto.Int32(20) + + apimeta.SetStatusCondition(&subnet.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionTrue, + Reason: "PrefixAllocated", + ObservedGeneration: subnet.Generation, + Message: "Subnet has been allocated a prefix", + }) + + if err := r.Client.Status().Update(ctx, &subnet); err != nil { + return ctrl.Result{}, fmt.Errorf("failed updating subnet status") + } + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *SubnetReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&networkingv1alpha.Subnet{}, builder.WithPredicates( + predicate.NewPredicateFuncs(func(object client.Object) bool { + // Don't bother processing subnets that have been allocated and are not + // deleting + o := object.(*networkingv1alpha.Subnet) + return o.Status.StartAddress == nil || !o.DeletionTimestamp.IsZero() + }), + )). + Named("subnet"). + Complete(r) +} diff --git a/internal/controller/subnet_controller_test.go b/internal/controller/subnet_controller_test.go new file mode 100644 index 0000000..e404eff --- /dev/null +++ b/internal/controller/subnet_controller_test.go @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +var _ = Describe("Subnet Controller", Pending, func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + subnet := &networkingv1alpha.Subnet{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Subnet") + err := k8sClient.Get(ctx, typeNamespacedName, subnet) + if err != nil && errors.IsNotFound(err) { + resource := &networkingv1alpha.Subnet{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &networkingv1alpha.Subnet{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Subnet") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &SubnetReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/internal/controller/subnetclaim_controller.go b/internal/controller/subnetclaim_controller.go new file mode 100644 index 0000000..bad7f80 --- /dev/null +++ b/internal/controller/subnetclaim_controller.go @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +// SubnetClaimReconciler reconciles a SubnetClaim object +type SubnetClaimReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=subnetclaims,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=subnetclaims/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=networking.datumapis.com,resources=subnetclaims/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the SubnetClaim object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile +func (r *SubnetClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + var claim networkingv1alpha.SubnetClaim + if err := r.Client.Get(ctx, req.NamespacedName, &claim); err != nil { + if apierrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + if !claim.DeletionTimestamp.IsZero() { + return ctrl.Result{}, nil + } + + logger.Info("reconciling subnet claim") + defer logger.Info("reconcile complete") + + // TODO(jreese) move to a network context level subnet allocator, instead of + // the 1:1 SubnetClaim:Subnet that's here right now. + + var subnet networkingv1alpha.Subnet + if err := r.Client.Get(ctx, client.ObjectKeyFromObject(&claim), &subnet); client.IgnoreNotFound(err) != nil { + return ctrl.Result{}, fmt.Errorf("failed fetching subnet: %w", err) + } + + if subnet.CreationTimestamp.IsZero() { + var networkContext networkingv1alpha.NetworkContext + networkContextObjectKey := client.ObjectKey{ + Namespace: claim.Namespace, + Name: claim.Spec.NetworkContext.Name, + } + if err := r.Client.Get(ctx, networkContextObjectKey, &networkContext); err != nil { + return ctrl.Result{}, fmt.Errorf("failed fetching network context: %w", err) + } + + subnet = networkingv1alpha.Subnet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: claim.Namespace, + Name: claim.Name, + }, + Spec: networkingv1alpha.SubnetSpec{ + SubnetClass: claim.Spec.SubnetClass, + NetworkContext: claim.Spec.NetworkContext, + Topology: claim.Spec.Topology, + }, + } + + if err := controllerutil.SetControllerReference(&networkContext, &subnet, r.Scheme); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to set controller on subnet: %w", err) + } + + if err := r.Client.Create(ctx, &subnet); err != nil { + return ctrl.Result{}, fmt.Errorf("failed creating subnet: %w", err) + } + + apimeta.SetStatusCondition(&claim.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionFalse, + Reason: "SubnetNotReady", + ObservedGeneration: claim.Generation, + Message: "Subnet is not ready", + }) + + if err := r.Client.Status().Update(ctx, &claim); err != nil { + return ctrl.Result{}, fmt.Errorf("failed updating claim status") + } + + return ctrl.Result{}, nil + } + + if !apimeta.IsStatusConditionTrue(subnet.Status.Conditions, "Ready") { + logger.Info("subnet is not ready") + return ctrl.Result{}, nil + } + + claim.Status.SubnetRef = &networkingv1alpha.LocalSubnetReference{ + Name: subnet.Name, + } + + apimeta.SetStatusCondition(&claim.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionTrue, + Reason: "SubnetReady", + ObservedGeneration: claim.Generation, + Message: "Subnet ready", + }) + + if err := r.Client.Status().Update(ctx, &claim); err != nil { + return ctrl.Result{}, fmt.Errorf("failed updating claim status") + } + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *SubnetClaimReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&networkingv1alpha.SubnetClaim{}, builder.WithPredicates( + predicate.NewPredicateFuncs(func(object client.Object) bool { + // Don't bother processing deployments that have been scheduled + o := object.(*networkingv1alpha.SubnetClaim) + return o.Status.SubnetRef == nil + }), + )). + // TODO(jreese) change when we don't have claims 1:1 with subnets + Watches(&networkingv1alpha.Subnet{}, &handler.EnqueueRequestForObject{}). + Named("subnetclaim"). + Complete(r) +} diff --git a/internal/controller/subnetclaim_controller_test.go b/internal/controller/subnetclaim_controller_test.go new file mode 100644 index 0000000..390b8d3 --- /dev/null +++ b/internal/controller/subnetclaim_controller_test.go @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" +) + +var _ = Describe("SubnetClaim Controller", Pending, func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + subnetclaim := &networkingv1alpha.SubnetClaim{} + + BeforeEach(func() { + By("creating the custom resource for the Kind SubnetClaim") + err := k8sClient.Get(ctx, typeNamespacedName, subnetclaim) + if err != nil && errors.IsNotFound(err) { + resource := &networkingv1alpha.SubnetClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &networkingv1alpha.SubnetClaim{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance SubnetClaim") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &SubnetClaimReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go new file mode 100644 index 0000000..d24c0f4 --- /dev/null +++ b/internal/controller/suite_test.go @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package controller + +import ( + "context" + "fmt" + "path/filepath" + "runtime" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/manager" + + networkingv1alpha "go.datum.net/network-services-operator/api/v1alpha" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var k8sManager manager.Manager +var testEnv *envtest.Environment +var ctx context.Context +var cancel context.CancelFunc + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + + // The BinaryAssetsDirectory is only required if you want to run the tests directly + // without call the makefile target test. If not informed it will look for the + // default path defined in controller-runtime which is /usr/local/kubebuilder/. + // Note that you must have the required binaries setup under the bin directory to perform + // the tests directly. When we run make test it will be setup and used automatically. + BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", + fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), + ControlPlane: envtest.ControlPlane{ + APIServer: &envtest.APIServer{}, + }, + } + + testEnv.ControlPlane.APIServer.Configure().Set("advertise-address", "127.0.0.1") + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = networkingv1alpha.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + k8sManager, err = ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + err = (&NetworkBindingReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + }).SetupWithManager(k8sManager) + Expect(err).To(BeNil()) + + go func() { + defer GinkgoRecover() + err = k8sManager.Start(ctx) + Expect(err).ToNot(HaveOccurred(), "failed to run manager") + }() + +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) From 7bf659a3ae53a3ed764ad926ba160b4343675a10 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 19 Nov 2024 02:14:34 +0000 Subject: [PATCH 12/19] Addressed linter violations. --- .../networkbinding_controller_test.go | 20 +++++++++---------- internal/controller/suite_test.go | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/controller/networkbinding_controller_test.go b/internal/controller/networkbinding_controller_test.go index 72629a5..5012fe1 100644 --- a/internal/controller/networkbinding_controller_test.go +++ b/internal/controller/networkbinding_controller_test.go @@ -52,7 +52,7 @@ var _ = Describe("NetworkBinding Controller", func() { } Expect(k8sClient.Create(ctx, network)).To(Succeed()) } - Expect(client.IgnoreNotFound(err)).To(BeNil()) + Expect(client.IgnoreNotFound(err)).To(Succeed()) By("creating a NetworkBinding") err = k8sClient.Get(ctx, bindingNamespacedName, binding) @@ -73,7 +73,7 @@ var _ = Describe("NetworkBinding Controller", func() { } Expect(k8sClient.Create(ctx, resource)).To(Succeed()) } - Expect(client.IgnoreNotFound(err)).To(BeNil()) + Expect(client.IgnoreNotFound(err)).To(Succeed()) }) @@ -82,7 +82,7 @@ var _ = Describe("NetworkBinding Controller", func() { Expect(k8sClient.Get(ctx, bindingNamespacedName, binding)).To(Succeed()) networkContextName, err := networkContextNameForBinding(binding) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(k8sClient.Delete(ctx, binding)).To(Succeed()) networkContext := &networkingv1alpha.NetworkContext{} @@ -91,7 +91,7 @@ var _ = Describe("NetworkBinding Controller", func() { Namespace: "default", } Expect(k8sClient.Get(ctx, networkContextNamespacedName, networkContext)).To(Succeed()) - Expect(k8sClient.Delete(ctx, networkContext)) + Expect(k8sClient.Delete(ctx, networkContext)).To(Succeed()) network := &networkingv1alpha.Network{} Expect(k8sClient.Get(ctx, networkNamespacedName, network)).To(Succeed()) @@ -100,13 +100,13 @@ var _ = Describe("NetworkBinding Controller", func() { It("should successfully create a NetworkContext", func() { err := k8sClient.Get(ctx, bindingNamespacedName, binding) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) bindingReady := apimeta.IsStatusConditionTrue(binding.Status.Conditions, networkingv1alpha.NetworkBindingReady) Expect(bindingReady).To(BeFalse()) networkContextName, err := networkContextNameForBinding(binding) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) var networkContext networkingv1alpha.NetworkContext networkContextObjectKey := client.ObjectKey{ @@ -116,12 +116,12 @@ var _ = Describe("NetworkBinding Controller", func() { Eventually(ctx, func() error { return k8sClient.Get(ctx, networkContextObjectKey, &networkContext) - }).Should(BeNil()) + }).Should(Succeed()) }) It("should become Ready once the referenced NetworkContext is Ready", func() { networkContextName, err := networkContextNameForBinding(binding) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) var networkContext networkingv1alpha.NetworkContext networkContextObjectKey := client.ObjectKey{ @@ -131,7 +131,7 @@ var _ = Describe("NetworkBinding Controller", func() { Eventually(ctx, func() error { return k8sClient.Get(ctx, networkContextObjectKey, &networkContext) - }).Should(BeNil()) + }).Should(Succeed()) // We set the status manually here, as external controllers are responsible // for updating Context readiness right now. @@ -150,7 +150,7 @@ var _ = Describe("NetworkBinding Controller", func() { Eventually(func() bool { err := k8sClient.Get(ctx, bindingNamespacedName, binding) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) return apimeta.IsStatusConditionTrue(binding.Status.Conditions, networkingv1alpha.NetworkBindingReady) }).Should(BeTrue()) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index d24c0f4..467510a 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -88,7 +88,7 @@ var _ = BeforeSuite(func() { Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), }).SetupWithManager(k8sManager) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() From a447d061c176deae00367c768810f783955b49ef Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Tue, 19 Nov 2024 02:29:08 +0000 Subject: [PATCH 13/19] Remove unnecessary duplicate status update. --- internal/controller/networkbinding_controller.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/controller/networkbinding_controller.go b/internal/controller/networkbinding_controller.go index 6488cec..c12223e 100644 --- a/internal/controller/networkbinding_controller.go +++ b/internal/controller/networkbinding_controller.go @@ -149,9 +149,7 @@ func (r *NetworkBindingReconciler) Reconcile(ctx context.Context, req ctrl.Reque readyCondition.Reason = "NetworkContextReady" readyCondition.Message = "Network context is ready." - if err := r.Client.Status().Update(ctx, &binding); err != nil { - return ctrl.Result{}, fmt.Errorf("failed updating binding status: %w", err) - } + // Update is handled in the defer function above. return ctrl.Result{}, nil } From f21ff711d06661decb0a0f75086095f1b8c3ba36 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Wed, 4 Dec 2024 08:51:55 -0600 Subject: [PATCH 14/19] Temporarily define DatumCluster in this project. --- .devcontainer/devcontainer.json | 3 - Makefile | 6 +- PROJECT | 8 + api/v1alpha/datumcluster_types.go | 94 +++++++++++ api/v1alpha/zz_generated.deepcopy.go | 141 +++++++++++++++++ ...etworking.datumapis.com_datumclusters.yaml | 147 ++++++++++++++++++ config/crd/kustomization.yaml | 1 + config/rbac/datumcluster_editor_role.yaml | 27 ++++ config/rbac/datumcluster_viewer_role.yaml | 23 +++ config/rbac/kustomization.yaml | 2 + config/samples/kustomization.yaml | 1 + .../networking_v1alpha_datumcluster_gcp.yaml | 13 ++ go.mod | 2 +- 13 files changed, 461 insertions(+), 7 deletions(-) create mode 100644 api/v1alpha/datumcluster_types.go create mode 100644 config/crd/bases/networking.datumapis.com_datumclusters.yaml create mode 100644 config/rbac/datumcluster_editor_role.yaml create mode 100644 config/rbac/datumcluster_viewer_role.yaml create mode 100644 config/samples/networking_v1alpha_datumcluster_gcp.yaml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 20c5d9b..77c3438 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,9 +13,6 @@ }, "ghcr.io/dhoeric/features/act": {} }, - "runArgs": [ - "--network=host" - ], "customizations": { "vscode": { "settings": { diff --git a/Makefile b/Makefile index 1274fc8..63f7eab 100644 --- a/Makefile +++ b/Makefile @@ -45,11 +45,11 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) rbac:roleName=manager-role crd:generateEmbeddedObjectMeta=true webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. - $(CONTROLLER_GEN) object paths="./..." + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." .PHONY: fmt fmt: ## Run go fmt against code. @@ -96,7 +96,7 @@ build: manifests generate fmt vet ## Build manager binary. .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. - go run ./cmd/main.go + go run ./cmd/main.go -health-probe-bind-address 0 # If you wish to build the manager image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. diff --git a/PROJECT b/PROJECT index 168107a..3a07df4 100644 --- a/PROJECT +++ b/PROJECT @@ -62,4 +62,12 @@ resources: kind: SubnetClaim path: go.datum.net/network-services-operator/api/v1alpha version: v1alpha +- api: + crdVersion: v1 + namespaced: true + domain: datumapis.com + group: networking + kind: DatumCluster + path: go.datum.net/network-services-operator/api/v1alpha + version: v1alpha version: "3" diff --git a/api/v1alpha/datumcluster_types.go b/api/v1alpha/datumcluster_types.go new file mode 100644 index 0000000..f0687be --- /dev/null +++ b/api/v1alpha/datumcluster_types.go @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package v1alpha + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TODO(jreese) move this definition out of network-services-operator. It's here +// right now for convenience as both workload-operator and infra-provider-gcp +// will need to leverage the type. We'll likely want an `infra-cluster-operator` +// project for this. + +// DatumClusterSpec defines the desired state of DatumCluster. +type DatumClusterSpec struct { + // The cluster class for the cluster. + // + // Valid values are: + // - datum-managed + // - self-managed + // + // +kubebuilder:validation:Required + ClusterClassName string `json:"clusterClassName,omitempty"` + + // The topology of the cluster + // + // This may contain arbitrary topology keys. Some keys may be well known, such + // as: + // - topology.datum.net/city-code + // + // +kubebuilder:validation:Required + Topology map[string]string `json:"topology"` + + // The cluster provider + // + // +kubebuilder:validation:Required + Provider DatumClusterProvider `json:"provider"` +} + +type DatumClusterProvider struct { + GCP *GCPClusterProvider `json:"gcp,omitempty"` +} + +type GCPClusterProvider struct { + // The GCP project servicing the cluster + // + // For clusters with the class of `datum-managed`, a service account will be + // required for each unique GCP project ID across all clusters registered in a + // namespace. + // + // +kubebuilder:validation:Required + ProjectID string `json:"projectId,omitempty"` + + // The GCP region servicing the cluster + // + // +kubebuilder:validation:Required + Region string `json:"region,omitempty"` + + // The GCP zone servicing the cluster + // + // +kubebuilder:validation:Required + Zone string `json:"zone,omitempty"` +} + +// DatumClusterStatus defines the observed state of DatumCluster. +type DatumClusterStatus struct { + // Represents the observations of a cluster's current state. + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// DatumCluster is the Schema for the datumclusters API. +type DatumCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DatumClusterSpec `json:"spec,omitempty"` + Status DatumClusterStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// DatumClusterList contains a list of DatumCluster. +type DatumClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DatumCluster `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DatumCluster{}, &DatumClusterList{}) +} diff --git a/api/v1alpha/zz_generated.deepcopy.go b/api/v1alpha/zz_generated.deepcopy.go index 5081178..3ab5eec 100644 --- a/api/v1alpha/zz_generated.deepcopy.go +++ b/api/v1alpha/zz_generated.deepcopy.go @@ -1,5 +1,7 @@ //go:build !ignore_autogenerated +// SPDX-License-Identifier: AGPL-3.0-only + // Code generated by controller-gen. DO NOT EDIT. package v1alpha @@ -11,6 +13,145 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatumCluster) DeepCopyInto(out *DatumCluster) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumCluster. +func (in *DatumCluster) DeepCopy() *DatumCluster { + if in == nil { + return nil + } + out := new(DatumCluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatumCluster) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatumClusterList) DeepCopyInto(out *DatumClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DatumCluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterList. +func (in *DatumClusterList) DeepCopy() *DatumClusterList { + if in == nil { + return nil + } + out := new(DatumClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DatumClusterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatumClusterProvider) DeepCopyInto(out *DatumClusterProvider) { + *out = *in + if in.GCP != nil { + in, out := &in.GCP, &out.GCP + *out = new(GCPClusterProvider) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterProvider. +func (in *DatumClusterProvider) DeepCopy() *DatumClusterProvider { + if in == nil { + return nil + } + out := new(DatumClusterProvider) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatumClusterSpec) DeepCopyInto(out *DatumClusterSpec) { + *out = *in + if in.Topology != nil { + in, out := &in.Topology, &out.Topology + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Provider.DeepCopyInto(&out.Provider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterSpec. +func (in *DatumClusterSpec) DeepCopy() *DatumClusterSpec { + if in == nil { + return nil + } + out := new(DatumClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatumClusterStatus) DeepCopyInto(out *DatumClusterStatus) { + *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]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterStatus. +func (in *DatumClusterStatus) DeepCopy() *DatumClusterStatus { + if in == nil { + return nil + } + out := new(DatumClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GCPClusterProvider) DeepCopyInto(out *GCPClusterProvider) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GCPClusterProvider. +func (in *GCPClusterProvider) DeepCopy() *GCPClusterProvider { + if in == nil { + return nil + } + out := new(GCPClusterProvider) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPBlock) DeepCopyInto(out *IPBlock) { *out = *in diff --git a/config/crd/bases/networking.datumapis.com_datumclusters.yaml b/config/crd/bases/networking.datumapis.com_datumclusters.yaml new file mode 100644 index 0000000..84f9525 --- /dev/null +++ b/config/crd/bases/networking.datumapis.com_datumclusters.yaml @@ -0,0 +1,147 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: datumclusters.networking.datumapis.com +spec: + group: networking.datumapis.com + names: + kind: DatumCluster + listKind: DatumClusterList + plural: datumclusters + singular: datumcluster + scope: Namespaced + versions: + - name: v1alpha + schema: + openAPIV3Schema: + description: DatumCluster is the Schema for the datumclusters API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: DatumClusterSpec defines the desired state of DatumCluster. + properties: + clusterClassName: + description: "The cluster class for the cluster.\n\nValid values are:\n\t- + datum-managed\n\t- self-managed" + type: string + provider: + description: The cluster provider + properties: + gcp: + properties: + projectId: + description: |- + The GCP project servicing the cluster + + For clusters with the class of `datum-managed`, a service account will be + required for each unique GCP project ID across all clusters registered in a + namespace. + type: string + region: + description: The GCP region servicing the cluster + type: string + zone: + description: The GCP zone servicing the cluster + type: string + required: + - projectId + - region + - zone + type: object + type: object + topology: + additionalProperties: + type: string + description: "The topology of the cluster\n\nThis may contain arbitrary + topology keys. Some keys may be well known, such\nas:\n\t- topology.datum.net/city-code" + type: object + required: + - clusterClassName + - provider + - topology + type: object + status: + description: DatumClusterStatus defines the observed state of DatumCluster. + properties: + conditions: + description: Represents the observations of a cluster's current state. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index f3a9385..2c46802 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -8,6 +8,7 @@ resources: - bases/networking.datumapis.com_networkpolicies.yaml - bases/networking.datumapis.com_subnets.yaml - bases/networking.datumapis.com_subnetclaims.yaml +- bases/networking.datumapis.com_datumclusters.yaml # +kubebuilder:scaffold:crdkustomizeresource patches: diff --git a/config/rbac/datumcluster_editor_role.yaml b/config/rbac/datumcluster_editor_role.yaml new file mode 100644 index 0000000..6995246 --- /dev/null +++ b/config/rbac/datumcluster_editor_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to edit datumclusters. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: datumcluster-editor-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - datumclusters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.datumapis.com + resources: + - datumclusters/status + verbs: + - get diff --git a/config/rbac/datumcluster_viewer_role.yaml b/config/rbac/datumcluster_viewer_role.yaml new file mode 100644 index 0000000..b660a8f --- /dev/null +++ b/config/rbac/datumcluster_viewer_role.yaml @@ -0,0 +1,23 @@ +# permissions for end users to view datumclusters. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: network-services-operator + app.kubernetes.io/managed-by: kustomize + name: datumcluster-viewer-role +rules: +- apiGroups: + - networking.datumapis.com + resources: + - datumclusters + verbs: + - get + - list + - watch +- apiGroups: + - networking.datumapis.com + resources: + - datumclusters/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 543983e..2c5de49 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -22,6 +22,8 @@ resources: # default, aiding admins in cluster management. Those roles are # not used by the Project itself. You can comment the following lines # if you do not want those helpers be installed with your Project. +- datumcluster_editor_role.yaml +- datumcluster_viewer_role.yaml - subnetclaim_editor_role.yaml - subnetclaim_viewer_role.yaml - subnet_editor_role.yaml diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index fbc1216..ce0d122 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -6,4 +6,5 @@ resources: - networking_v1alpha_networkpolicy.yaml - networking_v1alpha_subnet.yaml - networking_v1alpha_subnetclaim.yaml +- networking_v1alpha_datumcluster.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/networking_v1alpha_datumcluster_gcp.yaml b/config/samples/networking_v1alpha_datumcluster_gcp.yaml new file mode 100644 index 0000000..6ddceba --- /dev/null +++ b/config/samples/networking_v1alpha_datumcluster_gcp.yaml @@ -0,0 +1,13 @@ +apiVersion: networking.datumapis.com/v1alpha +kind: DatumCluster +metadata: + name: sample-gcp-cluster-dfw +spec: + clusterClassName: datum-managed + topology: + topology.datum.net/city-code: DFW + provider: + gcp: + projectId: datum-cloud-gcp-infra + region: us-south1 + zone: us-south1-a diff --git a/go.mod b/go.mod index c3a5c5f..1cfee04 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23.0 require ( github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 + google.golang.org/protobuf v1.34.2 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 @@ -81,7 +82,6 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect From 24648cf8d2e0106128186077a9dcbc76f887414e Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Wed, 4 Dec 2024 08:56:54 -0600 Subject: [PATCH 15/19] Add DatumClusterReference type. --- api/v1alpha/datumcluster_types.go | 12 ++++++++++++ api/v1alpha/zz_generated.deepcopy.go | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/api/v1alpha/datumcluster_types.go b/api/v1alpha/datumcluster_types.go index f0687be..cc0b32c 100644 --- a/api/v1alpha/datumcluster_types.go +++ b/api/v1alpha/datumcluster_types.go @@ -89,6 +89,18 @@ type DatumClusterList struct { Items []DatumCluster `json:"items"` } +type DatumClusterReference struct { + // Name of a datum cluster + // + // +kubebuilder:validation:Required + Name string `json:"name"` + + // Namespace for the datum cluster + // + // +kubebuilder:validation:Required + Namespace string `json:"namespace"` +} + func init() { SchemeBuilder.Register(&DatumCluster{}, &DatumClusterList{}) } diff --git a/api/v1alpha/zz_generated.deepcopy.go b/api/v1alpha/zz_generated.deepcopy.go index 3ab5eec..ed59045 100644 --- a/api/v1alpha/zz_generated.deepcopy.go +++ b/api/v1alpha/zz_generated.deepcopy.go @@ -92,6 +92,21 @@ func (in *DatumClusterProvider) DeepCopy() *DatumClusterProvider { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatumClusterReference) DeepCopyInto(out *DatumClusterReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterReference. +func (in *DatumClusterReference) DeepCopy() *DatumClusterReference { + if in == nil { + return nil + } + out := new(DatumClusterReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatumClusterSpec) DeepCopyInto(out *DatumClusterSpec) { *out = *in From d19fee49f0b75f59ae37578ba473be51d37852f0 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Thu, 5 Dec 2024 10:12:41 -0600 Subject: [PATCH 16/19] Rename DatumCluster to Location --- PROJECT | 2 +- api/v1alpha/datumcluster_types.go | 106 -------- api/v1alpha/location_types.go | 106 ++++++++ api/v1alpha/networkbinding_types.go | 13 +- api/v1alpha/networkcontext_types.go | 13 +- api/v1alpha/subnet_types.go | 4 +- api/v1alpha/subnetclaim_types.go | 4 +- api/v1alpha/zz_generated.deepcopy.go | 230 ++++++++---------- ...> networking.datumapis.com_locations.yaml} | 39 +-- ...working.datumapis.com_networkbindings.yaml | 25 +- ...working.datumapis.com_networkcontexts.yaml | 24 +- ...networking.datumapis.com_subnetclaims.yaml | 20 +- .../networking.datumapis.com_subnets.yaml | 20 +- config/crd/kustomization.yaml | 26 -- config/rbac/datumcluster_editor_role.yaml | 27 -- config/rbac/datumcluster_viewer_role.yaml | 23 -- config/rbac/kustomization.yaml | 39 --- config/samples/kustomization.yaml | 10 - .../networking_v1alpha_datumcluster_gcp.yaml | 13 - .../samples/networking_v1alpha_network.yaml | 8 +- .../controller/networkbinding_controller.go | 32 +-- .../networkbinding_controller_test.go | 14 +- internal/controller/subnet_controller.go | 11 +- internal/controller/subnetclaim_controller.go | 3 +- 24 files changed, 321 insertions(+), 491 deletions(-) delete mode 100644 api/v1alpha/datumcluster_types.go create mode 100644 api/v1alpha/location_types.go rename config/crd/bases/{networking.datumapis.com_datumclusters.yaml => networking.datumapis.com_locations.yaml} (84%) delete mode 100644 config/crd/kustomization.yaml delete mode 100644 config/rbac/datumcluster_editor_role.yaml delete mode 100644 config/rbac/datumcluster_viewer_role.yaml delete mode 100644 config/rbac/kustomization.yaml delete mode 100644 config/samples/kustomization.yaml delete mode 100644 config/samples/networking_v1alpha_datumcluster_gcp.yaml diff --git a/PROJECT b/PROJECT index 3a07df4..9370f66 100644 --- a/PROJECT +++ b/PROJECT @@ -67,7 +67,7 @@ resources: namespaced: true domain: datumapis.com group: networking - kind: DatumCluster + kind: Location path: go.datum.net/network-services-operator/api/v1alpha version: v1alpha version: "3" diff --git a/api/v1alpha/datumcluster_types.go b/api/v1alpha/datumcluster_types.go deleted file mode 100644 index cc0b32c..0000000 --- a/api/v1alpha/datumcluster_types.go +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only - -package v1alpha - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// TODO(jreese) move this definition out of network-services-operator. It's here -// right now for convenience as both workload-operator and infra-provider-gcp -// will need to leverage the type. We'll likely want an `infra-cluster-operator` -// project for this. - -// DatumClusterSpec defines the desired state of DatumCluster. -type DatumClusterSpec struct { - // The cluster class for the cluster. - // - // Valid values are: - // - datum-managed - // - self-managed - // - // +kubebuilder:validation:Required - ClusterClassName string `json:"clusterClassName,omitempty"` - - // The topology of the cluster - // - // This may contain arbitrary topology keys. Some keys may be well known, such - // as: - // - topology.datum.net/city-code - // - // +kubebuilder:validation:Required - Topology map[string]string `json:"topology"` - - // The cluster provider - // - // +kubebuilder:validation:Required - Provider DatumClusterProvider `json:"provider"` -} - -type DatumClusterProvider struct { - GCP *GCPClusterProvider `json:"gcp,omitempty"` -} - -type GCPClusterProvider struct { - // The GCP project servicing the cluster - // - // For clusters with the class of `datum-managed`, a service account will be - // required for each unique GCP project ID across all clusters registered in a - // namespace. - // - // +kubebuilder:validation:Required - ProjectID string `json:"projectId,omitempty"` - - // The GCP region servicing the cluster - // - // +kubebuilder:validation:Required - Region string `json:"region,omitempty"` - - // The GCP zone servicing the cluster - // - // +kubebuilder:validation:Required - Zone string `json:"zone,omitempty"` -} - -// DatumClusterStatus defines the observed state of DatumCluster. -type DatumClusterStatus struct { - // Represents the observations of a cluster's current state. - Conditions []metav1.Condition `json:"conditions,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status - -// DatumCluster is the Schema for the datumclusters API. -type DatumCluster struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec DatumClusterSpec `json:"spec,omitempty"` - Status DatumClusterStatus `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// DatumClusterList contains a list of DatumCluster. -type DatumClusterList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []DatumCluster `json:"items"` -} - -type DatumClusterReference struct { - // Name of a datum cluster - // - // +kubebuilder:validation:Required - Name string `json:"name"` - - // Namespace for the datum cluster - // - // +kubebuilder:validation:Required - Namespace string `json:"namespace"` -} - -func init() { - SchemeBuilder.Register(&DatumCluster{}, &DatumClusterList{}) -} diff --git a/api/v1alpha/location_types.go b/api/v1alpha/location_types.go new file mode 100644 index 0000000..0012658 --- /dev/null +++ b/api/v1alpha/location_types.go @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package v1alpha + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TODO(jreese) move this definition out of network-services-operator. It's here +// right now for convenience as both workload-operator and infra-provider-gcp +// will need to leverage the type. + +// LocationSpec defines the desired state of Location. +type LocationSpec struct { + // The location class that indicates control plane behavior of entities + // associated with the location. + // + // Valid values are: + // - datum-managed + // - self-managed + // + // +kubebuilder:validation:Required + LocationClassName string `json:"locationClassName,omitempty"` + + // The topology of the location + // + // This may contain arbitrary topology keys. Some keys may be well known, such + // as: + // - topology.datum.net/city-code + // + // +kubebuilder:validation:Required + Topology map[string]string `json:"topology"` + + // The location provider + // + // +kubebuilder:validation:Required + Provider LocationProvider `json:"provider"` +} + +type LocationProvider struct { + GCP *GCPLocationProvider `json:"gcp,omitempty"` +} + +type GCPLocationProvider struct { + // The GCP project servicing the location + // + // For locations with the class of `datum-managed`, a service account will be + // required for each unique GCP project ID across all locations registered in a + // namespace. + // + // +kubebuilder:validation:Required + ProjectID string `json:"projectId,omitempty"` + + // The GCP region servicing the location + // + // +kubebuilder:validation:Required + Region string `json:"region,omitempty"` + + // The GCP zone servicing the location + // + // +kubebuilder:validation:Required + Zone string `json:"zone,omitempty"` +} + +// LocationStatus defines the observed state of Location. +type LocationStatus struct { + // Represents the observations of a location's current state. + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Location is the Schema for the locations API. +type Location struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec LocationSpec `json:"spec,omitempty"` + Status LocationStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// LocationList contains a list of Location. +type LocationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Location `json:"items"` +} + +type LocationReference struct { + // Name of a datum location + // + // +kubebuilder:validation:Required + Name string `json:"name"` + + // Namespace for the datum location + // + // +kubebuilder:validation:Required + Namespace string `json:"namespace"` +} + +func init() { + SchemeBuilder.Register(&Location{}, &LocationList{}) +} diff --git a/api/v1alpha/networkbinding_types.go b/api/v1alpha/networkbinding_types.go index 55eb378..b43e40f 100644 --- a/api/v1alpha/networkbinding_types.go +++ b/api/v1alpha/networkbinding_types.go @@ -13,19 +13,10 @@ type NetworkBindingSpec struct { // +kubebuilder:validation:Required Network NetworkRef `json:"network,omitempty"` - // The topology of where this binding exists - // - // This may contain arbitrary topology keys. Some keys may be well known, such - // as: - // - topology.datum.net/city-code - // - topology.datum.net/cluster-name - // - topology.datum.net/cluster-namespace - // - // Each unique value of this field across bindings in the namespace will result - // in a NetworkAttachment to be created. + // The location of where a network binding exists. // // +kubebuilder:validation:Required - Topology map[string]string `json:"topology"` + Location LocationReference `json:"location,omitempty"` } // NetworkBindingObjectReference contains sufficient information for diff --git a/api/v1alpha/networkcontext_types.go b/api/v1alpha/networkcontext_types.go index b1ab5eb..f8c022a 100644 --- a/api/v1alpha/networkcontext_types.go +++ b/api/v1alpha/networkcontext_types.go @@ -13,19 +13,10 @@ type NetworkContextSpec struct { // +kubebuilder:validation:Required Network LocalNetworkRef `json:"network"` - // The topology of where this context exists - // - // This may contain arbitrary topology keys. Some keys may be well known, such - // as: - // - topology.datum.net/city-code - // - topology.datum.net/cluster-name - // - topology.datum.net/cluster-namespace - // - // The combined keys and values MUST be unique for contexts in the same - // network. + // The location of where a network context exists. // // +kubebuilder:validation:Required - Topology map[string]string `json:"topology"` + Location LocationReference `json:"location,omitempty"` } // NetworkContextStatus defines the observed state of NetworkContext diff --git a/api/v1alpha/subnet_types.go b/api/v1alpha/subnet_types.go index 3c50466..5f7fb69 100644 --- a/api/v1alpha/subnet_types.go +++ b/api/v1alpha/subnet_types.go @@ -18,10 +18,10 @@ type SubnetSpec struct { // +kubebuilder:validation:Required NetworkContext LocalNetworkContextRef `json:"networkContext"` - // The topology which a subnet is associated with + // The location which a subnet is associated with // // +kubebuilder:validation:Required - Topology map[string]string `json:"topology"` + Location LocationReference `json:"location,omitempty"` // The IP family of a subnet // diff --git a/api/v1alpha/subnetclaim_types.go b/api/v1alpha/subnetclaim_types.go index 31534a3..86f25a0 100644 --- a/api/v1alpha/subnetclaim_types.go +++ b/api/v1alpha/subnetclaim_types.go @@ -18,10 +18,10 @@ type SubnetClaimSpec struct { // +kubebuilder:validation:Required NetworkContext LocalNetworkContextRef `json:"networkContext"` - // The topology which the subnet is associated with + // The location which a subnet claim is associated with // // +kubebuilder:validation:Required - Topology map[string]string `json:"topology"` + Location LocationReference `json:"location,omitempty"` // The IP family of a subnet claim // diff --git a/api/v1alpha/zz_generated.deepcopy.go b/api/v1alpha/zz_generated.deepcopy.go index ed59045..436623b 100644 --- a/api/v1alpha/zz_generated.deepcopy.go +++ b/api/v1alpha/zz_generated.deepcopy.go @@ -14,220 +14,220 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatumCluster) DeepCopyInto(out *DatumCluster) { +func (in *GCPLocationProvider) DeepCopyInto(out *GCPLocationProvider) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumCluster. -func (in *DatumCluster) DeepCopy() *DatumCluster { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GCPLocationProvider. +func (in *GCPLocationProvider) DeepCopy() *GCPLocationProvider { if in == nil { return nil } - out := new(DatumCluster) + out := new(GCPLocationProvider) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DatumCluster) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatumClusterList) DeepCopyInto(out *DatumClusterList) { +func (in *IPBlock) DeepCopyInto(out *IPBlock) { *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]DatumCluster, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.Except != nil { + in, out := &in.Except, &out.Except + *out = make([]string, len(*in)) + copy(*out, *in) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterList. -func (in *DatumClusterList) DeepCopy() *DatumClusterList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPBlock. +func (in *IPBlock) DeepCopy() *IPBlock { if in == nil { return nil } - out := new(DatumClusterList) + out := new(IPBlock) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DatumClusterList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatumClusterProvider) DeepCopyInto(out *DatumClusterProvider) { +func (in *LocalNetworkContextRef) DeepCopyInto(out *LocalNetworkContextRef) { *out = *in - if in.GCP != nil { - in, out := &in.GCP, &out.GCP - *out = new(GCPClusterProvider) - **out = **in - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterProvider. -func (in *DatumClusterProvider) DeepCopy() *DatumClusterProvider { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalNetworkContextRef. +func (in *LocalNetworkContextRef) DeepCopy() *LocalNetworkContextRef { if in == nil { return nil } - out := new(DatumClusterProvider) + out := new(LocalNetworkContextRef) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatumClusterReference) DeepCopyInto(out *DatumClusterReference) { +func (in *LocalNetworkRef) DeepCopyInto(out *LocalNetworkRef) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterReference. -func (in *DatumClusterReference) DeepCopy() *DatumClusterReference { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalNetworkRef. +func (in *LocalNetworkRef) DeepCopy() *LocalNetworkRef { if in == nil { return nil } - out := new(DatumClusterReference) + out := new(LocalNetworkRef) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatumClusterSpec) DeepCopyInto(out *DatumClusterSpec) { +func (in *LocalSubnetReference) DeepCopyInto(out *LocalSubnetReference) { *out = *in - if in.Topology != nil { - in, out := &in.Topology, &out.Topology - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.Provider.DeepCopyInto(&out.Provider) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterSpec. -func (in *DatumClusterSpec) DeepCopy() *DatumClusterSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalSubnetReference. +func (in *LocalSubnetReference) DeepCopy() *LocalSubnetReference { if in == nil { return nil } - out := new(DatumClusterSpec) + out := new(LocalSubnetReference) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatumClusterStatus) DeepCopyInto(out *DatumClusterStatus) { +func (in *Location) DeepCopyInto(out *Location) { *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]) - } - } + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatumClusterStatus. -func (in *DatumClusterStatus) DeepCopy() *DatumClusterStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Location. +func (in *Location) DeepCopy() *Location { if in == nil { return nil } - out := new(DatumClusterStatus) + out := new(Location) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Location) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GCPClusterProvider) DeepCopyInto(out *GCPClusterProvider) { +func (in *LocationList) DeepCopyInto(out *LocationList) { *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Location, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GCPClusterProvider. -func (in *GCPClusterProvider) DeepCopy() *GCPClusterProvider { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocationList. +func (in *LocationList) DeepCopy() *LocationList { if in == nil { return nil } - out := new(GCPClusterProvider) + out := new(LocationList) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *LocationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IPBlock) DeepCopyInto(out *IPBlock) { +func (in *LocationProvider) DeepCopyInto(out *LocationProvider) { *out = *in - if in.Except != nil { - in, out := &in.Except, &out.Except - *out = make([]string, len(*in)) - copy(*out, *in) + if in.GCP != nil { + in, out := &in.GCP, &out.GCP + *out = new(GCPLocationProvider) + **out = **in } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPBlock. -func (in *IPBlock) DeepCopy() *IPBlock { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocationProvider. +func (in *LocationProvider) DeepCopy() *LocationProvider { if in == nil { return nil } - out := new(IPBlock) + out := new(LocationProvider) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LocalNetworkContextRef) DeepCopyInto(out *LocalNetworkContextRef) { +func (in *LocationReference) DeepCopyInto(out *LocationReference) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalNetworkContextRef. -func (in *LocalNetworkContextRef) DeepCopy() *LocalNetworkContextRef { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocationReference. +func (in *LocationReference) DeepCopy() *LocationReference { if in == nil { return nil } - out := new(LocalNetworkContextRef) + out := new(LocationReference) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LocalNetworkRef) DeepCopyInto(out *LocalNetworkRef) { +func (in *LocationSpec) DeepCopyInto(out *LocationSpec) { *out = *in + if in.Topology != nil { + in, out := &in.Topology, &out.Topology + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Provider.DeepCopyInto(&out.Provider) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalNetworkRef. -func (in *LocalNetworkRef) DeepCopy() *LocalNetworkRef { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocationSpec. +func (in *LocationSpec) DeepCopy() *LocationSpec { if in == nil { return nil } - out := new(LocalNetworkRef) + out := new(LocationSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LocalSubnetReference) DeepCopyInto(out *LocalSubnetReference) { +func (in *LocationStatus) DeepCopyInto(out *LocationStatus) { *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]) + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalSubnetReference. -func (in *LocalSubnetReference) DeepCopy() *LocalSubnetReference { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocationStatus. +func (in *LocationStatus) DeepCopy() *LocationStatus { if in == nil { return nil } - out := new(LocalSubnetReference) + out := new(LocationStatus) in.DeepCopyInto(out) return out } @@ -264,7 +264,7 @@ func (in *NetworkBinding) DeepCopyInto(out *NetworkBinding) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) + out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) } @@ -337,13 +337,7 @@ func (in *NetworkBindingObjectReference) DeepCopy() *NetworkBindingObjectReferen func (in *NetworkBindingSpec) DeepCopyInto(out *NetworkBindingSpec) { *out = *in out.Network = in.Network - if in.Topology != nil { - in, out := &in.Topology, &out.Topology - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } + out.Location = in.Location } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkBindingSpec. @@ -388,7 +382,7 @@ func (in *NetworkContext) DeepCopyInto(out *NetworkContext) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) + out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) } @@ -461,13 +455,7 @@ func (in *NetworkContextRef) DeepCopy() *NetworkContextRef { func (in *NetworkContextSpec) DeepCopyInto(out *NetworkContextSpec) { *out = *in out.Network = in.Network - if in.Topology != nil { - in, out := &in.Topology, &out.Topology - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } + out.Location = in.Location } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkContextSpec. @@ -790,7 +778,7 @@ func (in *Subnet) DeepCopyInto(out *Subnet) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) + out.Spec = in.Spec in.Status.DeepCopyInto(&out.Status) } @@ -875,13 +863,7 @@ func (in *SubnetClaimList) DeepCopyObject() runtime.Object { func (in *SubnetClaimSpec) DeepCopyInto(out *SubnetClaimSpec) { *out = *in out.NetworkContext = in.NetworkContext - if in.Topology != nil { - in, out := &in.Topology, &out.Topology - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } + out.Location = in.Location if in.StartAddress != nil { in, out := &in.StartAddress, &out.StartAddress *out = new(string) @@ -977,13 +959,7 @@ func (in *SubnetList) DeepCopyObject() runtime.Object { func (in *SubnetSpec) DeepCopyInto(out *SubnetSpec) { *out = *in out.NetworkContext = in.NetworkContext - if in.Topology != nil { - in, out := &in.Topology, &out.Topology - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } + out.Location = in.Location } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetSpec. diff --git a/config/crd/bases/networking.datumapis.com_datumclusters.yaml b/config/crd/bases/networking.datumapis.com_locations.yaml similarity index 84% rename from config/crd/bases/networking.datumapis.com_datumclusters.yaml rename to config/crd/bases/networking.datumapis.com_locations.yaml index 84f9525..e3f1b96 100644 --- a/config/crd/bases/networking.datumapis.com_datumclusters.yaml +++ b/config/crd/bases/networking.datumapis.com_locations.yaml @@ -4,20 +4,20 @@ kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.16.4 - name: datumclusters.networking.datumapis.com + name: locations.networking.datumapis.com spec: group: networking.datumapis.com names: - kind: DatumCluster - listKind: DatumClusterList - plural: datumclusters - singular: datumcluster + kind: Location + listKind: LocationList + plural: locations + singular: location scope: Namespaced versions: - name: v1alpha schema: openAPIV3Schema: - description: DatumCluster is the Schema for the datumclusters API. + description: Location is the Schema for the locations API. properties: apiVersion: description: |- @@ -37,30 +37,31 @@ spec: metadata: type: object spec: - description: DatumClusterSpec defines the desired state of DatumCluster. + description: LocationSpec defines the desired state of Location. properties: - clusterClassName: - description: "The cluster class for the cluster.\n\nValid values are:\n\t- + locationClassName: + description: "The location class that indicates control plane behavior + of entities\nassociated with the location.\n\nValid values are:\n\t- datum-managed\n\t- self-managed" type: string provider: - description: The cluster provider + description: The location provider properties: gcp: properties: projectId: description: |- - The GCP project servicing the cluster + The GCP project servicing the location - For clusters with the class of `datum-managed`, a service account will be - required for each unique GCP project ID across all clusters registered in a + For locations with the class of `datum-managed`, a service account will be + required for each unique GCP project ID across all locations registered in a namespace. type: string region: - description: The GCP region servicing the cluster + description: The GCP region servicing the location type: string zone: - description: The GCP zone servicing the cluster + description: The GCP zone servicing the location type: string required: - projectId @@ -71,19 +72,19 @@ spec: topology: additionalProperties: type: string - description: "The topology of the cluster\n\nThis may contain arbitrary + description: "The topology of the location\n\nThis may contain arbitrary topology keys. Some keys may be well known, such\nas:\n\t- topology.datum.net/city-code" type: object required: - - clusterClassName + - locationClassName - provider - topology type: object status: - description: DatumClusterStatus defines the observed state of DatumCluster. + description: LocationStatus defines the observed state of Location. properties: conditions: - description: Represents the observations of a cluster's current state. + description: Represents the observations of a location's current state. items: description: Condition contains details for one aspect of the current state of this API Resource. diff --git a/config/crd/bases/networking.datumapis.com_networkbindings.yaml b/config/crd/bases/networking.datumapis.com_networkbindings.yaml index dfaccc8..6b7fe5c 100644 --- a/config/crd/bases/networking.datumapis.com_networkbindings.yaml +++ b/config/crd/bases/networking.datumapis.com_networkbindings.yaml @@ -39,6 +39,19 @@ spec: spec: description: NetworkBindingSpec defines the desired state of NetworkBinding properties: + location: + description: The location of where a network binding exists. + properties: + name: + description: Name of a datum location + type: string + namespace: + description: Namespace for the datum location + type: string + required: + - name + - namespace + type: object network: description: The network that the binding is for. properties: @@ -54,19 +67,9 @@ spec: required: - name type: object - topology: - additionalProperties: - type: string - description: "The topology of where this binding exists\n\nThis may - contain arbitrary topology keys. Some keys may be well known, such\nas:\n\t- - topology.datum.net/city-code\n\t- topology.datum.net/cluster-name\n\t- - topology.datum.net/cluster-namespace\n\nEach unique value of this - field across bindings in the namespace will result\nin a NetworkAttachment - to be created." - type: object required: + - location - network - - topology type: object status: description: NetworkBindingStatus defines the observed state of NetworkBinding diff --git a/config/crd/bases/networking.datumapis.com_networkcontexts.yaml b/config/crd/bases/networking.datumapis.com_networkcontexts.yaml index faf7705..e66ed3c 100644 --- a/config/crd/bases/networking.datumapis.com_networkcontexts.yaml +++ b/config/crd/bases/networking.datumapis.com_networkcontexts.yaml @@ -39,6 +39,19 @@ spec: spec: description: NetworkContextSpec defines the desired state of NetworkContext properties: + location: + description: The location of where a network context exists. + properties: + name: + description: Name of a datum location + type: string + namespace: + description: Namespace for the datum location + type: string + required: + - name + - namespace + type: object network: description: The attached network properties: @@ -48,18 +61,9 @@ spec: required: - name type: object - topology: - additionalProperties: - type: string - description: "The topology of where this context exists\n\nThis may - contain arbitrary topology keys. Some keys may be well known, such\nas:\n\t- - topology.datum.net/city-code\n\t- topology.datum.net/cluster-name\n\t- - topology.datum.net/cluster-namespace\n\nThe combined keys and values - MUST be unique for contexts in the same\nnetwork." - type: object required: + - location - network - - topology type: object status: description: NetworkContextStatus defines the observed state of NetworkContext diff --git a/config/crd/bases/networking.datumapis.com_subnetclaims.yaml b/config/crd/bases/networking.datumapis.com_subnetclaims.yaml index 9a75284..4b8e1c2 100644 --- a/config/crd/bases/networking.datumapis.com_subnetclaims.yaml +++ b/config/crd/bases/networking.datumapis.com_subnetclaims.yaml @@ -45,6 +45,19 @@ spec: - IPv4 - IPv6 type: string + location: + description: The location which a subnet claim is associated with + properties: + name: + description: Name of a datum location + type: string + namespace: + description: Namespace for the datum location + type: string + required: + - name + - namespace + type: object networkContext: description: The network context to claim a subnet in properties: @@ -64,16 +77,11 @@ spec: subnetClass: description: The class of subnet required type: string - topology: - additionalProperties: - type: string - description: The topology which the subnet is associated with - type: object required: - ipFamily + - location - networkContext - subnetClass - - topology type: object status: description: SubnetClaimStatus defines the observed state of SubnetClaim diff --git a/config/crd/bases/networking.datumapis.com_subnets.yaml b/config/crd/bases/networking.datumapis.com_subnets.yaml index c7426ed..1a10b04 100644 --- a/config/crd/bases/networking.datumapis.com_subnets.yaml +++ b/config/crd/bases/networking.datumapis.com_subnets.yaml @@ -45,6 +45,19 @@ spec: - IPv4 - IPv6 type: string + location: + description: The location which a subnet is associated with + properties: + name: + description: Name of a datum location + type: string + namespace: + description: Namespace for the datum location + type: string + required: + - name + - namespace + type: object networkContext: description: A subnet's network context properties: @@ -64,18 +77,13 @@ spec: subnetClass: description: The class of subnet type: string - topology: - additionalProperties: - type: string - description: The topology which a subnet is associated with - type: object required: - ipFamily + - location - networkContext - prefixLength - startAddress - subnetClass - - topology type: object status: description: SubnetStatus defines the observed state of a Subnet diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml deleted file mode 100644 index 2c46802..0000000 --- a/config/crd/kustomization.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# This kustomization.yaml is not intended to be run by itself, -# since it depends on service name and namespace that are out of this kustomize package. -# It should be run by config/default -resources: -- bases/networking.datumapis.com_networks.yaml -- bases/networking.datumapis.com_networkbindings.yaml -- bases/networking.datumapis.com_networkcontexts.yaml -- bases/networking.datumapis.com_networkpolicies.yaml -- bases/networking.datumapis.com_subnets.yaml -- bases/networking.datumapis.com_subnetclaims.yaml -- bases/networking.datumapis.com_datumclusters.yaml -# +kubebuilder:scaffold:crdkustomizeresource - -patches: -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. -# patches here are for enabling the conversion webhook for each CRD -# +kubebuilder:scaffold:crdkustomizewebhookpatch - -# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. -# patches here are for enabling the CA injection for each CRD -# +kubebuilder:scaffold:crdkustomizecainjectionpatch - -# [WEBHOOK] To enable webhook, uncomment the following section -# the following config is for teaching kustomize how to do kustomization for CRDs. -#configurations: -#- kustomizeconfig.yaml diff --git a/config/rbac/datumcluster_editor_role.yaml b/config/rbac/datumcluster_editor_role.yaml deleted file mode 100644 index 6995246..0000000 --- a/config/rbac/datumcluster_editor_role.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# permissions for end users to edit datumclusters. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: network-services-operator - app.kubernetes.io/managed-by: kustomize - name: datumcluster-editor-role -rules: -- apiGroups: - - networking.datumapis.com - resources: - - datumclusters - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - networking.datumapis.com - resources: - - datumclusters/status - verbs: - - get diff --git a/config/rbac/datumcluster_viewer_role.yaml b/config/rbac/datumcluster_viewer_role.yaml deleted file mode 100644 index b660a8f..0000000 --- a/config/rbac/datumcluster_viewer_role.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# permissions for end users to view datumclusters. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: network-services-operator - app.kubernetes.io/managed-by: kustomize - name: datumcluster-viewer-role -rules: -- apiGroups: - - networking.datumapis.com - resources: - - datumclusters - verbs: - - get - - list - - watch -- apiGroups: - - networking.datumapis.com - resources: - - datumclusters/status - verbs: - - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml deleted file mode 100644 index 2c5de49..0000000 --- a/config/rbac/kustomization.yaml +++ /dev/null @@ -1,39 +0,0 @@ -resources: -# All RBAC will be applied under this service account in -# the deployment namespace. You may comment out this resource -# if your manager will use a service account that exists at -# runtime. Be sure to update RoleBinding and ClusterRoleBinding -# subjects if changing service account names. -- service_account.yaml -- role.yaml -- role_binding.yaml -- leader_election_role.yaml -- leader_election_role_binding.yaml -# The following RBAC configurations are used to protect -# the metrics endpoint with authn/authz. These configurations -# ensure that only authorized users and service accounts -# can access the metrics endpoint. Comment the following -# permissions if you want to disable this protection. -# More info: https://book.kubebuilder.io/reference/metrics.html -- metrics_auth_role.yaml -- metrics_auth_role_binding.yaml -- metrics_reader_role.yaml -# For each CRD, "Editor" and "Viewer" roles are scaffolded by -# default, aiding admins in cluster management. Those roles are -# not used by the Project itself. You can comment the following lines -# if you do not want those helpers be installed with your Project. -- datumcluster_editor_role.yaml -- datumcluster_viewer_role.yaml -- subnetclaim_editor_role.yaml -- subnetclaim_viewer_role.yaml -- subnet_editor_role.yaml -- subnet_viewer_role.yaml -- networkpolicy_editor_role.yaml -- networkpolicy_viewer_role.yaml -- networkcontext_editor_role.yaml -- networkcontext_viewer_role.yaml -- networkbinding_editor_role.yaml -- networkbinding_viewer_role.yaml -- network_editor_role.yaml -- network_viewer_role.yaml - diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml deleted file mode 100644 index ce0d122..0000000 --- a/config/samples/kustomization.yaml +++ /dev/null @@ -1,10 +0,0 @@ -## Append samples of your project ## -resources: -- networking_v1alpha_network.yaml -- networking_v1alpha_networkbinding.yaml -- networking_v1alpha_networkcontext.yaml -- networking_v1alpha_networkpolicy.yaml -- networking_v1alpha_subnet.yaml -- networking_v1alpha_subnetclaim.yaml -- networking_v1alpha_datumcluster.yaml -# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/networking_v1alpha_datumcluster_gcp.yaml b/config/samples/networking_v1alpha_datumcluster_gcp.yaml deleted file mode 100644 index 6ddceba..0000000 --- a/config/samples/networking_v1alpha_datumcluster_gcp.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: networking.datumapis.com/v1alpha -kind: DatumCluster -metadata: - name: sample-gcp-cluster-dfw -spec: - clusterClassName: datum-managed - topology: - topology.datum.net/city-code: DFW - provider: - gcp: - projectId: datum-cloud-gcp-infra - region: us-south1 - zone: us-south1-a diff --git a/config/samples/networking_v1alpha_network.yaml b/config/samples/networking_v1alpha_network.yaml index 2799b39..a5b6f7c 100644 --- a/config/samples/networking_v1alpha_network.yaml +++ b/config/samples/networking_v1alpha_network.yaml @@ -1,9 +1,7 @@ apiVersion: networking.datumapis.com/v1alpha kind: Network metadata: - labels: - app.kubernetes.io/name: network-services-operator - app.kubernetes.io/managed-by: kustomize - name: network-sample + name: default spec: - # TODO(user): Add fields here + ipam: + mode: Auto diff --git a/internal/controller/networkbinding_controller.go b/internal/controller/networkbinding_controller.go index c12223e..6fd8dda 100644 --- a/internal/controller/networkbinding_controller.go +++ b/internal/controller/networkbinding_controller.go @@ -4,15 +4,12 @@ package controller import ( "context" - "encoding/json" "fmt" - "hash/fnv" apierrors "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/rand" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -92,15 +89,10 @@ func (r *NetworkBindingReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, fmt.Errorf("failed fetching network for binding: %w", err) } - networkContextName, err := networkContextNameForBinding(&binding) - if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to determine network context name: %w", err) - } - var networkContext networkingv1alpha.NetworkContext networkContextObjectKey := client.ObjectKey{ Namespace: networkNamespace, - Name: networkContextName, + Name: networkContextNameForBinding(&binding), } if err := r.Client.Get(ctx, networkContextObjectKey, &networkContext); client.IgnoreNotFound(err) != nil { return ctrl.Result{}, fmt.Errorf("failed fetching network context: %w", err) @@ -109,14 +101,14 @@ func (r *NetworkBindingReconciler) Reconcile(ctx context.Context, req ctrl.Reque if networkContext.CreationTimestamp.IsZero() { networkContext = networkingv1alpha.NetworkContext{ ObjectMeta: metav1.ObjectMeta{ - Namespace: networkNamespace, - Name: networkContextName, + Namespace: networkContextObjectKey.Namespace, + Name: networkContextObjectKey.Name, }, Spec: networkingv1alpha.NetworkContextSpec{ Network: networkingv1alpha.LocalNetworkRef{ Name: binding.Spec.Network.Name, }, - Topology: binding.Spec.Topology, + Location: binding.Spec.Location, }, } @@ -166,18 +158,6 @@ func (r *NetworkBindingReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func networkContextNameForBinding(binding *networkingv1alpha.NetworkBinding) (string, error) { - if binding.CreationTimestamp.IsZero() { - return "", fmt.Errorf("binding has not been created") - } - topologyBytes, err := json.Marshal(binding.Spec.Topology) - if err != nil { - return "", fmt.Errorf("failed marshaling topology to json: %w", err) - } - - f := fnv.New32a() - f.Write(topologyBytes) - topologyHash := rand.SafeEncodeString(fmt.Sprint(f.Sum32())) - - return fmt.Sprintf("%s-%s", binding.Spec.Network.Name, topologyHash), nil +func networkContextNameForBinding(binding *networkingv1alpha.NetworkBinding) string { + return fmt.Sprintf("%s-%s-%s", binding.Spec.Network.Name, binding.Spec.Location.Namespace, binding.Spec.Location.Name) } diff --git a/internal/controller/networkbinding_controller_test.go b/internal/controller/networkbinding_controller_test.go index 5012fe1..adf7bf2 100644 --- a/internal/controller/networkbinding_controller_test.go +++ b/internal/controller/networkbinding_controller_test.go @@ -66,8 +66,9 @@ var _ = Describe("NetworkBinding Controller", func() { Network: networkingv1alpha.NetworkRef{ Name: network.Name, }, - Topology: map[string]string{ - "topo-key": "value", + Location: networkingv1alpha.LocationReference{ + Namespace: "default", + Name: "some-location", }, }, } @@ -81,8 +82,7 @@ var _ = Describe("NetworkBinding Controller", func() { binding := &networkingv1alpha.NetworkBinding{} Expect(k8sClient.Get(ctx, bindingNamespacedName, binding)).To(Succeed()) - networkContextName, err := networkContextNameForBinding(binding) - Expect(err).ToNot(HaveOccurred()) + networkContextName := networkContextNameForBinding(binding) Expect(k8sClient.Delete(ctx, binding)).To(Succeed()) networkContext := &networkingv1alpha.NetworkContext{} @@ -105,8 +105,7 @@ var _ = Describe("NetworkBinding Controller", func() { bindingReady := apimeta.IsStatusConditionTrue(binding.Status.Conditions, networkingv1alpha.NetworkBindingReady) Expect(bindingReady).To(BeFalse()) - networkContextName, err := networkContextNameForBinding(binding) - Expect(err).ToNot(HaveOccurred()) + networkContextName := networkContextNameForBinding(binding) var networkContext networkingv1alpha.NetworkContext networkContextObjectKey := client.ObjectKey{ @@ -120,8 +119,7 @@ var _ = Describe("NetworkBinding Controller", func() { }) It("should become Ready once the referenced NetworkContext is Ready", func() { - networkContextName, err := networkContextNameForBinding(binding) - Expect(err).ToNot(HaveOccurred()) + networkContextName := networkContextNameForBinding(binding) var networkContext networkingv1alpha.NetworkContext networkContextObjectKey := client.ObjectKey{ diff --git a/internal/controller/subnet_controller.go b/internal/controller/subnet_controller.go index e9a4bb5..a0585c8 100644 --- a/internal/controller/subnet_controller.go +++ b/internal/controller/subnet_controller.go @@ -68,8 +68,17 @@ func (r *SubnetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, fmt.Errorf("failed fetching network context: %w", err) } + var location networkingv1alpha.Location + locationObjectKey := client.ObjectKey{ + Namespace: networkContext.Spec.Location.Namespace, + Name: networkContext.Spec.Location.Name, + } + if err := r.Client.Get(ctx, locationObjectKey, &location); err != nil { + return ctrl.Result{}, fmt.Errorf("failed fetching network context location: %w", err) + } + // TODO(jreese) get topology key from well known package - cityCode, ok := networkContext.Spec.Topology["topology.datum.net/city-code"] + cityCode, ok := location.Spec.Topology["topology.datum.net/city-code"] if !ok { return ctrl.Result{}, fmt.Errorf("unable to find topology key: topology.datum.net/city-code") } diff --git a/internal/controller/subnetclaim_controller.go b/internal/controller/subnetclaim_controller.go index bad7f80..11bfb29 100644 --- a/internal/controller/subnetclaim_controller.go +++ b/internal/controller/subnetclaim_controller.go @@ -82,9 +82,10 @@ func (r *SubnetClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) Name: claim.Name, }, Spec: networkingv1alpha.SubnetSpec{ + IPFamily: claim.Spec.IPFamily, SubnetClass: claim.Spec.SubnetClass, NetworkContext: claim.Spec.NetworkContext, - Topology: claim.Spec.Topology, + Location: claim.Spec.Location, }, } From b5b39ebd37b9a7bc0c6fa63ed6d6c6078ecc0f01 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Fri, 6 Dec 2024 19:11:16 -0600 Subject: [PATCH 17/19] Add POC disclaimer to readme. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 27970be..32964a6 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ # Datum Network Services + +> [!CAUTION] +> This operator is currently in a POC phase. The POC integration branch will +> be orphaned and separate PRs opened for discrete components (APIs, controllers, +> etc) as they mature. + +See the [apis](./api/v1alpha) defined for this project. From 248afaa7d7f78d9b13792a7e46c0a29b1fbdfbfb Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Wed, 11 Dec 2024 17:14:09 -0600 Subject: [PATCH 18/19] Add leader election namespace flag, and missing kustomization file. --- cmd/main.go | 15 +++++++++------ config/crd/kustomization.yaml | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 config/crd/kustomization.yaml diff --git a/cmd/main.go b/cmd/main.go index 9ce2816..bd0a47a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -39,6 +39,7 @@ func init() { func main() { var metricsAddr string var enableLeaderElection bool + var leaderElectionNamespace string var probeAddr string var secureMetrics bool var enableHTTP2 bool @@ -49,6 +50,7 @@ func main() { flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") + flag.StringVar(&leaderElectionNamespace, "leader-elect-namespace", "", "The namespace to use for leader election.") flag.BoolVar(&secureMetrics, "metrics-secure", true, "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") flag.BoolVar(&enableHTTP2, "enable-http2", false, @@ -103,12 +105,13 @@ func main() { } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - Metrics: metricsServerOptions, - WebhookServer: webhookServer, - HealthProbeBindAddress: probeAddr, - LeaderElection: enableLeaderElection, - LeaderElectionID: "6a7d51cc.datumapis.com", + Scheme: scheme, + Metrics: metricsServerOptions, + WebhookServer: webhookServer, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "6a7d51cc.datumapis.com", + LeaderElectionNamespace: leaderElectionNamespace, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml new file mode 100644 index 0000000..5b981d8 --- /dev/null +++ b/config/crd/kustomization.yaml @@ -0,0 +1,26 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/networking.datumapis.com_networks.yaml +- bases/networking.datumapis.com_networkbindings.yaml +- bases/networking.datumapis.com_networkcontexts.yaml +- bases/networking.datumapis.com_networkpolicies.yaml +- bases/networking.datumapis.com_subnets.yaml +- bases/networking.datumapis.com_subnetclaims.yaml +- bases/networking.datumapis.com_locations.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patches: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +# +kubebuilder:scaffold:crdkustomizecainjectionpatch + +# [WEBHOOK] To enable webhook, uncomment the following section +# the following config is for teaching kustomize how to do kustomization for CRDs. +#configurations: +#- kustomizeconfig.yaml From 988bc35e818a212515be4266f94624bfbb13ce05 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Thu, 2 Jan 2025 13:17:57 -0600 Subject: [PATCH 19/19] Update README. --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 32964a6..3001094 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,87 @@ # Datum Network Services -> [!CAUTION] -> This operator is currently in a POC phase. The POC integration branch will -> be orphaned and separate PRs opened for discrete components (APIs, controllers, -> etc) as they mature. +The network services operator defines APIs and core controllers for interacting +with network related entities such as Networks, Network Contexts, and Subnets. -See the [apis](./api/v1alpha) defined for this project. +The operator itself is not responsible for provisioning of resources onto data +planes, but instead relies on infrastructure providers such as the [GCP +Infrastructure Provider][infra-provider-gcp] to interact with vendor or platform +specific APIs in order to satisfy the intents defined in custom resources + +[infra-provider-gcp]: https://github.com/datum-cloud/infra-provider-gcp + +## Documentation + +Documentation will be available at [docs.datum.net](https://docs.datum.net/) +shortly. + +## Getting Started + +### Prerequisites + +- go version v1.23.0+ +- docker version 17.03+. +- kubectl version v1.31.0+. +- Access to a Kubernetes v1.31.0+ cluster. + +### To Deploy on the cluster + +**Build and push your image to the location specified by `IMG`:** + +```sh +make docker-build docker-push IMG=/tmp:tag +``` + +**NOTE:** This image ought to be published in the personal registry you specified. +And it is required to have access to pull the image from the working environment. +Make sure you have the proper permission to the registry if the above commands don’t work. + +**Install the CRDs into the cluster:** + +```sh +make install +``` + +**Deploy the Manager to the cluster with the image specified by `IMG`:** + +```sh +make deploy IMG=/tmp:tag +``` + +> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin +privileges or be logged in as admin. + +**Create instances of your solution** +You can apply the samples (examples) from the config/sample: + +```sh +kubectl apply -k config/samples/ +``` + +>**NOTE**: Ensure that the samples has default values to test it out. + +### To Uninstall + +**Delete the instances (CRs) from the cluster:** + +```sh +kubectl delete -k config/samples/ +``` + +**Delete the APIs(CRDs) from the cluster:** + +```sh +make uninstall +``` + +**UnDeploy the controller from the cluster:** + +```sh +make undeploy +``` + + + +**NOTE:** Run `make help` for more information on all potential `make` targets + +More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)