diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a5870d1a..8fb713fb 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -343,9 +343,9 @@ jobs: outputs: # JSON encoded array of k8s versions K8S_VERSIONS: '["1.30.0", "1.29.4", "1.28.9", "1.27.13", "1.26.15"]' - VAULT_N: "1.16.3" - VAULT_N_1: "1.15.9" - VAULT_N_2: "1.14.13" + VAULT_N: "1.17.2" + VAULT_N_1: "1.16.6" + VAULT_N_2: "1.15.12" latest-vault: name: vault:${{ matrix.vault-version }} kind:${{ matrix.k8s-version }} ${{ matrix.installation-method }} enterprise=${{ matrix.vault-enterprise }} needs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bc28228..97da99bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,59 @@ +## 0.8.0 (July 18th, 2024) +**Important** + +* Helm: CRD schema changes are now automatically applied at upgrade time. + + *See [updating-crds](https://developer.hashicorp.com/vault/docs/platform/k8s/vso/installation#updating-crds-when-using-helm) for more details.* + +* This release contains CRD schema changes which remove the field validation on most VaultAuth spec fields. That means invalid VaultAuth + configurations will no longer be handled at resource application time. Please review the VSO logs and K8s + events when troubleshooting Vault authentication issues. + +Features: +* Helm: add support for auto upgrading CRDs: [GH-789](https://github.com/hashicorp/vault-secrets-operator/pull/789) +* VaultStaticSecret: support [instant event-driven updates](https://developer.hashicorp.com/vault/docs/platform/k8s/vso/sources/vault#instant-updates): [GH-771](https://github.com/hashicorp/vault-secrets-operator/pull/771) +* Add new [VaultAuthGlobal](https://developer.hashicorp.com/vault/docs/platform/k8s/vso/sources/vault#vaultauthglobal-custom-resource) type for shared VaultAuth configurations: + [GH-735](https://github.com/hashicorp/vault-secrets-operator/pull/735) + [GH-800](https://github.com/hashicorp/vault-secrets-operator/pull/800) + [GH-847](https://github.com/hashicorp/vault-secrets-operator/pull/847) + [GH-855](https://github.com/hashicorp/vault-secrets-operator/pull/855) + [GH-850](https://github.com/hashicorp/vault-secrets-operator/pull/850) +* CachingClientFactory: support client taints to trigger Vault client token validation: + [GH-717](https://github.com/hashicorp/vault-secrets-operator/pull/717) + [GH-769](https://github.com/hashicorp/vault-secrets-operator/pull/769) + +Improvements: +* VPS: add ca.crt from issuing CA for tls secret type: [GH-848](https://github.com/hashicorp/vault-secrets-operator/pull/848) +* Helm: support setting VaultAuthGlobalRef on VaultAuth: [GH-851](https://github.com/hashicorp/vault-secrets-operator/pull/851) +* Migrate to k8s.io/utils/ptr: [GH-856](https://github.com/hashicorp/vault-secrets-operator/pull/856) +* Core: update backoff option docs: [GH-801](https://github.com/hashicorp/vault-secrets-operator/pull/801) + +Fix: +* VaultAuth: set valid status on VaultAuthGlobal deref error: [GH-854](https://github.com/hashicorp/vault-secrets-operator/pull/854) +* VDS: properly handle the clone cache key variant during client callback execution: [GH-835](https://github.com/hashicorp/vault-secrets-operator/pull/835) +* Core: delete resource status metrics upon object deletion: [GH-815](https://github.com/hashicorp/vault-secrets-operator/pull/815) +* VSS: use a constant backoff on some reconciliation errors: [GH-811](https://github.com/hashicorp/vault-secrets-operator/pull/811) +* VDS: work around Vault DB static creds TTL rollover bug: [GH-730](https://github.com/hashicorp/vault-secrets-operator/pull/730) + +Build: +* CI: bump Vault versions: [GH-797](https://github.com/hashicorp/vault-secrets-operator/pull/797) + +Dependency Updates: +* Bump cloud.google.com/go/compute/metadata from 0.4.0 to 0.5.0: [GH-853](https://github.com/hashicorp/vault-secrets-operator/pull/853) +* Bump github.com/gruntwork-io/terratest from 0.46.16 to 0.47.0: [GH-852](https://github.com/hashicorp/vault-secrets-operator/pull/852) +* Bump github.com/hashicorp/go-getter from 1.7.4 to 1.7.5: [GH-834](https://github.com/hashicorp/vault-secrets-operator/pull/834) +* Bump github.com/hashicorp/go-retryablehttp from 0.7.1 to 0.7.7: [GH-833](https://github.com/hashicorp/vault-secrets-operator/pull/833) +* Bump github.com/hashicorp/go-version from 1.6.0 to 1.7.0: [GH-810](https://github.com/hashicorp/vault-secrets-operator/pull/810) +* Bump golang.org/x/crypto from 0.24.0 to 0.25.0: [GH-843](https://github.com/hashicorp/vault-secrets-operator/pull/843) +* Bump google.golang.org/api from 0.186.0 to 0.188.0: [GH-846](https://github.com/hashicorp/vault-secrets-operator/pull/846) +* Bump google.golang.org/grpc from 1.64.0 to 1.64.1: [GH-845](https://github.com/hashicorp/vault-secrets-operator/pull/845) +* Bump k8s.io/api from 0.30.1 to 0.30.2: [GH-822](https://github.com/hashicorp/vault-secrets-operator/pull/822) +* Bump k8s.io/apiextensions-apiserver from 0.30.1 to 0.30.2: [GH-828](https://github.com/hashicorp/vault-secrets-operator/pull/828) +* Bump k8s.io/client-go from 0.30.1 to 0.30.2: [GH-830](https://github.com/hashicorp/vault-secrets-operator/pull/830) +* Bump sigs.k8s.io/controller-runtime from 0.18.3 to 0.18.4: [GH-808](https://github.com/hashicorp/vault-secrets-operator/pull/808) +* Bump ubi9/ubi-micro from 9.4-6.1716471860 to 9.4-9: [GH-819](https://github.com/hashicorp/vault-secrets-operator/pull/819) +* Bump ubi9/ubi-minimal from 9.4-949.1717074713 to 9.4-1134: [GH-820](https://github.com/hashicorp/vault-secrets-operator/pull/820) + ## 0.7.1 (May 30th, 2024) Fix: @@ -31,7 +87,7 @@ Fix: * VDS: Selectively log calls to SyncRegistry.Delete(): [GH-718](https://github.com/hashicorp/vault-secrets-operator/pull/718) Build: -* CI: test against vault-1.16.2: [GH-715](https://github.com/hashicorp/vault-secrets-operator/pull/715) +* CI: Bump test vault versions: [GH-861](https://github.com/hashicorp/vault-secrets-operator/pull/861) * Bump GH actions for node 16 obsolescence: [GH-738](https://github.com/hashicorp/vault-secrets-operator/pull/738) Dependency Updates: diff --git a/api/v1beta1/hcpauth_types.go b/api/v1beta1/hcpauth_types.go index eac66b14..b2d671fe 100644 --- a/api/v1beta1/hcpauth_types.go +++ b/api/v1beta1/hcpauth_types.go @@ -49,12 +49,12 @@ type HCPAuthServicePrincipal struct { // HCPAuthStatus defines the observed state of HCPAuth type HCPAuthStatus struct { // Valid auth mechanism. - Valid bool `json:"valid"` + Valid *bool `json:"valid"` Error string `json:"error"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // HCPAuth is the Schema for the hcpauths API type HCPAuth struct { @@ -65,7 +65,7 @@ type HCPAuth struct { Status HCPAuthStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // HCPAuthList contains a list of HCPAuth type HCPAuthList struct { diff --git a/api/v1beta1/secrettransformation_types.go b/api/v1beta1/secrettransformation_types.go index c92e060c..01a68f28 100644 --- a/api/v1beta1/secrettransformation_types.go +++ b/api/v1beta1/secrettransformation_types.go @@ -9,12 +9,12 @@ import ( // SecretTransformationStatus defines the observed state of SecretTransformation type SecretTransformationStatus struct { - Valid bool `json:"valid"` + Valid *bool `json:"valid"` Error string `json:"error"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // SecretTransformation is the Schema for the secrettransformations API type SecretTransformation struct { @@ -55,7 +55,7 @@ type SourceTemplate struct { Text string `json:"text"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // SecretTransformationList contains a list of SecretTransformation type SecretTransformationList struct { diff --git a/api/v1beta1/vaultauth_types.go b/api/v1beta1/vaultauth_types.go index 10025b72..697a3f82 100644 --- a/api/v1beta1/vaultauth_types.go +++ b/api/v1beta1/vaultauth_types.go @@ -333,11 +333,12 @@ type VaultAuthGlobalRef struct { // set on the operator's '-global-vault-auth-options' flag // // The default VaultAuthGlobal search is conditional. - // When a ref Namespace is not set, the search follows the order: - // 1. The referring VaultAuth Namespace. - // 2. The Operator's namespace. - // Otherwise, the search follows the order: - // 1. The VaultAuthGlobal ref Namespace. + // When a ref Namespace is set, the search for the default + // VaultAuthGlobal resource is constrained to that namespace. + // Otherwise, the search order is: + // 1. The default VaultAuthGlobal resource in the referring VaultAuth resource's + // namespace. + // 2. The default VaultAuthGlobal resource in the Operator's namespace. AllowDefault *bool `json:"allowDefault,omitempty"` } @@ -428,7 +429,7 @@ type VaultAuthSpec struct { // VaultAuthStatus defines the observed state of VaultAuth type VaultAuthStatus struct { // Valid auth mechanism. - Valid bool `json:"valid,omitempty"` + Valid *bool `json:"valid,omitempty"` Error string `json:"error,omitempty"` Conditions []metav1.Condition `json:"conditions,omitempty"` SpecHash string `json:"specHash,omitempty"` diff --git a/api/v1beta1/vaultconnection_types.go b/api/v1beta1/vaultconnection_types.go index 7a280299..c7147b42 100644 --- a/api/v1beta1/vaultconnection_types.go +++ b/api/v1beta1/vaultconnection_types.go @@ -28,11 +28,11 @@ type VaultConnectionSpec struct { // VaultConnectionStatus defines the observed state of VaultConnection type VaultConnectionStatus struct { // Valid auth mechanism. - Valid bool `json:"valid"` + Valid *bool `json:"valid"` } -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status // VaultConnection is the Schema for the vaultconnections API type VaultConnection struct { @@ -43,7 +43,7 @@ type VaultConnection struct { Status VaultConnectionStatus `json:"status,omitempty"` } -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true // VaultConnectionList contains a list of VaultConnection type VaultConnectionList struct { diff --git a/api/v1beta1/vaultpkisecret_types.go b/api/v1beta1/vaultpkisecret_types.go index 9bf9c92e..736e4868 100644 --- a/api/v1beta1/vaultpkisecret_types.go +++ b/api/v1beta1/vaultpkisecret_types.go @@ -135,7 +135,7 @@ type VaultPKISecretStatus struct { // The SecretMac is also used to detect drift in the Destination Secret's Data. // If drift is detected the data will be synced to the Destination. SecretMAC string `json:"secretMAC,omitempty"` - Valid bool `json:"valid"` + Valid *bool `json:"valid"` Error string `json:"error"` } diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 8927df43..35cbc123 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -48,7 +48,7 @@ func (in *HCPAuth) DeepCopyInto(out *HCPAuth) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HCPAuth. @@ -144,6 +144,11 @@ func (in *HCPAuthSpec) DeepCopy() *HCPAuthSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HCPAuthStatus) DeepCopyInto(out *HCPAuthStatus) { *out = *in + if in.Valid != nil { + in, out := &in.Valid, &out.Valid + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HCPAuthStatus. @@ -287,7 +292,7 @@ func (in *SecretTransformation) DeepCopyInto(out *SecretTransformation) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretTransformation. @@ -380,6 +385,11 @@ func (in *SecretTransformationSpec) DeepCopy() *SecretTransformationSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecretTransformationStatus) DeepCopyInto(out *SecretTransformationStatus) { *out = *in + if in.Valid != nil { + in, out := &in.Valid, &out.Valid + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretTransformationStatus. @@ -1050,6 +1060,11 @@ func (in *VaultAuthSpec) DeepCopy() *VaultAuthSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VaultAuthStatus) DeepCopyInto(out *VaultAuthStatus) { *out = *in + if in.Valid != nil { + in, out := &in.Valid, &out.Valid + *out = new(bool) + **out = **in + } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make([]v1.Condition, len(*in)) @@ -1090,7 +1105,7 @@ func (in *VaultConnection) DeepCopyInto(out *VaultConnection) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultConnection. @@ -1168,6 +1183,11 @@ func (in *VaultConnectionSpec) DeepCopy() *VaultConnectionSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VaultConnectionStatus) DeepCopyInto(out *VaultConnectionStatus) { *out = *in + if in.Valid != nil { + in, out := &in.Valid, &out.Valid + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultConnectionStatus. @@ -1291,7 +1311,7 @@ func (in *VaultPKISecret) DeepCopyInto(out *VaultPKISecret) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultPKISecret. @@ -1393,6 +1413,11 @@ func (in *VaultPKISecretSpec) DeepCopy() *VaultPKISecretSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VaultPKISecretStatus) DeepCopyInto(out *VaultPKISecretStatus) { *out = *in + if in.Valid != nil { + in, out := &in.Valid, &out.Valid + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultPKISecretStatus. diff --git a/chart/Chart.yaml b/chart/Chart.yaml index 20b62ea4..387dce62 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -3,8 +3,8 @@ apiVersion: v2 name: vault-secrets-operator -version: 0.7.1 -appVersion: "0.7.1" +version: 0.8.0 +appVersion: "0.8.0" kubeVersion: ">=1.21.0-0" description: Official Vault Secrets Operator Chart type: application diff --git a/chart/crds/secrets.hashicorp.com_vaultauths.yaml b/chart/crds/secrets.hashicorp.com_vaultauths.yaml index 60226694..60d5ebb6 100644 --- a/chart/crds/secrets.hashicorp.com_vaultauths.yaml +++ b/chart/crds/secrets.hashicorp.com_vaultauths.yaml @@ -254,11 +254,12 @@ spec: The default VaultAuthGlobal search is conditional. - When a ref Namespace is not set, the search follows the order: - 1. The referring VaultAuth Namespace. - 2. The Operator's namespace. - Otherwise, the search follows the order: - 1. The VaultAuthGlobal ref Namespace. + When a ref Namespace is set, the search for the default + VaultAuthGlobal resource is constrained to that namespace. + Otherwise, the search order is: + 1. The default VaultAuthGlobal resource in the referring VaultAuth resource's + namespace. + 2. The default VaultAuthGlobal resource in the Operator's namespace. type: boolean mergeStrategy: description: |- diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl index c540985d..11efbce8 100644 --- a/chart/templates/_helpers.tpl +++ b/chart/templates/_helpers.tpl @@ -298,3 +298,39 @@ logging args {{- $ret | toYaml | nindent 8 -}} {{- end -}} {{- end -}} + +{{/* +vaultAuthGlobalRef generates the global-vault-auth-global-ref flag for the manager. +*/}} +{{- define "vso.vaulAuthGlobalRef" -}} +{{- if .Values.controller.manager.globalVaultAuthOptions.allowDefaultGlobals }} +--global-vault-auth-global-ref +{{- end -}} +{{- end -}} + +{{/* +vaultAuthGlobalRef generates the default VaultAuth spec.vaultAuthGlobalRef. +*/}} +{{- define "vso.vaultAuthGlobalRef" -}} +{{- $ret := dict -}} +{{- with .Values.defaultAuthMethod.vaultAuthGlobalRef -}} +{{ $_ := set $ret "namespace" .namespace -}} +{{ $_ = set $ret "name" .name -}} +{{ if ne .allowDefault nil -}} +{{- $_ = set $ret "allowDefault" .allowDefault -}} +{{- end -}} +{{- $strat := dict -}} +{{- if .mergeStrategy.headers -}} +{{- $_ = set $strat "headers" .mergeStrategy.headers -}} +{{- end -}} +{{- if .mergeStrategy.params -}} +{{- $_ = set $strat "params" .mergeStrategy.params -}} +{{- end -}} +{{- if $strat -}} +{{- $_ = set $ret "mergeStrategy" $strat -}} +{{- end -}} +{{- end -}} +{{- if $ret -}} +{{- $ret | toYaml | nindent 4 -}} +{{- end -}} +{{- end -}} diff --git a/chart/templates/default-vault-auth-method.yaml b/chart/templates/default-vault-auth-method.yaml index 8cd264fd..2c223297 100644 --- a/chart/templates/default-vault-auth-method.yaml +++ b/chart/templates/default-vault-auth-method.yaml @@ -24,4 +24,8 @@ spec: mount: {{ .Values.defaultAuthMethod.mount }} {{- $kubeServiceAccount := .Values.defaultAuthMethod.kubernetes.serviceAccount }} {{- include "vso.vaultAuthMethod" (list .Values.defaultAuthMethod $kubeServiceAccount . ) }} + {{- if .Values.defaultAuthMethod.vaultAuthGlobalRef.enabled }} + vaultAuthGlobalRef: + {{- include "vso.vaultAuthGlobalRef" . }} + {{- end }} {{- end }} diff --git a/chart/values.yaml b/chart/values.yaml index 75788e79..90ec3a6d 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -161,7 +161,7 @@ controller: image: pullPolicy: IfNotPresent repository: hashicorp/vault-secrets-operator - tag: 0.7.1 + tag: 0.8.0 # logging logging: @@ -746,6 +746,40 @@ defaultAuthMethod: # @type: map headers: {} + # VaultAuthGlobalRef + vaultAuthGlobalRef: + # toggles the inclusion of the VaultAuthGlobal configuration in the + # default VaultAuth CR. + # @type: boolean + enabled: false + # Name of the VaultAuthGlobal CR to reference. + # @type: string + name: "" + + # Namespace of the VaultAuthGlobal CR to reference. + # @type: string + namespace: "" + + # allow default globals + # @type: boolean + allowDefault: + + mergeStrategy: + # merge strategy for headers + # @type: string + # Valid values are: "replace", "merge", "none" + # Default: "replace" + # @type: string + headers: none + + # merge strategy for params + # @type: string + # Valid values are: "replace", "merge", "none" + # Default: "replace" + # @type: string + params: none + + # Configures a Prometheus ServiceMonitor telemetry: serviceMonitor: diff --git a/config/crd/bases/secrets.hashicorp.com_vaultauths.yaml b/config/crd/bases/secrets.hashicorp.com_vaultauths.yaml index 60226694..60d5ebb6 100644 --- a/config/crd/bases/secrets.hashicorp.com_vaultauths.yaml +++ b/config/crd/bases/secrets.hashicorp.com_vaultauths.yaml @@ -254,11 +254,12 @@ spec: The default VaultAuthGlobal search is conditional. - When a ref Namespace is not set, the search follows the order: - 1. The referring VaultAuth Namespace. - 2. The Operator's namespace. - Otherwise, the search follows the order: - 1. The VaultAuthGlobal ref Namespace. + When a ref Namespace is set, the search for the default + VaultAuthGlobal resource is constrained to that namespace. + Otherwise, the search order is: + 1. The default VaultAuthGlobal resource in the referring VaultAuth resource's + namespace. + 2. The default VaultAuthGlobal resource in the Operator's namespace. type: boolean mergeStrategy: description: |- diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 6df89fc6..c3033a83 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -16,4 +16,4 @@ kind: Kustomization images: - name: controller newName: hashicorp/vault-secrets-operator - newTag: 0.7.1 + newTag: 0.8.0 diff --git a/controllers/common.go b/controllers/common.go index d4f99f16..07228fdf 100644 --- a/controllers/common.go +++ b/controllers/common.go @@ -10,6 +10,7 @@ import ( "time" "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" @@ -270,3 +271,48 @@ func waitForStoppedCh(ctx context.Context, stoppedCh chan struct{}) error { return ctx.Err() } } + +// updateConditions updates the current conditions with updates, returning a new +// set of metav1.Condition(s). It will update the LastTransitionTime if the condition has +// changed. It will also append new conditions to the existing conditions. All +// updates are deduplicated based on their type and reason. +func updateConditions(current []metav1.Condition, updates ...metav1.Condition) []metav1.Condition { + seen := make(map[string]bool) + var ret []metav1.Condition + for _, newCond := range updates { + // we key conditions on their type and reason + // e.g: type=VaultAuthGlobal reason=Available, ... + key := fmt.Sprintf("%s/%s", newCond.Type, newCond.Reason) + if seen[key] { + // drop duplicate conditions + continue + } + + seen[key] = true + var updated bool + for _, cond := range current { + if cond.Type == newCond.Type && cond.Reason == newCond.Reason { + if cond.Status != newCond.Status { + newCond.LastTransitionTime = metav1.NewTime(nowFunc()) + } + if newCond.LastTransitionTime.IsZero() { + if cond.LastTransitionTime.IsZero() { + newCond.LastTransitionTime = metav1.NewTime(nowFunc()) + } else { + newCond.LastTransitionTime = cond.LastTransitionTime + } + } + ret = append(ret, newCond) + updated = true + break + } + } + if !updated { + if newCond.LastTransitionTime.IsZero() { + newCond.LastTransitionTime = metav1.NewTime(nowFunc()) + } + ret = append(ret, newCond) + } + } + return ret +} diff --git a/controllers/common_test.go b/controllers/common_test.go index e174a3c5..b3b7b656 100644 --- a/controllers/common_test.go +++ b/controllers/common_test.go @@ -398,3 +398,164 @@ func Test_waitForStoppedCh(t *testing.T) { err = waitForStoppedCh(ctx2, stoppedCh) assert.NoError(t, err) } + +func TestVaultAuthReconciler_updateConditions(t *testing.T) { + t0 := nowFunc().Truncate(time.Second) + tests := []struct { + name string + current []metav1.Condition + updates []metav1.Condition + expectLen int + }{ + { + name: "no-conditions", + }, + { + name: "initial-conditions", + updates: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + }, + }, + expectLen: 1, + }, + { + name: "unchanged-conditions", + current: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.NewTime(t0), + }, + }, + updates: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + }, + }, + expectLen: 1, + }, + { + name: "unchanged-conditions-no-last-transition-time", + current: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + }, + }, + updates: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + }, + }, + expectLen: 1, + }, + { + name: "unchanged-conditions-with-duplicate-updates", + current: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.NewTime(t0), + }, + }, + updates: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.NewTime(t0), + }, + { + Type: "Available", + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.NewTime(t0), + }, + }, + expectLen: 1, + }, + { + name: "condition-status-transition", + current: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionFalse, + LastTransitionTime: metav1.NewTime(nowFunc().Add(-time.Minute)), + }, + { + Type: "Progressing", + Status: metav1.ConditionTrue, + LastTransitionTime: metav1.NewTime(nowFunc().Add(-time.Minute)), + }, + }, + updates: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + }, + { + Type: "Progressing", + Status: metav1.ConditionFalse, + }, + }, + expectLen: 2, + }, + { + name: "condition-status-transition-appending-new-condition", + current: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionFalse, + LastTransitionTime: metav1.NewTime(nowFunc().Add(-time.Minute)), + }, + }, + updates: []metav1.Condition{ + { + Type: "Available", + Status: metav1.ConditionTrue, + }, + { + Type: "Progressing", + Status: metav1.ConditionFalse, + }, + }, + expectLen: 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + origUpdates := make([]metav1.Condition, len(tt.updates)) + seen := make(map[string]bool) + for idx, cond := range tt.updates { + key := fmt.Sprintf("%s/%s", cond.Type, cond.Reason) + if seen[key] { + continue + } + seen[key] = true + origUpdates[idx] = cond + } + + got := updateConditions(tt.current, tt.updates...) + assert.Equalf(t, tt.expectLen, len(got), + "expected %d conditions, got %d", tt.expectLen, len(got)) + for _, orig := range origUpdates { + for _, cond := range got { + if cond.Reason == orig.Reason && orig.Status == cond.Status { + if orig.LastTransitionTime.IsZero() { + assert.False(t, cond.LastTransitionTime.IsZero()) + } else { + assert.Equal(t, orig, cond) + } + } else if cond.Reason == orig.Reason && orig.Status != cond.Status { + assert.Greater(t, cond.LastTransitionTime.Time.Unix(), orig.LastTransitionTime.Time.Unix()) + } else { + // not updated + assert.False(t, orig.LastTransitionTime.IsZero()) + } + } + } + }) + } +} diff --git a/controllers/hcpauth_controller.go b/controllers/hcpauth_controller.go index 96db87ad..2040bdc3 100644 --- a/controllers/hcpauth_controller.go +++ b/controllers/hcpauth_controller.go @@ -13,6 +13,7 @@ import ( hvsclient "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-06-13/client" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -27,11 +28,11 @@ type HCPAuthReconciler struct { Scheme *runtime.Scheme } -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=hcpauths,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=hcpauths/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=hcpauths/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch -//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=hcpauths,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=hcpauths/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=hcpauths/finalizers,verbs=update +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch // Reconcile validates the HCPAuth CR instance, updating the instance's status // with the result. @@ -69,10 +70,10 @@ func (r *HCPAuthReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct logger.Error(err, "Validation failed") errs = errors.Join(err) o.Status.Error = err.Error() - o.Status.Valid = false + o.Status.Valid = ptr.To(true) } else { o.Status.Error = "" - o.Status.Valid = true + o.Status.Valid = ptr.To(true) } if err := r.updateStatus(ctx, o); err != nil { @@ -91,7 +92,7 @@ func (r *HCPAuthReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct func (r *HCPAuthReconciler) updateStatus(ctx context.Context, a *secretsv1beta1.HCPAuth) error { logger := log.FromContext(ctx) - metrics.SetResourceStatus("hcpauth", a, a.Status.Valid) + metrics.SetResourceStatus("hcpauth", a, ptr.Deref(a.Status.Valid, false)) if err := r.Status().Update(ctx, a); err != nil { logger.Error(err, "Failed to update the resource's status") return err diff --git a/controllers/secrettransformation_controller.go b/controllers/secrettransformation_controller.go index 9f7e7596..7748a4ea 100644 --- a/controllers/secrettransformation_controller.go +++ b/controllers/secrettransformation_controller.go @@ -10,6 +10,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -28,10 +29,10 @@ type SecretTransformationReconciler struct { Recorder record.EventRecorder } -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=secrettransformations,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=secrettransformations/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=secrettransformations/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=secrettransformations,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=secrettransformations/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=secrettransformations/finalizers,verbs=update +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -59,11 +60,11 @@ func (r *SecretTransformationReconciler) Reconcile(ctx context.Context, req ctrl return ctrl.Result{}, nil } - o.Status.Valid = true + o.Status.Valid = ptr.To(true) o.Status.Error = "" errs := ValidateSecretTransformation(ctx, o) if errs != nil { - o.Status.Valid = false + o.Status.Valid = ptr.To(false) o.Status.Error = errs.Error() logger.Error(err, "Failed to validate configured templates") r.Recorder.Eventf(o, corev1.EventTypeWarning, consts.ReasonInvalidConfiguration, @@ -79,7 +80,7 @@ func (r *SecretTransformationReconciler) Reconcile(ctx context.Context, req ctrl func (r *SecretTransformationReconciler) updateStatus(ctx context.Context, o *secretsv1beta1.SecretTransformation) error { logger := log.FromContext(ctx) - metrics.SetResourceStatus("secrettransformation", o, o.Status.Valid) + metrics.SetResourceStatus("secrettransformation", o, ptr.Deref(o.Status.Valid, false)) if err := r.Status().Update(ctx, o); err != nil { logger.Error(err, "Failed to update the resource's status") return err diff --git a/controllers/vaultauth_controller.go b/controllers/vaultauth_controller.go index d424c0cf..4bda0ba3 100644 --- a/controllers/vaultauth_controller.go +++ b/controllers/vaultauth_controller.go @@ -16,6 +16,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -76,26 +77,27 @@ func (r *VaultAuthReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } // assume that status is always invalid - o.Status.Valid = false + o.Status.Valid = ptr.To(false) var errs error var conditions []metav1.Condition if o.Spec.VaultAuthGlobalRef != nil { condition := metav1.Condition{ - Type: "VaultAuthGlobalRef", - Status: "True", + Type: "Available", + Status: metav1.ConditionTrue, ObservedGeneration: o.Generation, - LastTransitionTime: metav1.NewTime(nowFunc()), Reason: "VaultAuthGlobalRef", } mObj, gObj, err := common.MergeInVaultAuthGlobal(ctx, r.Client, o, r.GlobalVaultAuthOptions) if err != nil { + errs = errors.Join(errs, err) condition.Message = err.Error() - condition.Status = "False" + condition.Status = metav1.ConditionFalse } else { o = mObj - condition.Message = fmt.Sprintf("%s:%s:%d", + condition.Message = fmt.Sprintf( + "VaultAuthGlobal successfully merged, key=%s, uid=%s, generation=%d", client.ObjectKeyFromObject(gObj), gObj.UID, gObj.Generation) r.referenceCache.Set( VaultAuthGlobal, req.NamespacedName, @@ -106,8 +108,6 @@ func (r *VaultAuthReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( r.referenceCache.Remove(VaultAuthGlobal, req.NamespacedName) } - o.Status.Conditions = conditions - // ensure that the vaultConnectionRef is set for any VaultAuth resource in the operator namespace. if o.Namespace == common.OperatorNamespace && o.Spec.VaultConnectionRef == "" { err = fmt.Errorf("vaultConnectionRef must be set on resources in the %q namespace", common.OperatorNamespace) @@ -170,34 +170,46 @@ func (r *VaultAuthReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( o.Status.SpecHash = specHash + var horizon time.Duration if errs != nil { - o.Status.Valid = false + o.Status.Valid = ptr.To(false) o.Status.Error = errs.Error() + horizon = computeHorizonWithJitter(requeueDurationOnError) } else { - o.Status.Valid = true + o.Status.Valid = ptr.To(true) o.Status.Error = "" } - if err := r.updateStatus(ctx, o); err != nil { + if err := r.updateStatus(ctx, o, conditions...); err != nil { return ctrl.Result{}, err } - r.recordEvent(o, consts.ReasonAccepted, "Successfully handled VaultAuth resource request") - return ctrl.Result{}, nil + if errs == nil { + r.recordEvent(o, consts.ReasonAccepted, "Successfully handled VaultAuth resource request") + } else { + logger.Error(errs, "Failed to handle VaultAuth resource request", "horizon", horizon) + r.recordEvent(o, consts.ReasonAccepted, + fmt.Sprintf("Failed to handle VaultAuth resource request: err=%s", errs)) + } + + return ctrl.Result{ + RequeueAfter: horizon, + }, nil } -func (r *VaultAuthReconciler) recordEvent(a *secretsv1beta1.VaultAuth, reason, msg string, i ...interface{}) { +func (r *VaultAuthReconciler) recordEvent(o *secretsv1beta1.VaultAuth, reason, msg string, i ...interface{}) { eventType := corev1.EventTypeNormal - if !a.Status.Valid { + if !ptr.Deref(o.Status.Valid, false) { eventType = corev1.EventTypeWarning } - r.Recorder.Eventf(a, eventType, reason, msg, i...) + r.Recorder.Eventf(o, eventType, reason, msg, i...) } -func (r *VaultAuthReconciler) updateStatus(ctx context.Context, o *secretsv1beta1.VaultAuth) error { +func (r *VaultAuthReconciler) updateStatus(ctx context.Context, o *secretsv1beta1.VaultAuth, conditions ...metav1.Condition) error { logger := log.FromContext(ctx) - metrics.SetResourceStatus("vaultauth", o, o.Status.Valid) + metrics.SetResourceStatus("vaultauth", o, ptr.Deref(o.Status.Valid, false)) + o.Status.Conditions = updateConditions(o.Status.Conditions, conditions...) if err := r.Status().Update(ctx, o); err != nil { logger.Error(err, "Failed to update the resource's status") return err diff --git a/controllers/vaultconnection_controller.go b/controllers/vaultconnection_controller.go index 274ef69a..6a6d4ca5 100644 --- a/controllers/vaultconnection_controller.go +++ b/controllers/vaultconnection_controller.go @@ -7,12 +7,11 @@ import ( "context" "errors" - "github.com/hashicorp/vault-secrets-operator/internal/metrics" - corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -21,6 +20,7 @@ import ( secretsv1beta1 "github.com/hashicorp/vault-secrets-operator/api/v1beta1" "github.com/hashicorp/vault-secrets-operator/internal/consts" + "github.com/hashicorp/vault-secrets-operator/internal/metrics" "github.com/hashicorp/vault-secrets-operator/internal/vault" ) @@ -34,13 +34,13 @@ type VaultConnectionReconciler struct { ClientFactory vault.CachingClientFactory } -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=vaultconnections,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=vaultconnections/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=secrets.hashicorp.com,resources=vaultconnections/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch -//+kubebuilder:rbac:groups="",resources=secrets,verbs=get +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=vaultconnections,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=vaultconnections/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=secrets.hashicorp.com,resources=vaultconnections/finalizers,verbs=update +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get // needed for managing cached Clients, duplicated in vaultauth_controller.go -//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;delete;update;patch;deletecollection +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;delete;update;patch;deletecollection // Reconcile reconciles the secretsv1beta1.VaultConnection resource. // Upon a reconciliation it will verify that the configured Vault connection is valid. @@ -66,7 +66,7 @@ func (r *VaultConnectionReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // assume that status is always invalid - o.Status.Valid = false + o.Status.Valid = ptr.To(false) vaultConfig := &vault.ClientConfig{ CACertSecretRef: o.Spec.CACertSecretRef, @@ -92,7 +92,7 @@ func (r *VaultConnectionReconciler) Reconcile(ctx context.Context, req ctrl.Requ r.Recorder.Eventf(o, corev1.EventTypeWarning, "VaultClientError", "Failed to check Vault seal status: %s", err) errs = errors.Join(errs, err) } else { - o.Status.Valid = true + o.Status.Valid = ptr.To(true) } } @@ -124,7 +124,7 @@ func (r *VaultConnectionReconciler) Reconcile(ctx context.Context, req ctrl.Requ func (r *VaultConnectionReconciler) updateStatus(ctx context.Context, o *secretsv1beta1.VaultConnection) error { logger := log.FromContext(ctx) - metrics.SetResourceStatus("vaultconnection", o, o.Status.Valid) + metrics.SetResourceStatus("vaultconnection", o, ptr.Deref(o.Status.Valid, false)) if err := r.Status().Update(ctx, o); err != nil { logger.Error(err, "Failed to update the resource's status") return err diff --git a/controllers/vaultpkisecret_controller.go b/controllers/vaultpkisecret_controller.go index bad471e7..c52d2d40 100644 --- a/controllers/vaultpkisecret_controller.go +++ b/controllers/vaultpkisecret_controller.go @@ -18,6 +18,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -166,7 +167,7 @@ func (r *VaultPKISecretReconciler) Reconcile(ctx context.Context, req ctrl.Reque } // assume that status is always invalid - o.Status.Valid = false + o.Status.Valid = ptr.To(false) logger.Info("Must sync", "reason", syncReason) c, err := r.ClientFactory.Get(ctx, r.Client, o) if err != nil { @@ -294,7 +295,7 @@ func (r *VaultPKISecretReconciler) Reconcile(ctx context.Context, req ctrl.Reque } } - o.Status.Valid = true + o.Status.Valid = ptr.To(true) o.Status.Error = "" o.Status.SerialNumber = certResp.SerialNumber o.Status.Expiration = certResp.Expiration @@ -416,20 +417,20 @@ func (r *VaultPKISecretReconciler) getPath(spec secretsv1beta1.VaultPKISecretSpe return strings.Join(parts, "/") } -func (r *VaultPKISecretReconciler) recordEvent(p *secretsv1beta1.VaultPKISecret, reason, msg string, i ...interface{}) { +func (r *VaultPKISecretReconciler) recordEvent(o *secretsv1beta1.VaultPKISecret, reason, msg string, i ...interface{}) { eventType := corev1.EventTypeNormal - if !p.Status.Valid { + if !ptr.Deref(o.Status.Valid, false) { eventType = corev1.EventTypeWarning } - r.Recorder.Eventf(p, eventType, reason, msg, i...) + r.Recorder.Eventf(o, eventType, reason, msg, i...) } func (r *VaultPKISecretReconciler) updateStatus(ctx context.Context, o *secretsv1beta1.VaultPKISecret) error { logger := log.FromContext(ctx) logger.V(consts.LogLevelTrace).Info("Update status called") - metrics.SetResourceStatus("vaultpkisecret", o, o.Status.Valid) + metrics.SetResourceStatus("vaultpkisecret", o, ptr.Deref(o.Status.Valid, false)) o.Status.LastGeneration = o.GetGeneration() if err := r.Status().Update(ctx, o); err != nil { diff --git a/docs/api/api-reference.md b/docs/api/api-reference.md index 5c4ca97c..bfc901b6 100644 --- a/docs/api/api-reference.md +++ b/docs/api/api-reference.md @@ -729,7 +729,7 @@ _Appears in:_ | `name` _string_ | Name of the VaultAuthGlobal resource. | | Pattern: `^([a-z0-9.-]{1,253})$`
| | `namespace` _string_ | Namespace of the VaultAuthGlobal resource. If not provided, the namespace of
the referring VaultAuth resource is used. | | Pattern: `^([a-z0-9.-]{1,253})$`
| | `mergeStrategy` _[MergeStrategy](#mergestrategy)_ | MergeStrategy configures the merge strategy for HTTP headers and parameters
that are included in all Vault authentication requests. | | | -| `allowDefault` _boolean_ | AllowDefault when set to true will use the default VaultAuthGlobal resource
as the default if Name is not set. The 'allow-default-globals' option must be
set on the operator's '-global-vault-auth-options' flag

The default VaultAuthGlobal search is conditional.
When a ref Namespace is not set, the search follows the order:
1. The referring VaultAuth Namespace.
2. The Operator's namespace.
Otherwise, the search follows the order:
1. The VaultAuthGlobal ref Namespace. | | | +| `allowDefault` _boolean_ | AllowDefault when set to true will use the default VaultAuthGlobal resource
as the default if Name is not set. The 'allow-default-globals' option must be
set on the operator's '-global-vault-auth-options' flag

The default VaultAuthGlobal search is conditional.
When a ref Namespace is set, the search for the default
VaultAuthGlobal resource is constrained to that namespace.
Otherwise, the search order is:
1. The default VaultAuthGlobal resource in the referring VaultAuth resource's
namespace.
2. The default VaultAuthGlobal resource in the Operator's namespace. | | | #### VaultAuthGlobalSpec diff --git a/go.mod b/go.mod index 9ea847af..481ddcac 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.0 toolchain go1.22.2 require ( - cloud.google.com/go/compute/metadata v0.4.0 + cloud.google.com/go/compute/metadata v0.5.0 github.com/Masterminds/sprig/v3 v3.2.3 github.com/argoproj/argo-rollouts v1.6.6 github.com/cenkalti/backoff/v4 v4.3.0 @@ -13,7 +13,7 @@ require ( github.com/go-openapi/runtime v0.28.0 github.com/go-openapi/strfmt v0.23.0 github.com/google/uuid v1.6.0 - github.com/gruntwork-io/terratest v0.46.16 + github.com/gruntwork-io/terratest v0.47.0 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-rootcerts v1.0.2 github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 diff --git a/go.sum b/go.sum index 3d5d0e0a..c99f88c7 100644 --- a/go.sum +++ b/go.sum @@ -72,8 +72,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute/metadata v0.4.0 h1:vHzJCWaM4g8XIcm8kopr3XmDA4Gy/lblD3EhhSux05c= -cloud.google.com/go/compute/metadata v0.4.0/go.mod h1:SIQh1Kkb4ZJ8zJ874fqVkslA29PRXuleyj6vOzlbK7M= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= @@ -442,8 +442,8 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gruntwork-io/go-commons v0.8.0 h1:k/yypwrPqSeYHevLlEDmvmgQzcyTwrlZGRaxEM6G0ro= github.com/gruntwork-io/go-commons v0.8.0/go.mod h1:gtp0yTtIBExIZp7vyIV9I0XQkVwiQZze678hvDXof78= -github.com/gruntwork-io/terratest v0.46.16 h1:l+HHuU7lNLwoAl2sP8zkYJy0uoE2Mwha2nw+rim+OhQ= -github.com/gruntwork-io/terratest v0.46.16/go.mod h1:oywHw1cFKXSYvKPm27U7quZVzDUlA22H2xUrKCe26xM= +github.com/gruntwork-io/terratest v0.47.0 h1:xIy1pT7NbGVlMLDZEHl3+3iSnvffh8tN2pL6idn448c= +github.com/gruntwork-io/terratest v0.47.0/go.mod h1:oywHw1cFKXSYvKPm27U7quZVzDUlA22H2xUrKCe26xM= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/internal/common/common.go b/internal/common/common.go index 857b5b4c..3652c576 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -76,6 +76,18 @@ func (n *DefaultVaultAuthNotFoundError) Error() string { "default vault auth not found in namespaces=%v: global=%t", n.Namespaces, n.Global) } +type InvalidMergeError struct { + Err error +} + +func (n *InvalidMergeError) Error() string { + err := n.Err + if err == nil { + err = errors.New("unknown") + } + return fmt.Sprintf("invalid merge: %s", err) +} + func init() { var err error OperatorNamespace, err = utils.GetCurrentNamespace() @@ -288,9 +300,11 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets // object is used. if cObj.Spec.Method == "" { if gObj.Spec.DefaultAuthMethod == "" { - return nil, nil, fmt.Errorf( - "no auth method set in VaultAuth %s and no default method set in VaultAuthGlobal %s", - client.ObjectKeyFromObject(cObj), authGlobalRef) + return nil, nil, &InvalidMergeError{ + Err: fmt.Errorf( + "no auth method set in VaultAuth %s and no default method set in VaultAuthGlobal %s", + client.ObjectKeyFromObject(cObj), authGlobalRef), + } } cObj.Spec.Method = gObj.Spec.DefaultAuthMethod } @@ -304,8 +318,10 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets globalAuthMethod := gObj.Spec.Kubernetes mergeTargetAuthMethod := cObj.Spec.Kubernetes if mergeTargetAuthMethod == nil && globalAuthMethod == nil { - return nil, nil, fmt.Errorf("global auth method %s is not configured "+ - "in VaultAuthGlobal %s", cObj.Spec.Method, authGlobalRef) + return nil, nil, &InvalidMergeError{ + Err: fmt.Errorf("global auth method %s is not configured "+ + "in VaultAuthGlobal %s", cObj.Spec.Method, authGlobalRef), + } } if globalAuthMethod != nil { @@ -315,12 +331,12 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets } else { merged, err := mergeTargetAuthMethod.Merge(srcAuthMethod) if err != nil { - return nil, nil, err + return nil, nil, &InvalidMergeError{Err: err} } cObj.Spec.Kubernetes = merged } if err := cObj.Spec.Kubernetes.Validate(); err != nil { - return nil, nil, err + return nil, nil, &InvalidMergeError{Err: err} } globalAuthMount = globalAuthMethod.Mount globalAuthNamespace = globalAuthMethod.Namespace @@ -331,8 +347,10 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets globalAuthMethod := gObj.Spec.JWT mergeTargetAuthMethod := cObj.Spec.JWT if mergeTargetAuthMethod == nil && globalAuthMethod == nil { - return nil, nil, fmt.Errorf("global auth method %s is not configured "+ - "in VaultAuthGlobal %s", cObj.Spec.Method, authGlobalRef) + return nil, nil, &InvalidMergeError{ + Err: fmt.Errorf("global auth method %s is not configured "+ + "in VaultAuthGlobal %s", cObj.Spec.Method, authGlobalRef), + } } if globalAuthMethod != nil { @@ -342,12 +360,12 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets } else { merged, err := mergeTargetAuthMethod.Merge(srcAuthMethod) if err != nil { - return nil, nil, err + return nil, nil, &InvalidMergeError{Err: err} } cObj.Spec.JWT = merged } if err := cObj.Spec.JWT.Validate(); err != nil { - return nil, nil, err + return nil, nil, &InvalidMergeError{Err: err} } globalAuthMount = globalAuthMethod.Mount globalAuthNamespace = globalAuthMethod.Namespace @@ -358,8 +376,10 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets globalAuthMethod := gObj.Spec.AppRole mergeTargetAuthMethod := cObj.Spec.AppRole if mergeTargetAuthMethod == nil && globalAuthMethod == nil { - return nil, nil, fmt.Errorf("global auth method %s is not configured "+ - "in VaultAuthGlobal %s", cObj.Spec.Method, authGlobalRef) + return nil, nil, &InvalidMergeError{ + Err: fmt.Errorf("global auth method %s is not configured "+ + "in VaultAuthGlobal %s", cObj.Spec.Method, authGlobalRef), + } } if globalAuthMethod != nil { @@ -369,12 +389,12 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets } else { merged, err := mergeTargetAuthMethod.Merge(srcAuthMethod) if err != nil { - return nil, nil, err + return nil, nil, &InvalidMergeError{Err: err} } cObj.Spec.AppRole = merged } if err := cObj.Spec.AppRole.Validate(); err != nil { - return nil, nil, err + return nil, nil, &InvalidMergeError{Err: err} } globalAuthMount = globalAuthMethod.Mount globalAuthNamespace = globalAuthMethod.Namespace @@ -401,7 +421,7 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets cObj.Spec.AWS = merged } if err := cObj.Spec.AWS.Validate(); err != nil { - return nil, nil, err + return nil, nil, &InvalidMergeError{Err: err} } globalAuthMount = globalAuthMethod.Mount globalAuthNamespace = globalAuthMethod.Namespace @@ -412,8 +432,10 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets globalAuthMethod := gObj.Spec.GCP mergeTargetAuthMethod := cObj.Spec.GCP if mergeTargetAuthMethod == nil && globalAuthMethod == nil { - return nil, nil, fmt.Errorf("global auth method %s is not configured "+ - "in VaultAuthGlobal %s", cObj.Spec.Method, authGlobalRef) + return nil, nil, &InvalidMergeError{ + Err: fmt.Errorf("global auth method %s is not configured "+ + "in VaultAuthGlobal %s", cObj.Spec.Method, authGlobalRef), + } } if globalAuthMethod != nil { @@ -428,7 +450,7 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets cObj.Spec.GCP = merged } if err := cObj.Spec.GCP.Validate(); err != nil { - return nil, nil, err + return nil, nil, &InvalidMergeError{Err: err} } globalAuthMount = globalAuthMethod.Mount globalAuthNamespace = globalAuthMethod.Namespace @@ -436,19 +458,23 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets globalAuthHeaders = globalAuthMethod.Headers } default: - return nil, nil, fmt.Errorf( - "unsupported auth method %q for global auth merge", - cObj.Spec.Method, - ) + return nil, nil, &InvalidMergeError{ + Err: fmt.Errorf( + "unsupported auth method %q for global auth merge", + cObj.Spec.Method, + ), + } } cObj.Spec.Mount = firstNonZeroLen(strLenFunc, cObj.Spec.Mount, globalAuthMount, gObj.Spec.DefaultMount) if cObj.Spec.Mount == "" { - return nil, nil, fmt.Errorf( - "mount is not set in VaultAuth %s after merge with %s", - client.ObjectKeyFromObject(cObj), authGlobalRef, - ) + return nil, nil, &InvalidMergeError{ + Err: fmt.Errorf( + "mount is not set in VaultAuth %s after merge with %s", + client.ObjectKeyFromObject(cObj), authGlobalRef, + ), + } } cObj.Spec.Namespace = firstNonZeroLen(strLenFunc, @@ -473,7 +499,7 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets cObj.Spec.Params, globalAuthParams, gObj.Spec.DefaultParams) case "none": default: - return nil, nil, fmt.Errorf("unsupported params merge strategy %q", paramsMergeStrategy) + return nil, nil, &InvalidMergeError{Err: fmt.Errorf("unsupported params merge strategy %q", paramsMergeStrategy)} } switch headersMergeStrategy { @@ -484,7 +510,7 @@ func MergeInVaultAuthGlobal(ctx context.Context, c ctrlclient.Client, o *secrets cObj.Spec.Headers, globalAuthHeaders, gObj.Spec.DefaultHeaders) case "none": default: - return nil, nil, fmt.Errorf("unsupported headers merge strategy %q", headersMergeStrategy) + return nil, nil, &InvalidMergeError{Err: fmt.Errorf("unsupported headers merge strategy %q", headersMergeStrategy)} } cObj.Spec.VaultConnectionRef = firstNonZeroLen(strLenFunc, diff --git a/internal/common/common_test.go b/internal/common/common_test.go index d64afe3e..d6220231 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -18,7 +18,7 @@ import ( "k8s.io/apimachinery/pkg/types" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -846,7 +846,7 @@ func Test_MergeInVaultAuthGlobal(t *testing.T) { } wantK8sUnionParamsOverrideDefaultRef := wantK8sUnionParamsOverride.DeepCopy() - wantK8sUnionParamsOverrideDefaultRef.Spec.VaultAuthGlobalRef.AllowDefault = pointer.Bool(true) + wantK8sUnionParamsOverrideDefaultRef.Spec.VaultAuthGlobalRef.AllowDefault = ptr.To(true) wantK8sUnionParamsOverrideDefaultRef.Spec.VaultAuthGlobalRef.Name = "" wantK8sUnionParamsOverrideDefaultRef.Spec.VaultAuthGlobalRef.Namespace = "baz" @@ -1207,8 +1207,12 @@ func Test_MergeInVaultAuthGlobal(t *testing.T) { }, gObj: gObj.DeepCopy(), wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { - return assert.EqualError(t, err, - `unsupported auth method "invalid" for global auth merge`) + var wantErr *InvalidMergeError + if assert.ErrorAs(t, err, &wantErr) { + return assert.EqualError(t, wantErr.Err, + `unsupported auth method "invalid" for global auth merge`) + } + return false }, }, { @@ -1539,7 +1543,7 @@ func Test_MergeInVaultAuthGlobal(t *testing.T) { }, VaultAuthGlobalRef: &secretsv1beta1.VaultAuthGlobalRef{ Namespace: "baz", - AllowDefault: pointer.Bool(true), + AllowDefault: ptr.To(true), MergeStrategy: &secretsv1beta1.MergeStrategy{ Params: "union", }, @@ -1567,7 +1571,7 @@ func Test_MergeInVaultAuthGlobal(t *testing.T) { "baz": "override", }, VaultAuthGlobalRef: &secretsv1beta1.VaultAuthGlobalRef{ - AllowDefault: pointer.Bool(true), + AllowDefault: ptr.To(true), MergeStrategy: &secretsv1beta1.MergeStrategy{ Params: "union", }, @@ -1595,7 +1599,7 @@ func Test_MergeInVaultAuthGlobal(t *testing.T) { "baz": "override", }, VaultAuthGlobalRef: &secretsv1beta1.VaultAuthGlobalRef{ - AllowDefault: pointer.Bool(true), + AllowDefault: ptr.To(true), MergeStrategy: &secretsv1beta1.MergeStrategy{ Params: "union", }, @@ -1624,7 +1628,7 @@ func Test_MergeInVaultAuthGlobal(t *testing.T) { }, VaultAuthGlobalRef: &secretsv1beta1.VaultAuthGlobalRef{ Namespace: "baz", - AllowDefault: pointer.Bool(true), + AllowDefault: ptr.To(true), MergeStrategy: &secretsv1beta1.MergeStrategy{ Params: "union", }, @@ -1655,7 +1659,7 @@ func Test_MergeInVaultAuthGlobal(t *testing.T) { }, VaultAuthGlobalRef: &secretsv1beta1.VaultAuthGlobalRef{ Namespace: "baz", - AllowDefault: pointer.Bool(true), + AllowDefault: ptr.To(true), MergeStrategy: &secretsv1beta1.MergeStrategy{ Params: "union", }, @@ -1684,7 +1688,7 @@ func Test_MergeInVaultAuthGlobal(t *testing.T) { "baz": "override", }, VaultAuthGlobalRef: &secretsv1beta1.VaultAuthGlobalRef{ - AllowDefault: pointer.Bool(true), + AllowDefault: ptr.To(true), MergeStrategy: &secretsv1beta1.MergeStrategy{ Params: "union", }, diff --git a/internal/helpers/hmac.go b/internal/helpers/hmac.go index eac19a90..7461ad58 100644 --- a/internal/helpers/hmac.go +++ b/internal/helpers/hmac.go @@ -15,7 +15,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -205,7 +205,7 @@ func createHMACKeySecret(ctx context.Context, client ctrlclient.Client, objKey c Namespace: objKey.Namespace, Labels: hmacSecretLabels, }, - Immutable: pointer.Bool(true), + Immutable: ptr.To(true), Data: map[string][]byte{ HMACKeyName: key, }, diff --git a/internal/helpers/serviceaccount.go b/internal/helpers/serviceaccount.go index 38b57e4f..aabd599a 100644 --- a/internal/helpers/serviceaccount.go +++ b/internal/helpers/serviceaccount.go @@ -9,7 +9,7 @@ import ( v12 "k8s.io/api/authentication/v1" "k8s.io/api/core/v1" v13 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/hashicorp/vault-secrets-operator/internal/common" @@ -24,7 +24,7 @@ func RequestSAToken(ctx context.Context, client client.Client, sa *v1.ServiceAcc GenerateName: TokenGenerateName, }, Spec: v12.TokenRequestSpec{ - ExpirationSeconds: pointer.Int64(expirationSeconds), + ExpirationSeconds: ptr.To(expirationSeconds), Audiences: audiences, }, Status: v12.TokenRequestStatus{}, diff --git a/internal/helpers/template.go b/internal/helpers/template.go index 6dcdd5f1..b0c160b4 100644 --- a/internal/helpers/template.go +++ b/internal/helpers/template.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/golang-lru/v2" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/utils/ptr" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" secretsv1beta1 "github.com/hashicorp/vault-secrets-operator/api/v1beta1" @@ -217,7 +218,7 @@ func gatherTemplates(ctx context.Context, client ctrlclient.Client, meta *common continue } - if !obj.Status.Valid { + if !ptr.Deref(obj.Status.Valid, false) { errs = errors.Join(errs, &InvalidSecretTransformationRefError{ objKey: objKey, diff --git a/internal/helpers/template_test.go b/internal/helpers/template_test.go index 18158290..d49ffbc0 100644 --- a/internal/helpers/template_test.go +++ b/internal/helpers/template_test.go @@ -13,6 +13,7 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" secretsv1beta1 "github.com/hashicorp/vault-secrets-operator/api/v1beta1" @@ -562,7 +563,7 @@ func TestNewSecretTransformationOption(t *testing.T) { if status == nil { status = &secretsv1beta1.SecretTransformationStatus{ - Valid: true, + Valid: ptr.To(true), } } @@ -1211,7 +1212,7 @@ func TestNewSecretTransformationOption(t *testing.T) { }, }, &secretsv1beta1.SecretTransformationStatus{ - Valid: false, + Valid: ptr.To(false), Error: "", }), }, diff --git a/internal/vault/cache.go b/internal/vault/cache.go index d2c694bb..bd2306f8 100644 --- a/internal/vault/cache.go +++ b/internal/vault/cache.go @@ -7,7 +7,7 @@ import ( "fmt" "strings" - "github.com/hashicorp/golang-lru/v2" + lru "github.com/hashicorp/golang-lru/v2" "github.com/prometheus/client_golang/prometheus" ) diff --git a/internal/vault/cache_storage.go b/internal/vault/cache_storage.go index ec385fa3..67d74aac 100644 --- a/internal/vault/cache_storage.go +++ b/internal/vault/cache_storage.go @@ -20,7 +20,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/json" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -231,7 +231,7 @@ func (c *defaultClientCacheStorage) Store(ctx context.Context, client ctrlclient } s := &corev1.Secret{ // we always store Clients in an Immutable secret as an anti-tampering mitigation. - Immutable: pointer.Bool(true), + Immutable: ptr.To(true), ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf(NamePrefixVCC + cacheKey.String()), Namespace: common.OperatorNamespace, diff --git a/internal/vault/cache_storage_metrics_test.go b/internal/vault/cache_storage_metrics_test.go index 442ca1ce..7f00200a 100644 --- a/internal/vault/cache_storage_metrics_test.go +++ b/internal/vault/cache_storage_metrics_test.go @@ -17,7 +17,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -104,38 +104,38 @@ func Test_clientCacheStorage_Metrics(t *testing.T) { storeLabelPairs := []*io_prometheus_client.LabelPair{ { - Name: pointer.String(metrics.LabelOperation), - Value: pointer.String(metrics.OperationStore), + Name: ptr.To(metrics.LabelOperation), + Value: ptr.To(metrics.OperationStore), }, } restoreLabelPairs := []*io_prometheus_client.LabelPair{ { - Name: pointer.String(metrics.LabelOperation), - Value: pointer.String(metrics.OperationRestore), + Name: ptr.To(metrics.LabelOperation), + Value: ptr.To(metrics.OperationRestore), }, } purgeLabelPairs := []*io_prometheus_client.LabelPair{ { - Name: pointer.String(metrics.LabelOperation), - Value: pointer.String(metrics.OperationPurge), + Name: ptr.To(metrics.LabelOperation), + Value: ptr.To(metrics.OperationPurge), }, } pruneLabelPairs := []*io_prometheus_client.LabelPair{ { - Name: pointer.String(metrics.LabelOperation), - Value: pointer.String(metrics.OperationPrune), + Name: ptr.To(metrics.LabelOperation), + Value: ptr.To(metrics.OperationPrune), }, } deleteLabelPairs := []*io_prometheus_client.LabelPair{ { - Name: pointer.String(metrics.LabelOperation), - Value: pointer.String(metrics.OperationDelete), + Name: ptr.To(metrics.LabelOperation), + Value: ptr.To(metrics.OperationDelete), }, } configLabelPairs := []*io_prometheus_client.LabelPair{ { - Name: pointer.String(metricsLabelEnforceEncryption), - Value: pointer.String("false"), + Name: ptr.To(metricsLabelEnforceEncryption), + Value: ptr.To("false"), }, } @@ -179,13 +179,13 @@ func Test_clientCacheStorage_Metrics(t *testing.T) { expectMetrics: expectMetrics{ store: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(5), + total: ptr.To[float64](5), labelPairs: storeLabelPairs, - errorsTotal: pointer.Float64(2), + errorsTotal: ptr.To[float64](2), }, ops: &expectedMetricVec{ - total: pointer.Float64(5), - errorsTotal: pointer.Float64(2), + total: ptr.To[float64](5), + errorsTotal: ptr.To[float64](2), labelPairs: storeLabelPairs, }, }, @@ -200,23 +200,23 @@ func Test_clientCacheStorage_Metrics(t *testing.T) { expectMetrics: expectMetrics{ store: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: storeLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: storeLabelPairs, }, }, restore: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(2), - errorsTotal: pointer.Float64(4), + total: ptr.To[float64](2), + errorsTotal: ptr.To[float64](4), labelPairs: restoreLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(2), - errorsTotal: pointer.Float64(4), + total: ptr.To[float64](2), + errorsTotal: ptr.To[float64](4), labelPairs: restoreLabelPairs, }, }, @@ -230,35 +230,35 @@ func Test_clientCacheStorage_Metrics(t *testing.T) { expectMetrics: expectMetrics{ store: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: storeLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: storeLabelPairs, }, }, prune: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(2), - errorsTotal: pointer.Float64(1), + total: ptr.To[float64](2), + errorsTotal: ptr.To[float64](1), labelPairs: pruneLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(2), - errorsTotal: pointer.Float64(1), + total: ptr.To[float64](2), + errorsTotal: ptr.To[float64](1), labelPairs: pruneLabelPairs, }, }, delete: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(2), - errorsTotal: pointer.Float64(1), + total: ptr.To[float64](2), + errorsTotal: ptr.To[float64](1), labelPairs: deleteLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(2), - errorsTotal: pointer.Float64(1), + total: ptr.To[float64](2), + errorsTotal: ptr.To[float64](1), labelPairs: deleteLabelPairs, }, }, @@ -273,33 +273,33 @@ func Test_clientCacheStorage_Metrics(t *testing.T) { expectMetrics: expectMetrics{ store: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: storeLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: storeLabelPairs, }, }, purge: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(1), - errorsTotal: pointer.Float64(1), + total: ptr.To[float64](1), + errorsTotal: ptr.To[float64](1), labelPairs: purgeLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(1), - errorsTotal: pointer.Float64(1), + total: ptr.To[float64](1), + errorsTotal: ptr.To[float64](1), labelPairs: purgeLabelPairs, }, }, prune: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: pruneLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: pruneLabelPairs, }, }, @@ -314,21 +314,21 @@ func Test_clientCacheStorage_Metrics(t *testing.T) { expectMetrics: expectMetrics{ store: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: storeLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: storeLabelPairs, }, }, restore: &expected{ reqs: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: restoreLabelPairs, }, ops: &expectedMetricVec{ - total: pointer.Float64(4), + total: ptr.To[float64](4), labelPairs: restoreLabelPairs, }, }, diff --git a/test/integration/integration_test.go b/test/integration/integration_test.go index a03242d1..6e8d5eb5 100644 --- a/test/integration/integration_test.go +++ b/test/integration/integration_test.go @@ -45,7 +45,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" @@ -920,7 +920,7 @@ func createDeployment(t *testing.T, ctx context.Context, client ctrlclient.Clien "test": key.Name, }, }, - Replicas: pointer.Int32(3), + Replicas: ptr.To[int32](3), Template: corev1.PodTemplateSpec{ ObjectMeta: v1.ObjectMeta{ Labels: map[string]string{ @@ -937,7 +937,7 @@ func createDeployment(t *testing.T, ctx context.Context, client ctrlclient.Clien }, }, }, - TerminationGracePeriodSeconds: pointer.Int64(2), + TerminationGracePeriodSeconds: ptr.To[int64](2), }, }, Strategy: appsv1.DeploymentStrategy{ @@ -970,7 +970,7 @@ func createArgoRolloutV1alpha1(t *testing.T, ctx context.Context, client ctrlcli "test": key.Name, }, }, - Replicas: pointer.Int32(2), + Replicas: ptr.To[int32](2), Template: corev1.PodTemplateSpec{ ObjectMeta: v1.ObjectMeta{ Labels: map[string]string{ @@ -987,14 +987,14 @@ func createArgoRolloutV1alpha1(t *testing.T, ctx context.Context, client ctrlcli }, }, }, - TerminationGracePeriodSeconds: pointer.Int64(2), + TerminationGracePeriodSeconds: ptr.To[int64](2), }, }, Strategy: argorolloutsv1alpha1.RolloutStrategy{ Canary: &argorolloutsv1alpha1.CanaryStrategy{ Steps: []argorolloutsv1alpha1.CanaryStep{ { - SetWeight: pointer.Int32(50), + SetWeight: ptr.To[int32](50), }, { Pause: &argorolloutsv1alpha1.RolloutPause{ diff --git a/test/unit/default-vault-auth-method.bats b/test/unit/default-vault-auth-method.bats index 1a9e07ab..4ac4745e 100755 --- a/test/unit/default-vault-auth-method.bats +++ b/test/unit/default-vault-auth-method.bats @@ -1,5 +1,10 @@ #!/usr/bin/env bats +# +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 +# + load _helpers #-------------------------------------------------------------------- @@ -398,3 +403,139 @@ load _helpers actual=$(echo "$object" | yq '.spec.gcp.projectID' | tee /dev/stderr) [ "${actual}" = "my-project" ] } + +@test "defaultAuthMethod/CR: with vaultAuthGlobalRef/default" { + cd "$(chart_dir)" + local actual + actual=$(helm template \ + --debug \ + -s templates/default-vault-auth-method.yaml \ + --set 'defaultAuthMethod.enabled=true' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + [ "$(echo "$actual" | yq '. | has("vaultAuthGlobalRef")')" = "false" ] +} + +@test "defaultAuthMethod/CR: with vaultAuthGlobalRef/enabled" { + cd "$(chart_dir)" + local actual + actual=$(helm template \ + --debug \ + -s templates/default-vault-auth-method.yaml \ + --set 'defaultAuthMethod.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.name=foo' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.namespace=baz' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef | has("allowDefault")')" = "false" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.name')" = "foo" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.namespace')" = "baz" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.mergeStrategy.params')" = "none" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.mergeStrategy.headers')" = "none" ] +} + +@test "defaultAuthMethod/CR: with vaultAuthGlobalRef/defaults/empty-params" { + cd "$(chart_dir)" + local actual + actual=$(helm template \ + --debug \ + -s templates/default-vault-auth-method.yaml \ + --set 'defaultAuthMethod.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.name=foo' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.namespace=baz' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.mergeStrategy.params=' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef | has("allowDefault")')" = "false" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.name')" = "foo" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.namespace')" = "baz" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.mergeStrategy | has("params")')" = "false" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.mergeStrategy.headers')" = "none" ] +} + +@test "defaultAuthMethod/CR: with vaultAuthGlobalRef/mergeStrategy/empty-headers" { + cd "$(chart_dir)" + local actual + actual=$(helm template \ + --debug \ + -s templates/default-vault-auth-method.yaml \ + --set 'defaultAuthMethod.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.name=foo' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.namespace=baz' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.mergeStrategy.headers=' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef | has("allowDefault")')" = "false" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.name')" = "foo" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.namespace')" = "baz" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.mergeStrategy.params')" = "none" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.mergeStrategy | has("headers")')" = "false" ] +} + +@test "defaultAuthMethod/CR: with vaultAuthGlobalRef/allowDefault=true" { + cd "$(chart_dir)" + local actual + actual=$(helm template \ + --debug \ + -s templates/default-vault-auth-method.yaml \ + --set 'defaultAuthMethod.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.name=foo' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.namespace=baz' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.allowDefault=true' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.allowDefault')" = "true" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.name')" = "foo" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.namespace')" = "baz" ] +} + +@test "defaultAuthMethod/CR: with vaultAuthGlobalRef/allowDefault=false" { + cd "$(chart_dir)" + local actual + actual=$(helm template \ + --debug \ + -s templates/default-vault-auth-method.yaml \ + --set 'defaultAuthMethod.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.name=foo' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.namespace=baz' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.allowDefault=false' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.allowDefault')" = "false" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.name')" = "foo" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.namespace')" = "baz" ] +} + +@test "defaultAuthMethod/CR: with vaultAuthGlobalRef/mergeStrategy/params=union-headers=replace" { + cd "$(chart_dir)" + local actual + actual=$(helm template \ + --debug \ + -s templates/default-vault-auth-method.yaml \ + --set 'defaultAuthMethod.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.enabled=true' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.name=foo' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.namespace=baz' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.allowDefault=false' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.mergeStrategy.params=union' \ + --set 'defaultAuthMethod.vaultAuthGlobalRef.mergeStrategy.headers=replace' \ + . | tee /dev/stderr | + yq '.spec' | tee /dev/stderr) + + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.allowDefault')" = "false" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.name')" = "foo" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.namespace')" = "baz" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.mergeStrategy.params')" = "union" ] + [ "$(echo "$actual" | yq '.vaultAuthGlobalRef.mergeStrategy.headers')" = "replace" ] +}