Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .bingo/Variables.mk
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ $(KUBE_SCORE): $(BINGO_DIR)/kube-score.mod
@echo "(re)installing $(GOBIN)/kube-score-v1.20.0"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=kube-score.mod -o=$(GOBIN)/kube-score-v1.20.0 "github.com/zegl/kube-score/cmd/kube-score"

MOCKGEN := $(GOBIN)/mockgen-v0.6.0
$(MOCKGEN): $(BINGO_DIR)/mockgen.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
@echo "(re)installing $(GOBIN)/mockgen-v0.6.0"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=mockgen.mod -o=$(GOBIN)/mockgen-v0.6.0 "go.uber.org/mock/mockgen"

OPERATOR_SDK := $(GOBIN)/operator-sdk-v1.41.1
$(OPERATOR_SDK): $(BINGO_DIR)/operator-sdk.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
Expand Down
5 changes: 5 additions & 0 deletions .bingo/mockgen.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT

go 1.26.3

require go.uber.org/mock v0.6.0 // mockgen
8 changes: 8 additions & 0 deletions .bingo/mockgen.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
2 changes: 2 additions & 0 deletions .bingo/variables.env
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ KIND="${GOBIN}/kind-v0.32.0"

KUBE_SCORE="${GOBIN}/kube-score-v1.20.0"

MOCKGEN="${GOBIN}/mockgen-v0.6.0"

OPERATOR_SDK="${GOBIN}/operator-sdk-v1.41.1"

OPM="${GOBIN}/opm-v1.60.0"
Expand Down
15 changes: 13 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,15 @@ install and manage cluster extensions. The project follows a microservices archi
- `containers_image_openpgp` - required for image handling

**Tools (managed via .bingo/):**
- controller-gen, golangci-lint, goreleaser, helm, kind, kustomize, setup-envtest, operator-sdk
- controller-gen, golangci-lint, goreleaser, helm, kind, kustomize, mockgen, setup-envtest, operator-sdk

**Test Mocking:**
- **gomock** (`go.uber.org/mock`): Used for generated mock implementations. `//go:generate mockgen` directives
live in `internal/testutil/mock/generate.go` (external and internal exported interfaces) and at interface
definitions for internal unexported interfaces (source mode). Run `make generate-mocks` to regenerate.
All interface mocks should be gomock-generated. Test fakes that return preconfigured values without
interaction verification (e.g., `FakePuller`, `FakeCache` in `image/fakes.go`) are not mocks and stay
hand-written.

---

Expand Down Expand Up @@ -140,8 +148,11 @@ make manifests
# Update CRDs and reference docs (when Go-based API definitions change)
make update-crds crd-ref-docs

# Generate code (DeepCopy methods)
# Generate code (DeepCopy methods and mock implementations)
make generate

