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