Skip to content

Commit 880481c

Browse files
committed
tests: e2e tests implemented
Signed-off-by: Bharath Nallapeta <[email protected]>
1 parent 7141023 commit 880481c

File tree

14 files changed

+84
-163
lines changed

14 files changed

+84
-163
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ e2e-templates: $(addprefix $(E2E_NO_ARTIFACT_TEMPLATES_DIR)/, \
188188
cluster-template-flatcar-sysext.yaml \
189189
cluster-template-no-bastion.yaml \
190190
cluster-template-health-monitor.yaml \
191-
cluster-template-capi-v1beta1.yaml)
191+
cluster-template-capi-v1beta1.yaml \
192+
cluster-template-cluster-identity.yaml)
192193
# Currently no templates that require CI artifacts
193194
# $(addprefix $(E2E_TEMPLATES_DIR)/, add-templates-here.yaml) \
194195

config/crd/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ labels:
88
# It should be run by config/
99
resources:
1010
- bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml
11+
- bases/infrastructure.cluster.x-k8s.io_openstackclusteridentities.yaml
1112
- bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml
1213
- bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml
1314
- bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml

controllers/openstackcluster_controller_test.go

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -180,85 +180,71 @@ var _ = Describe("OpenStackCluster controller", func() {
180180
})
181181

182182
It("should successfully create OpenStackCluster with valid identityRef", func() {
183+
testCluster.Spec.IdentityRef = infrav1.OpenStackIdentityReference{
184+
Name: "creds",
185+
CloudName: "openstack",
186+
// Type should default to "Secret"
187+
}
183188
err := k8sClient.Create(ctx, testCluster)
184189
Expect(err).To(BeNil())
185190
err = k8sClient.Create(ctx, capiCluster)
186191
Expect(err).To(BeNil())
187192

188-
c := &infrav1.OpenStackCluster{
189-
ObjectMeta: metav1.ObjectMeta{
190-
Name: "cluster-valid-identity",
191-
Namespace: testNamespace,
192-
},
193-
Spec: infrav1.OpenStackClusterSpec{
194-
IdentityRef: infrav1.OpenStackIdentityReference{
195-
Name: "creds",
196-
CloudName: "openstack",
197-
// Type should default to "Secret"
198-
},
199-
},
200-
}
201-
err = k8sClient.Create(ctx, c)
202-
Expect(err).To(Succeed())
203-
204193
// Verify the object was created and Type was defaulted
205194
created := &infrav1.OpenStackCluster{}
206-
err = k8sClient.Get(ctx, client.ObjectKey{Name: c.Name, Namespace: c.Namespace}, created)
195+
err = k8sClient.Get(ctx, client.ObjectKey{Name: testCluster.Name, Namespace: testCluster.Namespace}, created)
207196
Expect(err).To(Succeed())
208197
Expect(created.Spec.IdentityRef.Type).To(Equal("Secret"))
209198
Expect(created.Spec.IdentityRef.Name).To(Equal("creds"))
210199
Expect(created.Spec.IdentityRef.CloudName).To(Equal("openstack"))
211200
})
212201

213202
It("should successfully create OpenStackCluster with ClusterIdentity type", func() {
203+
testCluster.Spec.IdentityRef = infrav1.OpenStackIdentityReference{
204+
Type: "ClusterIdentity",
205+
Name: "global-creds",
206+
CloudName: "openstack",
207+
Region: "RegionOne",
208+
}
214209
err := k8sClient.Create(ctx, testCluster)
215210
Expect(err).To(BeNil())
216211
err = k8sClient.Create(ctx, capiCluster)
217212
Expect(err).To(BeNil())
218213

219-
c := &infrav1.OpenStackCluster{
220-
ObjectMeta: metav1.ObjectMeta{
221-
Name: "cluster-clusteridentity-type",
222-
Namespace: testNamespace,
223-
},
224-
Spec: infrav1.OpenStackClusterSpec{
225-
IdentityRef: infrav1.OpenStackIdentityReference{
226-
Type: "ClusterIdentity",
227-
Name: "global-creds",
228-
CloudName: "openstack",
229-
Region: "RegionOne",
230-
},
231-
},
232-
}
233-
err = k8sClient.Create(ctx, c)
234-
Expect(err).To(Succeed())
235-
236214
// Verify all fields are preserved
237215
created := &infrav1.OpenStackCluster{}
238-
err = k8sClient.Get(ctx, client.ObjectKey{Name: c.Name, Namespace: c.Namespace}, created)
216+
err = k8sClient.Get(ctx, client.ObjectKey{Name: testCluster.Name, Namespace: testCluster.Namespace}, created)
239217
Expect(err).To(Succeed())
240218
Expect(created.Spec.IdentityRef.Type).To(Equal("ClusterIdentity"))
241219
Expect(created.Spec.IdentityRef.Name).To(Equal("global-creds"))
242220
Expect(created.Spec.IdentityRef.CloudName).To(Equal("openstack"))
243221
Expect(created.Spec.IdentityRef.Region).To(Equal("RegionOne"))
244222
})
245223

