Merge pull request #5776 from maelvls/test-vault-validation
Vault: add unit tests for the controller-side validation of the Vault Issuer
This commit is contained in:
commit
57113668e0
354
pkg/issuer/vault/setup_test.go
Normal file
354
pkg/issuer/vault/setup_test.go
Normal file
@ -0,0 +1,354 @@
|
||||
/*
|
||||
Copyright 2022 The cert-manager 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
|
||||
|
||||
http://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 vault
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
|
||||
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
|
||||
cmfake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake"
|
||||
"github.com/cert-manager/cert-manager/pkg/controller"
|
||||
testlisters "github.com/cert-manager/cert-manager/test/unit/listers"
|
||||
corelisters "k8s.io/client-go/listers/core/v1"
|
||||
)
|
||||
|
||||
func TestVault_Setup(t *testing.T) {
|
||||
// Create a mock Vault HTTP server.
|
||||
vaultServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case r.URL.Path == "/v1/auth/approle/login" || r.URL.Path == "/v1/auth/kubernetes/login":
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{"auth":{"client_token": "5b1a0318-679c-9c45-e5c6-d1b9a9035d49"}}`))
|
||||
}
|
||||
}))
|
||||
defer vaultServer.Close()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenIssuer v1.IssuerConfig
|
||||
expectCond string
|
||||
expectErr string
|
||||
mockGetSecret *corev1.Secret
|
||||
mockGetSecretErr error
|
||||
}{
|
||||
{
|
||||
name: "developer mistake: the vault field is empty",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: nil,
|
||||
},
|
||||
expectCond: "Ready False: VaultError: Vault config cannot be empty",
|
||||
},
|
||||
{
|
||||
name: "path is missing",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Server: "https://vault.example.com",
|
||||
},
|
||||
},
|
||||
expectCond: "Ready False: VaultError: Vault server and path are required fields",
|
||||
},
|
||||
{
|
||||
name: "server is missing",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
},
|
||||
},
|
||||
expectCond: "Ready False: VaultError: Vault server and path are required fields",
|
||||
},
|
||||
{
|
||||
name: "auth.appRole, auth.kubernetes, and auth.tokenSecretRef are mutually exclusive",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
|
||||
Path: "pki_int",
|
||||
Server: "https://vault.example.com",
|
||||
Auth: v1.VaultAuth{
|
||||
AppRole: &v1.VaultAppRole{
|
||||
SecretRef: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
},
|
||||
},
|
||||
Kubernetes: &v1.VaultKubernetesAuth{
|
||||
SecretRef: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
},
|
||||
Path: "kubernetes",
|
||||
Role: "cert-manager",
|
||||
},
|
||||
TokenSecretRef: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
Key: "token",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready False: VaultError: Multiple auth methods cannot be set on the same Vault issuer",
|
||||
},
|
||||
{
|
||||
name: "valid auth.appRole",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: vaultServer.URL,
|
||||
Auth: v1.VaultAuth{
|
||||
AppRole: &v1.VaultAppRole{
|
||||
RoleId: "cert-manager",
|
||||
SecretRef: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
Key: "token",
|
||||
},
|
||||
Path: "approle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready True: VaultVerified: Vault verified",
|
||||
},
|
||||
{
|
||||
name: "auth.appRole.secretRef.key can be left empty, but an error will show",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: "https://vault.example.com",
|
||||
Auth: v1.VaultAuth{
|
||||
AppRole: &v1.VaultAppRole{
|
||||
RoleId: "cert-manager",
|
||||
SecretRef: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
},
|
||||
Path: "approle",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: `no data for "" in secret 'test-namespace/cert-manager'`,
|
||||
},
|
||||
{
|
||||
name: "auth.appRole.roleId is missing",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: "https://vault.example.com",
|
||||
Auth: v1.VaultAuth{
|
||||
AppRole: &v1.VaultAppRole{
|
||||
SecretRef: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready False: VaultError: Vault AppRole auth requires both roleId and tokenSecretRef.name",
|
||||
},
|
||||
{
|
||||
name: "auth.appRole.secretRef.name is missing",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: "https://vault.example.com",
|
||||
Auth: v1.VaultAuth{
|
||||
AppRole: &v1.VaultAppRole{
|
||||
RoleId: "cert-manager",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready False: VaultError: Vault AppRole auth requires both roleId and tokenSecretRef.name",
|
||||
},
|
||||
{
|
||||
name: "valid auth.kubernetes",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: vaultServer.URL,
|
||||
Auth: v1.VaultAuth{
|
||||
Kubernetes: &v1.VaultKubernetesAuth{
|
||||
Role: "cert-manager",
|
||||
SecretRef: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
Key: "token",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready True: VaultVerified: Vault verified",
|
||||
},
|
||||
{
|
||||
name: "auth.kubernetes.secretRef.name is missing",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: "https://vault.example.com",
|
||||
Auth: v1.VaultAuth{
|
||||
Kubernetes: &v1.VaultKubernetesAuth{
|
||||
Role: "cert-manager",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready False: VaultError: Vault Kubernetes auth requires both role and secretRef.name",
|
||||
},
|
||||
{
|
||||
name: "auth.kubernetes.secretRef.key can be left empty and defaults to 'token'",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: vaultServer.URL,
|
||||
Auth: v1.VaultAuth{
|
||||
Kubernetes: &v1.VaultKubernetesAuth{
|
||||
Role: "cert-manager",
|
||||
SecretRef: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready True: VaultVerified: Vault verified",
|
||||
},
|
||||
{
|
||||
name: "auth.kubernetes.role is missing",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: "https://vault.example.com",
|
||||
Auth: v1.VaultAuth{
|
||||
Kubernetes: &v1.VaultKubernetesAuth{
|
||||
SecretRef: cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready False: VaultError: Vault Kubernetes auth requires both role and secretRef.name",
|
||||
},
|
||||
{
|
||||
name: "valid auth.tokenSecretRef",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: vaultServer.URL,
|
||||
Auth: v1.VaultAuth{
|
||||
TokenSecretRef: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
Key: "token",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready True: VaultVerified: Vault verified",
|
||||
},
|
||||
{
|
||||
name: "auth.tokenSecretRef.key can be left empty and defaults to 'token'",
|
||||
givenIssuer: v1.IssuerConfig{
|
||||
Vault: &v1.VaultIssuer{
|
||||
Path: "pki_int",
|
||||
Server: vaultServer.URL,
|
||||
Auth: v1.VaultAuth{
|
||||
TokenSecretRef: &cmmeta.SecretKeySelector{
|
||||
LocalObjectReference: cmmeta.LocalObjectReference{
|
||||
Name: "cert-manager",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectCond: "Ready True: VaultVerified: Vault verified",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
givenIssuer := &v1.Issuer{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-issuer",
|
||||
Namespace: "test-namespace",
|
||||
},
|
||||
Spec: v1.IssuerSpec{
|
||||
IssuerConfig: tt.givenIssuer,
|
||||
},
|
||||
}
|
||||
cmclient := cmfake.NewSimpleClientset(givenIssuer)
|
||||
|
||||
v := &Vault{
|
||||
issuer: givenIssuer,
|
||||
Context: &controller.Context{CMClient: cmclient},
|
||||
resourceNamespace: "test-namespace",
|
||||
secretsLister: &testlisters.FakeSecretLister{
|
||||
SecretsFn: func(namespace string) corelisters.SecretNamespaceLister {
|
||||
return &testlisters.FakeSecretNamespaceLister{
|
||||
GetFn: func(name string) (ret *corev1.Secret, err error) {
|
||||
assert.Equal(t, "cert-manager", name)
|
||||
assert.Equal(t, "test-namespace", namespace)
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "cert-manager", Namespace: "test-namespace"},
|
||||
Data: map[string][]byte{"token": []byte("root")},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := v.Setup(context.Background())
|
||||
if tt.expectErr != "" {
|
||||
assert.EqualError(t, err, tt.expectErr)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
if tt.expectCond != "" {
|
||||
require.Len(t, givenIssuer.Status.Conditions, 1)
|
||||
assert.Equal(t, tt.expectCond, fmt.Sprintf("%s %s: %s: %s", givenIssuer.Status.Conditions[0].Type, givenIssuer.Status.Conditions[0].Status, givenIssuer.Status.Conditions[0].Reason, givenIssuer.Status.Conditions[0].Message))
|
||||
} else {
|
||||
require.Len(t, givenIssuer.Status.Conditions, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user