Skip to content

Commit fe619b4

Browse files
committed
ROSANetwork: tests
1 parent 56760b6 commit fe619b4

File tree

2 files changed

+353
-0
lines changed

2 files changed

+353
-0
lines changed
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
/*
2+
Copyright The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package controllers
15+
16+
import (
17+
"context"
18+
"fmt"
19+
"testing"
20+
21+
awsSdk "github.com/aws/aws-sdk-go-v2/aws"
22+
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
23+
cloudformationtypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
24+
"github.com/aws/aws-sdk-go-v2/service/ec2"
25+
ec2Types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
26+
. "github.com/onsi/gomega"
27+
rosaAWSClient "github.com/openshift/rosa/pkg/aws"
28+
rosaMocks "github.com/openshift/rosa/pkg/aws/mocks"
29+
"github.com/sirupsen/logrus"
30+
gomock "go.uber.org/mock/gomock"
31+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/apimachinery/pkg/types"
33+
ctrl "sigs.k8s.io/controller-runtime"
34+
35+
expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
36+
)
37+
38+
func TestROSANetworkReconciler_Reconcile(t *testing.T) {
39+
g := NewWithT(t)
40+
41+
rosaNetwork := &expinfrav1.ROSANetwork{
42+
ObjectMeta: metav1.ObjectMeta{
43+
Name: "test-rosa-network",
44+
Namespace: "test-namespace"},
45+
Spec: expinfrav1.ROSANetworkSpec{},
46+
}
47+
48+
reconciler := &ROSANetworkReconciler{
49+
Client: testEnv.Client,
50+
}
51+
52+
req := ctrl.Request{}
53+
req.NamespacedName = types.NamespacedName{Name: rosaNetwork.Name, Namespace: rosaNetwork.Namespace}
54+
_, errReconcile := reconciler.Reconcile(ctx, req)
55+
56+
g.Expect(errReconcile).ToNot(HaveOccurred())
57+
}
58+
59+
func TestROSANetworkReconciler_updateROSANetworkResources(t *testing.T) {
60+
g := NewWithT(t)
61+
mockCtrl := gomock.NewController(t)
62+
ctx := context.TODO()
63+
64+
rosaNetwork := &expinfrav1.ROSANetwork{
65+
ObjectMeta: metav1.ObjectMeta{
66+
Name: "test-rosa-network",
67+
Namespace: "test-namespace",
68+
},
69+
Spec: expinfrav1.ROSANetworkSpec{},
70+
Status: expinfrav1.ROSANetworkStatus{},
71+
}
72+
73+
t.Run("Handle cloudformation client error", func(t *testing.T) {
74+
_, mockCFClient, reconciler := createMocks(mockCtrl)
75+
76+
describeStackResourcesOutput := &cloudformation.DescribeStackResourcesOutput{}
77+
clientErr := fmt.Errorf("test-error")
78+
79+
mockCFClient.EXPECT().DescribeStackResources(gomock.Any(), gomock.Any(), gomock.Any()).
80+
DoAndReturn(func(_ context.Context, _ *cloudformation.DescribeStackResourcesInput, _ ...func(*cloudformation.Options)) (*cloudformation.DescribeStackResourcesOutput, error) {
81+
return describeStackResourcesOutput, clientErr
82+
}).Times(1)
83+
84+
err := reconciler.updateROSANetworkResources(ctx, rosaNetwork)
85+
g.Expect(err).To(HaveOccurred())
86+
g.Expect(len(rosaNetwork.Status.Resources)).To(Equal(0))
87+
})
88+
89+
t.Run("Update ROSANetwork.Status.Resources", func(t *testing.T) {
90+
_, mockCFClient, reconciler := createMocks(mockCtrl)
91+
92+
logicalResourceID := "logical-resource-id"
93+
resourceStatus := cloudformationtypes.ResourceStatusCreateComplete
94+
resourceType := "resource-type"
95+
resourceStatusReason := "resource-status-reason"
96+
physicalResourceID := "physical-resource-id"
97+
98+
describeStackResourcesOutput := &cloudformation.DescribeStackResourcesOutput{
99+
StackResources: []cloudformationtypes.StackResource{
100+
{
101+
LogicalResourceId: &logicalResourceID,
102+
ResourceStatus: resourceStatus,
103+
ResourceType: &resourceType,
104+
ResourceStatusReason: &resourceStatusReason,
105+
PhysicalResourceId: &physicalResourceID,
106+
},
107+
},
108+
}
109+
110+
mockCFClient.EXPECT().DescribeStackResources(gomock.Any(), gomock.Any(), gomock.Any()).
111+
DoAndReturn(func(_ context.Context, _ *cloudformation.DescribeStackResourcesInput, _ ...func(*cloudformation.Options)) (*cloudformation.DescribeStackResourcesOutput, error) {
112+
return describeStackResourcesOutput, nil
113+
}).Times(1)
114+
115+
err := reconciler.updateROSANetworkResources(ctx, rosaNetwork)
116+
g.Expect(err).ToNot(HaveOccurred())
117+
g.Expect(rosaNetwork.Status.Resources[0].LogicalID).To(Equal(logicalResourceID))
118+
g.Expect(rosaNetwork.Status.Resources[0].Status).To(Equal(string(resourceStatus)))
119+
g.Expect(rosaNetwork.Status.Resources[0].ResourceType).To(Equal(resourceType))
120+
g.Expect(rosaNetwork.Status.Resources[0].Reason).To(Equal(resourceStatusReason))
121+
g.Expect(rosaNetwork.Status.Resources[0].PhysicalID).To(Equal(physicalResourceID))
122+
})
123+
}
124+
125+
func TestROSANetworkReconciler_parseSubnets(t *testing.T) {
126+
g := NewWithT(t)
127+
mockCtrl := gomock.NewController(t)
128+
129+
subnet1Id := "subnet1-physical-id"
130+
subnet2Id := "subnet2-physical-id"
131+
132+
rosaNetwork := &expinfrav1.ROSANetwork{
133+
ObjectMeta: metav1.ObjectMeta{
134+
Name: "test-rosa-network",
135+
Namespace: "test-namespace",
136+
},
137+
Spec: expinfrav1.ROSANetworkSpec{},
138+
Status: expinfrav1.ROSANetworkStatus{
139+
Resources: []expinfrav1.CFResource{
140+
{
141+
ResourceType: "AWS::EC2::Subnet",
142+
LogicalID: "SubnetPrivate",
143+
PhysicalID: subnet1Id,
144+
Status: "subnet1-status",
145+
Reason: "subnet1-reason",
146+
},
147+
{
148+
ResourceType: "AWS::EC2::Subnet",
149+
LogicalID: "SubnetPublic",
150+
PhysicalID: subnet2Id,
151+
Status: "subnet2-status",
152+
Reason: "subnet2-reason",
153+
},
154+
{
155+
ResourceType: "bogus-type",
156+
LogicalID: "bogus-logical-id",
157+
PhysicalID: "bugus-physical-id",
158+
Status: "bogus-status",
159+
Reason: "bogus-reason",
160+
},
161+
},
162+
},
163+
}
164+
165+
t.Run("Handle EC2 client error", func(t *testing.T) {
166+
mockEC2Client, _, reconciler := createMocks(mockCtrl)
167+
168+
describeSubnetsOutput := &ec2.DescribeSubnetsOutput{}
169+
170+
mockEC2Client.EXPECT().DescribeSubnets(gomock.Any(), gomock.Any(), gomock.Any()).
171+
DoAndReturn(func(_ context.Context, _ *ec2.DescribeSubnetsInput, _ ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) {
172+
return describeSubnetsOutput, fmt.Errorf("test-error")
173+
}).Times(1)
174+
175+
err := reconciler.parseSubnets(rosaNetwork)
176+
g.Expect(err).To(HaveOccurred())
177+
g.Expect(len(rosaNetwork.Status.Subnets)).To(Equal(0))
178+
})
179+
180+
t.Run("Update ROSANetwork.Status.Subnets", func(t *testing.T) {
181+
mockEC2Client, _, reconciler := createMocks(mockCtrl)
182+
183+
az := "az01"
184+
185+
describeSubnetsOutput := &ec2.DescribeSubnetsOutput{
186+
Subnets: []ec2Types.Subnet{
187+
{
188+
AvailabilityZone: &az,
189+
},
190+
},
191+
}
192+
193+
mockEC2Client.EXPECT().DescribeSubnets(gomock.Any(), gomock.Any(), gomock.Any()).
194+
DoAndReturn(func(_ context.Context, _ *ec2.DescribeSubnetsInput, _ ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) {
195+
return describeSubnetsOutput, nil
196+
}).Times(2)
197+
198+
err := reconciler.parseSubnets(rosaNetwork)
199+
g.Expect(err).ToNot(HaveOccurred())
200+
g.Expect(rosaNetwork.Status.Subnets[0].AvailabilityZone).To(Equal(az))
201+
g.Expect(rosaNetwork.Status.Subnets[0].PrivateSubnet).To(Equal(subnet1Id))
202+
g.Expect(rosaNetwork.Status.Subnets[0].PublicSubnet).To(Equal(subnet2Id))
203+
})
204+
}
205+
206+
func createMocks(mockCtrl *gomock.Controller) (*rosaMocks.MockEc2ApiClient, *rosaMocks.MockCloudFormationApiClient, *ROSANetworkReconciler) {
207+
mockEC2Client := rosaMocks.NewMockEc2ApiClient(mockCtrl)
208+
mockCFClient := rosaMocks.NewMockCloudFormationApiClient(mockCtrl)
209+
awsClient := rosaAWSClient.New(
210+
awsSdk.Config{},
211+
rosaAWSClient.NewLoggerWrapper(logrus.New(), nil),
212+
rosaMocks.NewMockIamApiClient(mockCtrl),
213+
mockEC2Client,
214+
rosaMocks.NewMockOrganizationsApiClient(mockCtrl),
215+
rosaMocks.NewMockS3ApiClient(mockCtrl),
216+
rosaMocks.NewMockSecretsManagerApiClient(mockCtrl),
217+
rosaMocks.NewMockStsApiClient(mockCtrl),
218+
mockCFClient,
219+
rosaMocks.NewMockServiceQuotasApiClient(mockCtrl),
220+
rosaMocks.NewMockServiceQuotasApiClient(mockCtrl),
221+
&rosaAWSClient.AccessKey{},
222+
false,
223+
)
224+
225+
reconciler := &ROSANetworkReconciler{
226+
Client: testEnv.Client,
227+
awsClient: awsClient,
228+
}
229+
230+
return mockEC2Client, mockCFClient, reconciler
231+
}

pkg/cloud/scope/rosanetwork_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
Copyright The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package scope
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/gomega"
23+
corev1 "k8s.io/api/core/v1"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/klog/v2"
27+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
28+
29+
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
30+
expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
31+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
32+
"sigs.k8s.io/cluster-api-provider-aws/v2/util/system"
33+
)
34+
35+
func TestNewROSANetworkScope(t *testing.T) {
36+
g := NewGomegaWithT(t)
37+
38+
scheme := runtime.NewScheme()
39+
corev1.AddToScheme(scheme)
40+
infrav1.AddToScheme(scheme)
41+
expinfrav1.AddToScheme(scheme)
42+
43+
clusterControllerIdentity := &infrav1.AWSClusterControllerIdentity{
44+
TypeMeta: metav1.TypeMeta{
45+
Kind: string(infrav1.ControllerIdentityKind),
46+
},
47+
ObjectMeta: metav1.ObjectMeta{
48+
Name: "default",
49+
},
50+
Spec: infrav1.AWSClusterControllerIdentitySpec{
51+
AWSClusterIdentitySpec: infrav1.AWSClusterIdentitySpec{
52+
AllowedNamespaces: &infrav1.AllowedNamespaces{},
53+
},
54+
},
55+
}
56+
57+
staticSecret := &corev1.Secret{
58+
ObjectMeta: metav1.ObjectMeta{
59+
Name: "static-secret",
60+
Namespace: system.GetManagerNamespace(),
61+
},
62+
Data: map[string][]byte{
63+
"AccessKeyID": []byte("access-key-id"),
64+
"SecretAccessKey": []byte("secret-access-key"),
65+
},
66+
}
67+
68+
clusterStaticIdentity := &infrav1.AWSClusterStaticIdentity{
69+
ObjectMeta: metav1.ObjectMeta{
70+
Name: "cluster-static-identity",
71+
},
72+
Spec: infrav1.AWSClusterStaticIdentitySpec{
73+
SecretRef: "static-secret",
74+
AWSClusterIdentitySpec: infrav1.AWSClusterIdentitySpec{
75+
AllowedNamespaces: &infrav1.AllowedNamespaces{},
76+
},
77+
},
78+
}
79+
80+
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(clusterControllerIdentity, staticSecret, clusterStaticIdentity).Build()
81+
82+
rosaNetwork := expinfrav1.ROSANetwork{
83+
TypeMeta: metav1.TypeMeta{
84+
Kind: "ROSANetwork",
85+
APIVersion: "v1beta2",
86+
},
87+
ObjectMeta: metav1.ObjectMeta{
88+
Name: "test-rosa-net",
89+
Namespace: "test-namespace",
90+
},
91+
Spec: expinfrav1.ROSANetworkSpec{
92+
IdentityRef: &infrav1.AWSIdentityReference{
93+
Name: "default",
94+
Kind: "AWSClusterControllerIdentity",
95+
},
96+
},
97+
Status: expinfrav1.ROSANetworkStatus{},
98+
}
99+
100+
rosaNetScopeParams := ROSANetworkScopeParams{
101+
Client: fakeClient,
102+
ControllerName: "test-rosanet-controller",
103+
Logger: logger.NewLogger(klog.Background()),
104+
ROSANetwork: &rosaNetwork,
105+
}
106+
107+
rosaNetScope, err := NewROSANetworkScope(rosaNetScopeParams)
108+
g.Expect(err).NotTo(HaveOccurred())
109+
g.Expect(rosaNetScope.ControllerName()).To(Equal("test-rosanet-controller"))
110+
g.Expect(rosaNetScope.InfraCluster()).To(Equal(&rosaNetwork))
111+
g.Expect(rosaNetScope.InfraClusterName()).To(Equal("test-rosa-net"))
112+
g.Expect(rosaNetScope.Namespace()).To(Equal("test-namespace"))
113+
g.Expect(rosaNetScope.IdentityRef()).To(Equal(rosaNetwork.Spec.IdentityRef))
114+
g.Expect(rosaNetScope.Session()).ToNot(BeNil())
115+
116+
// AWSClusterStaticIdentity
117+
rosaNetwork.Spec.IdentityRef.Name = "cluster-static-identity"
118+
rosaNetwork.Spec.IdentityRef.Kind = "AWSClusterStaticIdentity"
119+
rosaNetScope, err = NewROSANetworkScope(rosaNetScopeParams)
120+
g.Expect(err).NotTo(HaveOccurred())
121+
g.Expect(rosaNetScope.Session()).ToNot(BeNil())
122+
}

0 commit comments

Comments
 (0)