246-
It("should accept cluster and default identityRef.type to Secret", func() {
247-
testCluster.Spec = infrav1.OpenStackClusterSpec{
248-
IdentityRef: infrav1.OpenStackIdentityReference{
249-
Name: "creds",
250-
CloudName: "openstack",
251-
// Type omitted -> should default to Secret
252-
},
224+
It("should fail when namespace is denied access to ClusterIdentity", func() {
225+
testCluster.SetName("identity-access-denied")
226+
testCluster.Spec.IdentityRef = infrav1.OpenStackIdentityReference{
227+
Type: "ClusterIdentity",
228+
Name: "test-cluster-identity",
229+
CloudName: "openstack",
253230
}
231+
254232
err := k8sClient.Create(ctx, testCluster)
255233
Expect(err).To(BeNil())
256234
err = k8sClient.Create(ctx, capiCluster)
257235
Expect(err).To(BeNil())
258236

259-
fetched := &infrav1.OpenStackCluster{}
260-
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: testClusterName, Namespace: testNamespace}, fetched)).To(Succeed())
261-
Expect(fetched.Spec.IdentityRef.Type).To(Equal("Secret"))
237+
identityAccessErr := &scope.IdentityAccessDeniedError{
238+
IdentityName: "test-cluster-identity",
239+
RequesterNamespace: testNamespace,
240+
}
241+
mockScopeFactory.SetClientScopeCreateError(identityAccessErr)
242+
243+
req := createRequestFromOSCluster(testCluster)
244+
result, err := reconciler.Reconcile(ctx, req)
245+
246+
Expect(err).To(MatchError(identityAccessErr))
247+
Expect(result).To(Equal(reconcile.Result{}))
262248
})
263249

