Skip to content

Commit e74bb3f

Browse files
authored
test: add more unit test for rotateCredential (#1113)
1 parent d77bc1b commit e74bb3f

File tree

1 file changed

+222
-5
lines changed

1 file changed

+222
-5
lines changed

internal/controller/backend_security_policy_test.go

Lines changed: 222 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"fmt"
1212
"net/http"
1313
"net/http/httptest"
14-
"strings"
1514
"testing"
1615
"time"
1716

@@ -863,7 +862,9 @@ func TestValidateGCPCredentialsParams(t *testing.T) {
863862
func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *testing.T) {
864863
bspNamespace := "default"
865864
bspName := "test-gcp-policy"
866-
tests := []struct {
865+
866+
// Test cases for validation errors.
867+
validationTests := []struct {
867868
name string
868869
bsp *aigv1a1.BackendSecurityPolicySpec
869870
expectedErrMsg string
@@ -884,11 +885,23 @@ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *test
884885
},
885886
expectedErrMsg: "invalid GCP credentials configuration: projectName cannot be empty",
886887
},
888+
{
889+
name: "neither oidc nor credentials file configured",
890+
bsp: &aigv1a1.BackendSecurityPolicySpec{
891+
Type: aigv1a1.BackendSecurityPolicyTypeGCPCredentials,
892+
GCPCredentials: &aigv1a1.BackendSecurityPolicyGCPCredentials{
893+
ProjectName: "test-project",
894+
Region: "us-central1",
895+
// Neither WorkloadIdentityFederationConfig nor CredentialsFile is set.
896+
},
897+
},
898+
expectedErrMsg: "one of service account key json file or oidc must be defined",
899+
},
887900
}
888901

889902
c := NewBackendSecurityPolicyController(fake.NewFakeClient(), fake2.NewClientset(), ctrl.Log, nil)
890903

891-
for _, tt := range tests {
904+
for _, tt := range validationTests {
892905
bsp := &aigv1a1.BackendSecurityPolicy{
893906
ObjectMeta: metav1.ObjectMeta{
894907
Name: bspName,
@@ -897,7 +910,6 @@ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *test
897910
Spec: *tt.bsp,
898911
}
899912
t.Run(tt.name, func(t *testing.T) {
900-
// Initial rotation should create a new secret.
901913
res, err := c.rotateCredential(context.Background(), bsp)
902914

903915
switch {
@@ -906,7 +918,7 @@ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *test
906918
case tt.expectedErrMsg == "" && err != nil:
907919
t.Errorf("unexpected error: %v", err)
908920
case tt.expectedErrMsg != "" && err != nil:
909-
strings.Contains(err.Error(), tt.expectedErrMsg)
921+
require.Contains(t, err.Error(), tt.expectedErrMsg)
910922
default:
911923
require.NoError(t, err)
912924
require.NotZero(t, res.RequeueAfter)
@@ -915,6 +927,211 @@ func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials(t *test
915927
}
916928
}
917929

930+
func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials_OIDC(t *testing.T) {
931+
eventCh := internaltesting.NewControllerEventChan[*aigv1a1.AIServiceBackend]()
932+
cl := fake.NewClientBuilder().WithScheme(Scheme).Build()
933+
c := NewBackendSecurityPolicyController(cl, fake2.NewClientset(), ctrl.Log, eventCh.Ch)
934+
bspName := "gcp-oidc-policy"
935+
bspNamespace := "default"
936+
937+
oidcSecretName := "gcp-oidc-secret" // #nosec G101
938+
oidcSecret := corev1.Secret{
939+
ObjectMeta: metav1.ObjectMeta{
940+
Name: oidcSecretName,
941+
Namespace: bspNamespace,
942+
},
943+
Data: map[string][]byte{
944+
"client-secret": []byte("client-secret"),
945+
},
946+
}
947+
require.NoError(t, cl.Create(t.Context(), &oidcSecret, &client.CreateOptions{}))
948+
949+
// Create backend security policy with GCP OIDC config.
950+
oidc := egv1a1.OIDC{
951+
Provider: egv1a1.OIDCProvider{
952+
Issuer: "https://fake-issuer.com",
953+
},
954+
ClientID: ptr.To("some-client-id"),
955+
ClientSecret: gwapiv1.SecretObjectReference{
956+
Name: gwapiv1.ObjectName(oidcSecretName),
957+
Namespace: (*gwapiv1.Namespace)(&bspNamespace),
958+
},
959+
}
960+
bsp := &aigv1a1.BackendSecurityPolicy{
961+
ObjectMeta: metav1.ObjectMeta{Name: bspName, Namespace: bspNamespace},
962+
Spec: aigv1a1.BackendSecurityPolicySpec{
963+
Type: aigv1a1.BackendSecurityPolicyTypeGCPCredentials,
964+
GCPCredentials: &aigv1a1.BackendSecurityPolicyGCPCredentials{
965+
ProjectName: "test-project",
966+
Region: "us-central1",
967+
WorkloadIdentityFederationConfig: &aigv1a1.GCPWorkloadIdentityFederationConfig{
968+
ProjectID: "test-project-id",
969+
WorkloadIdentityPoolName: "test-pool",
970+
WorkloadIdentityProviderName: "test-provider",
971+
OIDCExchangeToken: aigv1a1.GCPOIDCExchangeToken{
972+
BackendSecurityPolicyOIDC: aigv1a1.BackendSecurityPolicyOIDC{
973+
OIDC: oidc,
974+
},
975+
},
976+
},
977+
},
978+
},
979+
}
980+
err := cl.Create(t.Context(), bsp)
981+
require.NoError(t, err)
982+
983+
// Test that the OIDC path validation passes and the method attempts to create OIDC provider
984+
// (which will fail due to network issues, but that confirms the OIDC path logic is working).
985+
res, err := c.rotateCredential(t.Context(), bsp)
986+
987+
// We expect an error due to network calls in the OIDC provider initialization.
988+
require.Error(t, err)
989+
require.Contains(t, err.Error(), "failed to initialize OIDC provider")
990+
require.Equal(t, ctrl.Result{}, res)
991+
}
992+
993+
func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials_CredentialsFile(t *testing.T) {
994+
eventCh := internaltesting.NewControllerEventChan[*aigv1a1.AIServiceBackend]()
995+
cl := fake.NewClientBuilder().WithScheme(Scheme).Build()
996+
c := NewBackendSecurityPolicyController(cl, fake2.NewClientset(), ctrl.Log, eventCh.Ch)
997+
bspName := "gcp-sa-policy"
998+
bspNamespace := "default"
999+
1000+
// Create a secret containing an invalid service account JSON structure to ensure controlled test failure.
1001+
serviceAccountJSON := `{
1002+
"type": "service_account",
1003+
"project_id": "test-project",
1004+
"private_key_id": "key-id",
1005+
"private_key": "invalid-private-key-data",
1006+
"client_email": "[email protected]",
1007+
"client_id": "123456789",
1008+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
1009+
"token_uri": "https://oauth2.googleapis.com/token"
1010+
}` // #nosec G101
1011+
1012+
serviceAccountSecretName := "gcp-sa-secret" // #nosec G101
1013+
serviceAccountSecret := corev1.Secret{
1014+
ObjectMeta: metav1.ObjectMeta{
1015+
Name: serviceAccountSecretName,
1016+
Namespace: bspNamespace,
1017+
},
1018+
Data: map[string][]byte{
1019+
rotators.GCPServiceAccountJSON: []byte(serviceAccountJSON),
1020+
},
1021+
}
1022+
require.NoError(t, cl.Create(t.Context(), &serviceAccountSecret, &client.CreateOptions{}))
1023+
1024+
// Create backend security policy with GCP service account credentials file config.
1025+
bsp := &aigv1a1.BackendSecurityPolicy{
1026+
ObjectMeta: metav1.ObjectMeta{Name: bspName, Namespace: bspNamespace},
1027+
Spec: aigv1a1.BackendSecurityPolicySpec{
1028+
Type: aigv1a1.BackendSecurityPolicyTypeGCPCredentials,
1029+
GCPCredentials: &aigv1a1.BackendSecurityPolicyGCPCredentials{
1030+
ProjectName: "test-project",
1031+
Region: "us-central1",
1032+
CredentialsFile: &aigv1a1.GCPCredentialsFile{
1033+
SecretRef: &gwapiv1.SecretObjectReference{
1034+
Name: gwapiv1.ObjectName(serviceAccountSecretName),
1035+
},
1036+
},
1037+
},
1038+
},
1039+
}
1040+
err := cl.Create(t.Context(), bsp)
1041+
require.NoError(t, err)
1042+
1043+
// Test that credentials file path is correctly selected and reaches token provider creation.
1044+
res, err := c.rotateCredential(t.Context(), bsp)
1045+
1046+
// The test behavior varies depending on environment mocking, but both outcomes are valid:
1047+
// 1. Error at token provider creation confirms the credentials file path worked
1048+
// 2. Success indicates test environment provides full mocking.
1049+
if err != nil {
1050+
require.Contains(t, err.Error(), "private key")
1051+
} else {
1052+
require.NotZero(t, res.RequeueAfter)
1053+
}
1054+
}
1055+
1056+
func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials_MissingSecret(t *testing.T) {
1057+
eventCh := internaltesting.NewControllerEventChan[*aigv1a1.AIServiceBackend]()
1058+
cl := fake.NewClientBuilder().WithScheme(Scheme).Build()
1059+
c := NewBackendSecurityPolicyController(cl, fake2.NewClientset(), ctrl.Log, eventCh.Ch)
1060+
bspName := "gcp-missing-secret-policy"
1061+
bspNamespace := "default"
1062+
1063+
// Create backend security policy with GCP credentials file that doesn't exist.
1064+
bsp := &aigv1a1.BackendSecurityPolicy{
1065+
ObjectMeta: metav1.ObjectMeta{Name: bspName, Namespace: bspNamespace},
1066+
Spec: aigv1a1.BackendSecurityPolicySpec{
1067+
Type: aigv1a1.BackendSecurityPolicyTypeGCPCredentials,
1068+
GCPCredentials: &aigv1a1.BackendSecurityPolicyGCPCredentials{
1069+
ProjectName: "test-project",
1070+
Region: "us-central1",
1071+
CredentialsFile: &aigv1a1.GCPCredentialsFile{
1072+
SecretRef: &gwapiv1.SecretObjectReference{
1073+
Name: gwapiv1.ObjectName("non-existent-secret"),
1074+
},
1075+
},
1076+
},
1077+
},
1078+
}
1079+
err := cl.Create(t.Context(), bsp)
1080+
require.NoError(t, err)
1081+
1082+
// Test that rotation fails when the referenced secret doesn't exist.
1083+
res, err := c.rotateCredential(t.Context(), bsp)
1084+
require.Error(t, err)
1085+
require.Contains(t, err.Error(), "secrets \"non-existent-secret\" not found")
1086+
require.Equal(t, ctrl.Result{}, res)
1087+
}
1088+
1089+
func TestBackendSecurityPolicyController_RotateCredential_GCPCredentials_MissingSecretKey(t *testing.T) {
1090+
eventCh := internaltesting.NewControllerEventChan[*aigv1a1.AIServiceBackend]()
1091+
cl := fake.NewClientBuilder().WithScheme(Scheme).Build()
1092+
c := NewBackendSecurityPolicyController(cl, fake2.NewClientset(), ctrl.Log, eventCh.Ch)
1093+
bspName := "gcp-missing-key-policy"
1094+
bspNamespace := "default"
1095+
1096+
// Create a secret without the required GCP service account JSON key.
1097+
serviceAccountSecretName := "gcp-incomplete-secret" // #nosec G101
1098+
serviceAccountSecret := corev1.Secret{
1099+
ObjectMeta: metav1.ObjectMeta{
1100+
Name: serviceAccountSecretName,
1101+
Namespace: bspNamespace,
1102+
},
1103+
Data: map[string][]byte{
1104+
"wrong-key": []byte("some-data"),
1105+
},
1106+
}
1107+
require.NoError(t, cl.Create(t.Context(), &serviceAccountSecret, &client.CreateOptions{}))
1108+
1109+
// Create backend security policy with GCP credentials file config pointing to incomplete secret.
1110+
bsp := &aigv1a1.BackendSecurityPolicy{
1111+
ObjectMeta: metav1.ObjectMeta{Name: bspName, Namespace: bspNamespace},
1112+
Spec: aigv1a1.BackendSecurityPolicySpec{
1113+
Type: aigv1a1.BackendSecurityPolicyTypeGCPCredentials,
1114+
GCPCredentials: &aigv1a1.BackendSecurityPolicyGCPCredentials{
1115+
ProjectName: "test-project",
1116+
Region: "us-central1",
1117+
CredentialsFile: &aigv1a1.GCPCredentialsFile{
1118+
SecretRef: &gwapiv1.SecretObjectReference{
1119+
Name: gwapiv1.ObjectName(serviceAccountSecretName),
1120+
},
1121+
},
1122+
},
1123+
},
1124+
}
1125+
err := cl.Create(t.Context(), bsp)
1126+
require.NoError(t, err)
1127+
1128+
// Test that rotation fails when the secret doesn't contain the required key.
1129+
res, err := c.rotateCredential(t.Context(), bsp)
1130+
require.Error(t, err)
1131+
require.Contains(t, err.Error(), "missing gcp service account key service_account.json")
1132+
require.Equal(t, ctrl.Result{}, res)
1133+
}
1134+
9181135
func TestGetBSPGeneratedSecretName(t *testing.T) {
9191136
tests := []struct {
9201137
name string

0 commit comments

Comments
 (0)