| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- /*
- Copyright © The ESO Authors
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- https://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package esutils
- import (
- "strings"
- "testing"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
- esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
- )
- // storeOfKind returns a minimal GenericStore whose GetKind() reports the
- // requested kind. TypeMeta is set as well so the selector validators, which
- // read GetObjectKind().GroupVersionKind().Kind, agree with GetKind().
- func storeOfKind(kind string) esv1.GenericStore {
- if kind == esv1.ClusterSecretStoreKind {
- return &esv1.ClusterSecretStore{TypeMeta: metav1.TypeMeta{Kind: kind}}
- }
- return &esv1.SecretStore{TypeMeta: metav1.TypeMeta{Kind: kind}}
- }
- func TestIsReferentKubernetesAuth(t *testing.T) {
- tests := []struct {
- name string
- auth *esv1.KubernetesAuth
- want bool
- }{
- {name: "nil auth", auth: nil, want: false},
- {name: "empty auth", auth: &esv1.KubernetesAuth{}, want: false},
- {
- name: "cert clientCert without namespace is referent",
- auth: &esv1.KubernetesAuth{Cert: &esv1.CertAuth{
- ClientCert: esmeta.SecretKeySelector{Name: "c", Key: "tls.crt"},
- ClientKey: esmeta.SecretKeySelector{Name: "c", Key: "tls.key", Namespace: new("ns")},
- }},
- want: true,
- },
- {
- name: "cert clientKey without namespace is referent",
- auth: &esv1.KubernetesAuth{Cert: &esv1.CertAuth{
- ClientCert: esmeta.SecretKeySelector{Name: "c", Key: "tls.crt", Namespace: new("ns")},
- ClientKey: esmeta.SecretKeySelector{Name: "c", Key: "tls.key"},
- }},
- want: true,
- },
- {
- name: "cert with both namespaces set is not referent",
- auth: &esv1.KubernetesAuth{Cert: &esv1.CertAuth{
- ClientCert: esmeta.SecretKeySelector{Name: "c", Key: "tls.crt", Namespace: new("ns")},
- ClientKey: esmeta.SecretKeySelector{Name: "c", Key: "tls.key", Namespace: new("ns")},
- }},
- want: false,
- },
- {
- name: "serviceAccount without namespace is referent",
- auth: &esv1.KubernetesAuth{ServiceAccount: &esmeta.ServiceAccountSelector{Name: "sa"}},
- want: true,
- },
- {
- name: "serviceAccount with namespace is not referent",
- auth: &esv1.KubernetesAuth{ServiceAccount: &esmeta.ServiceAccountSelector{Name: "sa", Namespace: new("ns")}},
- want: false,
- },
- {
- name: "token without namespace is referent",
- auth: &esv1.KubernetesAuth{Token: &esv1.TokenAuth{BearerToken: esmeta.SecretKeySelector{Name: "t", Key: "token"}}},
- want: true,
- },
- {
- name: "token with namespace is not referent",
- auth: &esv1.KubernetesAuth{Token: &esv1.TokenAuth{BearerToken: esmeta.SecretKeySelector{Name: "t", Key: "token", Namespace: new("ns")}}},
- want: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := IsReferentKubernetesAuth(tt.auth); got != tt.want {
- t.Errorf("IsReferentKubernetesAuth() = %v, want %v", got, tt.want)
- }
- })
- }
- }
- func TestValidateKubernetesConnection(t *testing.T) {
- tests := []struct {
- name string
- kind string
- server esv1.KubernetesServer
- auth *esv1.KubernetesAuth
- authRef *esmeta.SecretKeySelector
- wantErr bool
- errContains string
- wantWarning bool
- }{
- {
- name: "no CA and no authRef warns",
- kind: esv1.SecretStoreKind,
- wantWarning: true,
- },
- {
- name: "authRef suppresses the no-CA warning",
- kind: esv1.SecretStoreKind,
- authRef: &esmeta.SecretKeySelector{Name: "kubeconfig", Key: "config"},
- },
- {
- name: "CABundle suppresses the no-CA warning",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- },
- {
- name: "SecretStore with CAProvider (no namespace) is valid",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CAProvider: &esv1.CAProvider{Name: "ca"}},
- },
- {
- name: "ClusterSecretStore CAProvider requires namespace",
- kind: esv1.ClusterSecretStoreKind,
- server: esv1.KubernetesServer{CAProvider: &esv1.CAProvider{Name: "ca"}},
- wantErr: true,
- errContains: "CAProvider.namespace must not be empty",
- },
- {
- name: "SecretStore rejects CAProvider namespace",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CAProvider: &esv1.CAProvider{Name: "ca", Namespace: new("ns")}},
- wantErr: true,
- errContains: "CAProvider.namespace must be empty",
- },
- {
- name: "ClusterSecretStore CAProvider with namespace is valid",
- kind: esv1.ClusterSecretStoreKind,
- server: esv1.KubernetesServer{CAProvider: &esv1.CAProvider{Name: "ca", Namespace: new("ns")}},
- },
- {
- name: "cert missing clientCert name",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{Cert: &esv1.CertAuth{ClientCert: esmeta.SecretKeySelector{Key: "tls.crt"}, ClientKey: esmeta.SecretKeySelector{Name: "c", Key: "tls.key"}}},
- wantErr: true,
- errContains: "ClientCert.Name cannot be empty",
- },
- {
- name: "cert missing clientCert key",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{Cert: &esv1.CertAuth{ClientCert: esmeta.SecretKeySelector{Name: "c"}, ClientKey: esmeta.SecretKeySelector{Name: "c", Key: "tls.key"}}},
- wantErr: true,
- errContains: "ClientCert.Key cannot be empty",
- },
- {
- name: "cert valid",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{Cert: &esv1.CertAuth{ClientCert: esmeta.SecretKeySelector{Name: "c", Key: "tls.crt"}, ClientKey: esmeta.SecretKeySelector{Name: "c", Key: "tls.key"}}},
- },
- {
- name: "token missing name",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{Token: &esv1.TokenAuth{BearerToken: esmeta.SecretKeySelector{Key: "token"}}},
- wantErr: true,
- errContains: "BearerToken.Name cannot be empty",
- },
- {
- name: "token missing key",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{Token: &esv1.TokenAuth{BearerToken: esmeta.SecretKeySelector{Name: "t"}}},
- wantErr: true,
- errContains: "BearerToken.Key cannot be empty",
- },
- {
- name: "token valid",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{Token: &esv1.TokenAuth{BearerToken: esmeta.SecretKeySelector{Name: "t", Key: "token"}}},
- },
- {
- name: "referent serviceAccount on ClusterSecretStore is allowed",
- kind: esv1.ClusterSecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{ServiceAccount: &esmeta.ServiceAccountSelector{Name: "sa"}},
- },
- {
- // ClientCert has a cross-namespace selector on a SecretStore, which
- // the delegated ValidateSecretSelector rejects. Exercises the cert
- // selector error-propagation path.
- name: "cert selector namespace mismatch is rejected",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{
- Cert: &esv1.CertAuth{ClientCert: esmeta.SecretKeySelector{Name: "c", Key: "tls.crt", Namespace: new("other")}, ClientKey: esmeta.SecretKeySelector{Name: "c", Key: "tls.key"}},
- },
- wantErr: true,
- },
- {
- name: "token selector namespace mismatch is rejected",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{Token: &esv1.TokenAuth{BearerToken: esmeta.SecretKeySelector{Name: "t", Key: "token", Namespace: new("other")}}},
- wantErr: true,
- },
- {
- name: "serviceAccount selector namespace mismatch is rejected",
- kind: esv1.SecretStoreKind,
- server: esv1.KubernetesServer{CABundle: []byte("ca")},
- auth: &esv1.KubernetesAuth{ServiceAccount: &esmeta.ServiceAccountSelector{Name: "sa", Namespace: new("other")}},
- wantErr: true,
- },
- {
- name: "valid config without CA warns only",
- kind: esv1.SecretStoreKind,
- auth: &esv1.KubernetesAuth{Token: &esv1.TokenAuth{BearerToken: esmeta.SecretKeySelector{Name: "t", Key: "token"}}},
- wantWarning: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- store := storeOfKind(tt.kind)
- warnings, err := ValidateKubernetesConnection(store, tt.server, tt.auth, tt.authRef)
- if (err != nil) != tt.wantErr {
- t.Fatalf("ValidateKubernetesConnection() error = %v, wantErr %v", err, tt.wantErr)
- }
- if tt.errContains != "" && (err == nil || !strings.Contains(err.Error(), tt.errContains)) {
- t.Errorf("ValidateKubernetesConnection() error = %v, want contains %q", err, tt.errContains)
- }
- if tt.wantWarning {
- if len(warnings) != 1 || warnings[0] != WarnNoCAConfigured {
- t.Errorf("ValidateKubernetesConnection() warnings = %v, want [%q]", warnings, WarnNoCAConfigured)
- }
- } else if len(warnings) > 0 {
- t.Errorf("ValidateKubernetesConnection() unexpected warnings: %v", warnings)
- }
- })
- }
- }
|