# Regenerate mock implementations only
make generate-mocks
```

---
Expand Down
25 changes: 17 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ endif
# bingo manages consistent tooling versions for things like kind, etc.
include .bingo/Variables.mk

# mockgen is installed by bingo with a versioned name (mockgen-v0.6.0).
# Redefine MOCKGEN to an unversioned symlink so that go generate can find it on PATH.
_MOCKGEN_BINGO := $(MOCKGEN)
MOCKGEN := $(ROOT_DIR)/bin/mockgen
$(MOCKGEN): $(_MOCKGEN_BINGO)
@mkdir -p $(dir $@)
@ln -sf $< $@

ifeq ($(origin KIND_CLUSTER_NAME), undefined)
KIND_CLUSTER_NAME := operator-controller
endif
Expand Down Expand Up @@ -152,10 +160,6 @@ lint-custom: custom-linter-build #EXHELP Call custom linter for the project
lint-api-diff: $(GOLANGCI_LINT) #HELP Validate API changes using kube-api-linter with diff-aware analysis
hack/api-lint-diff/run.sh

.PHONY: k8s-pin
k8s-pin: #EXHELP Pin k8s staging modules based on k8s.io/kubernetes version (in go.mod or from K8S_IO_K8S_VERSION env var) and run go mod tidy.
K8S_IO_K8S_VERSION='$(K8S_IO_K8S_VERSION)' go run hack/tools/k8smaintainer/main.go

.PHONY: tidy #HELP Run go mod tidy.
tidy:
go mod tidy
Expand Down Expand Up @@ -201,8 +205,12 @@ manifests: update-crds $(MANIFESTS) $(HELM) #EXHELP Generate OLMv1 manifests
$(HELM) template olmv1 helm/olmv1 --values helm/tilt.yaml $(addprefix --set ,$(HELM_SETTINGS)) > /dev/null
$(HELM) template olmv1 helm/olmv1 --set "options.openshift.enabled=true" > /dev/null

.PHONY: generate-mocks
generate-mocks: $(MOCKGEN) #EXHELP Generate mock implementations for testing.
PATH="$(ROOT_DIR)/bin:$$PATH" go generate ./...

.PHONY: generate
generate: $(CONTROLLER_GEN) #EXHELP Generate code containing DeepCopy, DeepCopyInto, DeepCopyObject, and ApplyConfiguration type implementations.
generate: $(CONTROLLER_GEN) generate-mocks #EXHELP Generate code containing DeepCopy, DeepCopyInto, DeepCopyObject, and ApplyConfiguration type implementations.
# Need to delete the files for them to be generated properly
@find api cmd hack internal -name "zz_generated.deepcopy.go" -not -path "*/vendor/*" -delete && rm -rf applyconfigurations
$(CONTROLLER_GEN) --load-build-tags=$(GO_BUILD_TAGS) applyconfiguration:headerFile="hack/boilerplate.go.txt" paths="./api/..."
Expand All @@ -213,7 +221,7 @@ generate: $(CONTROLLER_GEN) #EXHELP Generate code containing DeepCopy, DeepCopyI
done

.PHONY: verify
verify: k8s-pin kind-verify-versions fmt generate manifests update-tls-profiles crd-ref-docs update-registryv1-bundle-schema verify-bingo #HELP Verify all generated code is up-to-date. Runs k8s-pin instead of just tidy.
verify: tidy kind-verify-versions fmt generate manifests update-tls-profiles crd-ref-docs update-registryv1-bundle-schema verify-bingo #HELP Verify all generated code is up-to-date.
git diff --exit-code

.PHONY: verify-bingo
Expand Down Expand Up @@ -289,7 +297,8 @@ extension-developer-e2e: export CONTAINER_RUNTIME := $(CONTAINER_RUNTIME)
extension-developer-e2e: $(OPERATOR_SDK) #EXHELP Run extension create, upgrade and delete tests.
go test -count=1 -v ./test/extension-developer-e2e/...

UNIT_TEST_DIRS := $(shell go list ./... | grep -vE "/test/|/testutils") $(shell go list ./test/internal/...)
UNIT_TEST_DIRS := $(shell go list ./... | grep -vE "/test/|/testutils|/testutil/mock") $(shell go list ./test/internal/...)
COVERAGE_PKGS := $(shell go list ./... | grep -vE "/test/|/testutils|/testutil/mock" | paste -sd,)
COVERAGE_UNIT_DIR := $(ROOT_DIR)/coverage/unit

.PHONY: envtest-k8s-bins #HELP Uses setup-envtest to download and install the binaries required to run ENVTEST-test based locally at the project/bin directory.
Expand All @@ -303,7 +312,7 @@ test-unit: $(SETUP_ENVTEST) envtest-k8s-bins #HELP Run the unit tests
KUBEBUILDER_ASSETS="$(shell $(SETUP_ENVTEST) use -p path $(ENVTEST_VERSION) $(SETUP_ENVTEST_BIN_DIR_OVERRIDE))" \
CGO_ENABLED=1 go test \
-tags '$(GO_BUILD_TAGS)' \
-cover -coverprofile ${ROOT_DIR}/coverage/unit.out \
-cover -coverpkg=$(COVERAGE_PKGS) -coverprofile ${ROOT_DIR}/coverage/unit.out \
-count=1 -race -short \
$(UNIT_TEST_DIRS) \
-test.gocoverdir=$(COVERAGE_UNIT_DIR)
Expand Down
37 changes: 19 additions & 18 deletions api/v1/clusterextension_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ const (
// ClusterExtensionSpec defines the desired state of ClusterExtension
type ClusterExtensionSpec struct {
// namespace specifies a Kubernetes namespace.
// This is the namespace where the provided ServiceAccount must exist.
// It also designates the default namespace where namespace-scoped resources for the extension are applied to the cluster.
// It designates the default namespace where namespace-scoped resources for the extension are applied to the cluster.
// Some extensions may contain namespace-scoped resources to be applied in other namespaces.
// This namespace must exist.
//
Expand All @@ -67,14 +66,15 @@ type ClusterExtensionSpec struct {
// +required
Namespace string `json:"namespace"`

// serviceAccount specifies a ServiceAccount used to perform all interactions with the cluster
// that are required to manage the extension.
// The ServiceAccount must be configured with the necessary permissions to perform these interactions.
// The ServiceAccount must exist in the namespace referenced in the spec.
// The serviceAccount field is required.
// serviceAccount is a deprecated field and is completely ignored.
// OLMv1 is a single-tenant system where users with ClusterExtension write access are
// effectively delegated cluster-admin trust. The operator-controller runs with
// cluster-admin privileges and uses its own service account for all cluster interactions.
//
// +required
ServiceAccount ServiceAccountReference `json:"serviceAccount"`
// Deprecated: serviceAccount is no longer used and will be removed in a future release.
//
// +optional
ServiceAccount ServiceAccountReference `json:"serviceAccount,omitzero"` //nolint:kubeapilinter // deprecated field uses omitzero per OpenShift convention; pointer would be a breaking API change

// source is required and selects the installation source of content for this ClusterExtension.
// Set the sourceType field to perform the selection.
Expand Down Expand Up @@ -150,7 +150,7 @@ type SourceConfig struct {
}

// ClusterExtensionInstallConfig is a union which selects the clusterExtension installation config.
// ClusterExtensionInstallConfig requires the namespace and serviceAccount which should be used for the installation of packages.
// ClusterExtensionInstallConfig requires the namespace which should be used for the installation of packages.
//
// +kubebuilder:validation:XValidation:rule="has(self.preflight)",message="at least one of [preflight] are required when install is specified"
// +union
Expand Down Expand Up @@ -378,12 +378,13 @@ type CatalogFilter struct {
UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"`
}

