···33# To re-generate a bundle for another specific version without changing the standard setup, you can:
44# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
55# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
66-VERSION ?= 0.0.1
66+VERSION ?= 0.5.22
7788# CHANNELS define the bundle channels used in the bundle.
99# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
···9090help: ## Display this help.
9191 @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\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)
92929393+##@ Version Management
9494+9595+.PHONY: version-show
9696+version-show: ## Show current version
9797+ @echo "Current version: $(VERSION)"
9898+9999+.PHONY: version-sync
100100+version-sync: ## Sync Chart.yaml versions with Makefile VERSION
101101+ @echo "Syncing Chart.yaml with VERSION=$(VERSION)..."
102102+ @sed -i 's/^version: .*/version: $(VERSION)/' helm/hsm-secrets-operator/Chart.yaml
103103+ @sed -i 's/^appVersion: .*/appVersion: v$(VERSION)/' helm/hsm-secrets-operator/Chart.yaml
104104+ @echo "✅ Chart.yaml synced with version $(VERSION)"
105105+106106+.PHONY: version-patch
107107+version-patch: ## Increment patch version (x.y.Z+1)
108108+ $(call increment-version,patch)
109109+110110+.PHONY: version-minor
111111+version-minor: ## Increment minor version (x.Y+1.0)
112112+ $(call increment-version,minor)
113113+114114+.PHONY: version-major
115115+version-major: ## Increment major version (X+1.0.0)
116116+ $(call increment-version,major)
117117+118118+# Helper function to increment version
119119+define increment-version
120120+$(eval NEW_VERSION := $(shell echo "$(VERSION)" | awk -F. -v component=$(1) '{ \
121121+ if (component == "major") { print ($$1+1) ".0.0" } \
122122+ else if (component == "minor") { print $$1 "." ($$2+1) ".0" } \
123123+ else if (component == "patch") { print $$1 "." $$2 "." ($$3+1) } \
124124+}'))
125125+@echo "Incrementing $(1): $(VERSION) → $(NEW_VERSION)"
126126+@sed -i 's/^VERSION ?= .*/VERSION ?= $(NEW_VERSION)/' Makefile
127127+@$(MAKE) version-sync VERSION=$(NEW_VERSION)
128128+@echo "✅ Version updated to $(NEW_VERSION)"
129129+@echo ""
130130+@echo "Next steps:"
131131+@echo "1. git add Makefile helm/hsm-secrets-operator/Chart.yaml"
132132+@echo "2. git commit -m 'chore: bump version to $(NEW_VERSION)'"
133133+@echo "3. git tag v$(NEW_VERSION) && git push --tags"
134134+endef
135135+93136##@ Development
9413795138.PHONY: manifests
···116159117160.PHONY: test
118161test: manifests generate fmt vet setup-envtest ## Run tests.
119119- KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
162162+ KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v -E '/(e2e|test/utils)') -coverprofile cover.out
120163121164# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
122165# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
···11+/*
22+Copyright 2025.
33+44+Licensed under the Apache License, Version 2.0 (the "License");
55+you may not use this file except in compliance with the License.
66+You may obtain a copy of the License at
77+88+ http://www.apache.org/licenses/LICENSE-2.0
99+1010+Unless required by applicable law or agreed to in writing, software
1111+distributed under the License is distributed on an "AS IS" BASIS,
1212+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313+See the License for the specific language governing permissions and
1414+limitations under the License.
1515+*/
1616+1717+package api
1818+1919+import (
2020+ "context"
2121+2222+ "github.com/go-logr/logr"
2323+ "github.com/stretchr/testify/mock"
2424+2525+ "github.com/evanjarrett/hsm-secrets-operator/internal/hsm"
2626+)
2727+2828+// MockAgentManager provides a mock implementation of the agent manager interface
2929+type MockAgentManager struct {
3030+ mock.Mock
3131+}
3232+3333+func (m *MockAgentManager) GetAvailableDevices(ctx context.Context, namespace string) ([]string, error) {
3434+ args := m.Called(ctx, namespace)
3535+ return args.Get(0).([]string), args.Error(1)
3636+}
3737+3838+func (m *MockAgentManager) CreateGRPCClient(ctx context.Context, deviceName, namespace string, logger logr.Logger) (hsm.Client, error) {
3939+ args := m.Called(ctx, deviceName, namespace, logger)
4040+ if args.Get(0) == nil {
4141+ return nil, args.Error(1)
4242+ }
4343+ return args.Get(0).(hsm.Client), args.Error(1)
4444+}
4545+4646+// MockHSMClient provides a mock implementation of the HSM client interface
4747+type MockHSMClient struct {
4848+ mock.Mock
4949+}
5050+5151+func (m *MockHSMClient) IsConnected() bool {
5252+ args := m.Called()
5353+ return args.Bool(0)
5454+}
5555+5656+func (m *MockHSMClient) GetInfo(ctx context.Context) (*hsm.HSMInfo, error) {
5757+ args := m.Called(ctx)
5858+ if args.Get(0) == nil {
5959+ return nil, args.Error(1)
6060+ }
6161+ return args.Get(0).(*hsm.HSMInfo), args.Error(1)
6262+}
6363+6464+func (m *MockHSMClient) ListSecrets(ctx context.Context, prefix string) ([]string, error) {
6565+ args := m.Called(ctx, prefix)
6666+ return args.Get(0).([]string), args.Error(1)
6767+}
6868+6969+func (m *MockHSMClient) ReadSecret(ctx context.Context, path string) (hsm.SecretData, error) {
7070+ args := m.Called(ctx, path)
7171+ if args.Get(0) == nil {
7272+ return nil, args.Error(1)
7373+ }
7474+ return args.Get(0).(hsm.SecretData), args.Error(1)
7575+}
7676+7777+func (m *MockHSMClient) WriteSecret(ctx context.Context, path string, data hsm.SecretData) error {
7878+ args := m.Called(ctx, path, data)
7979+ return args.Error(0)
8080+}
8181+8282+func (m *MockHSMClient) WriteSecretWithMetadata(ctx context.Context, path string, data hsm.SecretData, metadata *hsm.SecretMetadata) error {
8383+ args := m.Called(ctx, path, data, metadata)
8484+ return args.Error(0)
8585+}
8686+8787+func (m *MockHSMClient) DeleteSecret(ctx context.Context, path string) error {
8888+ args := m.Called(ctx, path)
8989+ return args.Error(0)
9090+}
9191+9292+func (m *MockHSMClient) ReadMetadata(ctx context.Context, path string) (*hsm.SecretMetadata, error) {
9393+ args := m.Called(ctx, path)
9494+ if args.Get(0) == nil {
9595+ return nil, args.Error(1)
9696+ }
9797+ return args.Get(0).(*hsm.SecretMetadata), args.Error(1)
9898+}
9999+100100+func (m *MockHSMClient) GetChecksum(ctx context.Context, path string) (string, error) {
101101+ args := m.Called(ctx, path)
102102+ return args.String(0), args.Error(1)
103103+}
104104+105105+func (m *MockHSMClient) Initialize(ctx context.Context, config hsm.Config) error {
106106+ args := m.Called(ctx, config)
107107+ return args.Error(0)
108108+}
109109+110110+func (m *MockHSMClient) Close() error {
111111+ args := m.Called()
112112+ return args.Error(0)
113113+}
+4-2
internal/api/proxy_client.go
···120120 }
121121122122 // Find the most common checksum (consensus)
123123+ // In case of ties, prefer the first occurrence for deterministic behavior
123124 var consensusChecksum string
124125 var maxCount int
125125- for checksum, count := range checksumCounts {
126126+ for _, result := range results {
127127+ count := checksumCounts[result.checksum]
126128 if count > maxCount {
127127- consensusChecksum = checksum
129129+ consensusChecksum = result.checksum
128130 maxCount = count
129131 }
130132 }