264250
It("should reject updates that modify identityRef.region (immutable)", func() {

docs/book/src/topics/openstack-cluster-identity.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ spec:
6969
cloudName: openstack
7070
```
7171

72-
## Using a Secret directly (legacy/default)
72+
## Using a Secret directly (default)
7373
- If you don’t need cross-namespace identities, use a Secret in the same namespace.
7474
```yaml
7575
spec:

pkg/scope/provider.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,10 @@ func (f *providerScopeFactory) NewClientScopeFromObject(ctx context.Context, ctr
120120
}
121121

122122
// Read cloud from the resolved secret using the provided cloudName
123-
c, cert, err := getCloudFromSecret(ctx, ctrlClient, secretNamespace, secretName, identityRef.CloudName)
123+
cloud, caCert, err := getCloudFromSecret(ctx, ctrlClient, secretNamespace, secretName, identityRef.CloudName)
124124
if err != nil {
125125
return nil, err
126126
}
127-
cloud = c
128-
caCert = cert
129127

130128
if caCert == nil {
131129
caCert = defaultCACert

pkg/scope/provider_resolution_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ var (
6767
testDefaultCloudsYAML = []byte("clouds: { default: {} }\n")
6868
)
6969

70+
// ensureSchemes creates a runtime scheme with all required API types for testing.
7071
func ensureSchemes(t *testing.T) *runtime.Scheme {
7172
t.Helper()
7273
local := runtime.NewScheme()
@@ -82,6 +83,7 @@ func ensureSchemes(t *testing.T) *runtime.Scheme {
8283
return local
8384
}
8485

86+
// createResTestSecret creates a test Secret with the given namespace, name, and data.
8587
func createResTestSecret(namespace, name string, data map[string][]byte) *corev1.Secret {
8688
secret := &corev1.Secret{}
8789
secret.Namespace = namespace
@@ -90,13 +92,15 @@ func createResTestSecret(namespace, name string, data map[string][]byte) *corev1
9092
return secret
9193
}
9294

95+
// createTestNamespace creates a test Namespace with the given name and labels.
9396
func createTestNamespace(name string, labels map[string]string) *corev1.Namespace {
9497
ns := &corev1.Namespace{}
9598
ns.Name = name
9699
ns.Labels = labels
97100
return ns
98101
}
99102

103+
// createTestClusterIdentity creates a test OpenStackClusterIdentity with the given name and namespace selector.
100104
func createTestClusterIdentity(name string, selector *metav1.LabelSelector) *infrav1alpha1.OpenStackClusterIdentity {
101105
identity := &infrav1alpha1.OpenStackClusterIdentity{}
102106
identity.Name = name
@@ -108,7 +112,7 @@ func createTestClusterIdentity(name string, selector *metav1.LabelSelector) *inf
108112
return identity
109113
}
110114

111-
// newFakeClient creates a fake client with the provided scheme and objects.
115+
// newFakeClient creates a fake Kubernetes client with the provided scheme and objects.
112116
func newFakeClient(sch *runtime.Scheme, objs ...client.Object) client.Client {
113117
return fake.NewClientBuilder().WithScheme(sch).WithObjects(objs...).Build()
114118
}
@@ -129,6 +133,7 @@ func assertResolutionReached(t *testing.T, err error) {
129133
}
130134
}
131135

136+
// assertDenied verifies that the error is an IdentityAccessDeniedError.
132137
func assertDenied(t *testing.T, err error) {
133138
t.Helper()
134139
var denied *IdentityAccessDeniedError
@@ -137,6 +142,7 @@ func assertDenied(t *testing.T, err error) {
137142
}
138143
}
139144

145+
// assertNotDenied verifies that the error is NOT an IdentityAccessDeniedError.
140146
func assertNotDenied(t *testing.T, err error) {
141147
t.Helper()
142148
var denied *IdentityAccessDeniedError
@@ -145,6 +151,7 @@ func assertNotDenied(t *testing.T, err error) {
145151
}
146152
}
147153

154+
// TestNewClientScopeFromObject_Resolution tests credential resolution logic for both Secret and ClusterIdentity paths.
148155
func TestNewClientScopeFromObject_Resolution(t *testing.T) {
149156
t.Parallel()
150157
localScheme := ensureSchemes(t)

pkg/scope/provider_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var (
4747
testCACert = []byte("-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n")
4848
)
4949

50+
// buildCoreScheme creates a runtime scheme with core Kubernetes API types for testing.
5051
func buildCoreScheme(t *testing.T) *runtime.Scheme {
5152
t.Helper()
5253
sch := runtime.NewScheme()
@@ -56,6 +57,7 @@ func buildCoreScheme(t *testing.T) *runtime.Scheme {
5657
return sch
5758
}
5859

60+
// createTestSecret creates a test Secret in the test namespace with the given name and data.
5961
func createTestSecret(name string, data map[string][]byte) *corev1.Secret {
6062
secret := &corev1.Secret{}
6163
secret.Namespace = testNamespace
@@ -64,6 +66,7 @@ func createTestSecret(name string, data map[string][]byte) *corev1.Secret {
6466
return secret
6567
}
6668

69+
// TestGetCloudFromSecret_SuccessWithCACert tests successful cloud retrieval when CA certificate is present.
6770
func TestGetCloudFromSecret_SuccessWithCACert(t *testing.T) {
6871
t.Parallel()
6972
ctx := context.Background()
@@ -88,6 +91,7 @@ func TestGetCloudFromSecret_SuccessWithCACert(t *testing.T) {
8891
}
8992
}
9093

94+
// TestGetCloudFromSecret_SuccessWithoutCACert tests successful cloud retrieval when CA certificate is not present.
9195
func TestGetCloudFromSecret_SuccessWithoutCACert(t *testing.T) {
9296
t.Parallel()
9397
ctx := context.Background()
@@ -111,6 +115,7 @@ func TestGetCloudFromSecret_SuccessWithoutCACert(t *testing.T) {
111115
}
112116
}
113117

118+
// TestGetCloudFromSecret_MissingSecret tests error handling when the secret does not exist.
114119
func TestGetCloudFromSecret_MissingSecret(t *testing.T) {
115120
t.Parallel()
116121
ctx := context.Background()
@@ -123,6 +128,7 @@ func TestGetCloudFromSecret_MissingSecret(t *testing.T) {
123128
}
124129
}
125130

131+
// TestGetCloudFromSecret_MissingCloudsKey tests error handling when the clouds.yaml key is missing from the secret.
126132
func TestGetCloudFromSecret_MissingCloudsKey(t *testing.T) {
127133
t.Parallel()
128134
ctx := context.Background()
@@ -141,6 +147,7 @@ func TestGetCloudFromSecret_MissingCloudsKey(t *testing.T) {
141147
}
142148
}
143149

150+
// TestGetCloudFromSecret_EmptyCloudName tests error handling when cloudName is empty.
144151
func TestGetCloudFromSecret_EmptyCloudName(t *testing.T) {
145152
t.Parallel()
146153
ctx := context.Background()
@@ -158,6 +165,7 @@ func TestGetCloudFromSecret_EmptyCloudName(t *testing.T) {
158165
}
159166
}
160167

168+
// TestGetCloudFromSecret_InvalidCloudName tests behavior when cloudName does not exist in clouds.yaml.
161169
func TestGetCloudFromSecret_InvalidCloudName(t *testing.T) {
162170
t.Parallel()
163171
ctx := context.Background()

test/e2e/data/e2e_conf.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ providers:
170170
- sourcePath: "../data/shared/provider/metadata.yaml"
171171
- sourcePath: "./infrastructure-openstack-no-artifact/cluster-template.yaml"
172172
- sourcePath: "./infrastructure-openstack-no-artifact/cluster-template-without-lb.yaml"
173+
- sourcePath: "./infrastructure-openstack-no-artifact/cluster-template-cluster-identity.yaml"
173174
replacements:
174175
- old: gcr.io/k8s-staging-capi-openstack/capi-openstack-controller:dev
175176
new: gcr.io/k8s-staging-capi-openstack/capi-openstack-controller:e2e

test/e2e/data/kustomize/cluster-identity-denied/kustomization.yaml

Lines changed: 0 additions & 21 deletions
This file was deleted.

test/e2e/data/kustomize/cluster-identity-denied/openstackclusteridentity.yaml

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)