// ServiceAccountReference identifies the serviceAccount used fo install a ClusterExtension.
// ServiceAccountReference is a deprecated type and is completely ignored.
//
// Deprecated: ServiceAccountReference is no longer used and will be removed in a future release.
type ServiceAccountReference struct {
// name is a required, immutable reference to the name of the ServiceAccount used for installation
// and management of the content for the package specified in the packageName field.
// name is a deprecated field and is completely ignored.
//
// This ServiceAccount must exist in the installNamespace.
// Deprecated: name is no longer used and will be removed in a future release.
//
// The name field follows the DNS subdomain standard as defined in [RFC 1123].
// It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.),
Expand All @@ -403,10 +404,10 @@ type ServiceAccountReference struct {
// [RFC 1123]: https://tools.ietf.org/html/rfc1123
//
// +kubebuilder:validation:MaxLength:=253
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable"
// +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="name must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters"
// +required
Name string `json:"name"`
// +kubebuilder:validation:XValidation:rule="self == oldSelf || size(self) == 0",message="name is immutable once set, but may be cleared"
// +kubebuilder:validation:XValidation:rule="size(self) == 0 || self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="name must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters"
// +optional
Name string `json:"name"` //nolint:kubeapilinter // deprecated field; empty string means unset, pointer would be a breaking API change
}

// PreflightConfig holds the configuration for the preflight checks. If used, at least one preflight check must be non-nil.
Expand Down
3 changes: 0 additions & 3 deletions api/v1/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ func TestValidate(t *testing.T) {
}
defaultExtensionSpec := func(s *ClusterExtensionSpec) *ClusterExtensionSpec {
s.Namespace = "ns"
s.ServiceAccount = ServiceAccountReference{
Name: "sa",
}
s.Source = SourceConfig{
SourceType: SourceTypeCatalog,
Catalog: &CatalogFilter{
Expand Down
4 changes: 2 additions & 2 deletions applyconfigurations/api/v1/clusterextensioninstallconfig.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions applyconfigurations/api/v1/clusterextensionspec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions applyconfigurations/api/v1/serviceaccountreference.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading