Skip to content

Commit cd4236f

Browse files
committed
** Added tests for compute -> firewalls, subnets, network
1 parent f257382 commit cd4236f

File tree

3 files changed

+782
-1
lines changed

3 files changed

+782
-1
lines changed
Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
/*
2+
Copyright 2024 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 firewalls
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"net/http"
23+
"testing"
24+
25+
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
26+
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
27+
"github.com/pkg/errors"
28+
"google.golang.org/api/compute/v1"
29+
"google.golang.org/api/googleapi"
30+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
"k8s.io/client-go/kubernetes/scheme"
32+
"k8s.io/utils/ptr"
33+
infrav1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
34+
"sigs.k8s.io/cluster-api-provider-gcp/cloud/scope"
35+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
36+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
37+
)
38+
39+
func init() {
40+
_ = clusterv1.AddToScheme(scheme.Scheme)
41+
_ = infrav1.AddToScheme(scheme.Scheme)
42+
}
43+
44+
var fakeCluster = &clusterv1.Cluster{
45+
ObjectMeta: metav1.ObjectMeta{
46+
Name: "my-cluster",
47+
Namespace: "default",
48+
},
49+
Spec: clusterv1.ClusterSpec{},
50+
}
51+
52+
var fakeGCPCluster = &infrav1.GCPCluster{
53+
ObjectMeta: metav1.ObjectMeta{
54+
Name: "my-cluster",
55+
Namespace: "default",
56+
},
57+
Spec: infrav1.GCPClusterSpec{
58+
Project: "my-proj",
59+
Region: "us-central1",
60+
Network: infrav1.NetworkSpec{
61+
Name: ptr.To("my-network"),
62+
Subnets: infrav1.Subnets{
63+
infrav1.SubnetSpec{
64+
Name: "workers",
65+
CidrBlock: "10.0.0.1/28",
66+
Region: "us-central1",
67+
Purpose: ptr.To[string]("INTERNAL_HTTPS_LOAD_BALANCER"),
68+
},
69+
},
70+
},
71+
},
72+
Status: infrav1.GCPClusterStatus{
73+
Network: infrav1.Network{
74+
FirewallRules: map[string]string{
75+
fmt.Sprintf("allow-%s-healthchecks", "my-cluster"): "test",
76+
},
77+
},
78+
},
79+
}
80+
81+
var fakeGCPClusterSharedVPC = &infrav1.GCPCluster{
82+
ObjectMeta: metav1.ObjectMeta{
83+
Name: "my-cluster",
84+
Namespace: "default",
85+
},
86+
Spec: infrav1.GCPClusterSpec{
87+
Project: "my-proj",
88+
Region: "us-central1",
89+
Network: infrav1.NetworkSpec{
90+
HostProject: ptr.To("my-shared-vpc-project"),
91+
Name: ptr.To("my-network"),
92+
Subnets: infrav1.Subnets{
93+
infrav1.SubnetSpec{
94+
Name: "workers",
95+
CidrBlock: "10.0.0.1/28",
96+
Region: "us-central1",
97+
Purpose: ptr.To[string]("INTERNAL_HTTPS_LOAD_BALANCER"),
98+
},
99+
},
100+
},
101+
},
102+
Status: infrav1.GCPClusterStatus{
103+
Network: infrav1.Network{
104+
FirewallRules: map[string]string{
105+
"my-cluster-apiserver": "test",
106+
"my-cluster-apiintserver": "test",
107+
},
108+
},
109+
},
110+
}
111+
112+
type testCase struct {
113+
name string
114+
scope func() Scope
115+
mockFirewalls *cloud.MockFirewalls
116+
wantErr bool
117+
assert func(ctx context.Context, t testCase) error
118+
}
119+
120+
func TestService_Reconcile(t *testing.T) {
121+
fakec := fake.NewClientBuilder().
122+
WithScheme(scheme.Scheme).
123+
Build()
124+
125+
clusterScope, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
126+
Client: fakec,
127+
Cluster: fakeCluster,
128+
GCPCluster: fakeGCPCluster,
129+
GCPServices: scope.GCPServices{
130+
Compute: &compute.Service{},
131+
},
132+
})
133+
if err != nil {
134+
t.Fatal(err)
135+
}
136+
137+
clusterScopeSharedVpc, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
138+
Client: fakec,
139+
Cluster: fakeCluster,
140+
GCPCluster: fakeGCPClusterSharedVPC,
141+
GCPServices: scope.GCPServices{
142+
Compute: &compute.Service{},
143+
},
144+
})
145+
if err != nil {
146+
t.Fatal(err)
147+
}
148+
149+
tests := []testCase{
150+
{
151+
name: "firewall rule does not exist successful create",
152+
scope: func() Scope { return clusterScope },
153+
mockFirewalls: &cloud.MockFirewalls{
154+
ProjectRouter: &cloud.SingleProjectRouter{ID: "my-proj"},
155+
Objects: map[meta.Key]*cloud.MockFirewallsObj{},
156+
},
157+
assert: func(ctx context.Context, t testCase) error {
158+
key := meta.GlobalKey(fmt.Sprintf("allow-%s-healthchecks", fakeGCPCluster.ObjectMeta.Name))
159+
fwRule, err := t.mockFirewalls.Get(ctx, key)
160+
if err != nil {
161+
return err
162+
}
163+
164+
if _, ok := fakeGCPCluster.Status.Network.FirewallRules[fwRule.Name]; !ok {
165+
return errors.New("firewall rule was created but with wrong values")
166+
}
167+
return nil
168+
},
169+
},
170+
{
171+
name: "firewall rule already exist (should return existing firewall rule)",
172+
scope: func() Scope { return clusterScope },
173+
mockFirewalls: &cloud.MockFirewalls{
174+
ProjectRouter: &cloud.SingleProjectRouter{ID: "my-proj"},
175+
Objects: map[meta.Key]*cloud.MockFirewallsObj{
176+
*meta.GlobalKey(fmt.Sprintf("allow-%s-healthchecks", fakeGCPCluster.ObjectMeta.Name)): {},
177+
},
178+
},
179+
},
180+
{
181+
name: "error getting instance with non 404 error code (should return an error)",
182+
scope: func() Scope { return clusterScope },
183+
mockFirewalls: &cloud.MockFirewalls{
184+
ProjectRouter: &cloud.SingleProjectRouter{ID: "my-proj"},
185+
Objects: map[meta.Key]*cloud.MockFirewallsObj{},
186+
GetHook: func(_ context.Context, _ *meta.Key, _ *cloud.MockFirewalls, _ ...cloud.Option) (bool, *compute.Firewall, error) {
187+
return true, &compute.Firewall{}, &googleapi.Error{Code: http.StatusBadRequest}
188+
},
189+
},
190+
wantErr: true,
191+
},
192+
{
193+
name: "firewall rule creation fails (should return an error)",
194+
scope: func() Scope { return clusterScope },
195+
mockFirewalls: &cloud.MockFirewalls{
196+
ProjectRouter: &cloud.SingleProjectRouter{ID: "my-proj"},
197+
Objects: map[meta.Key]*cloud.MockFirewallsObj{},
198+
InsertError: map[meta.Key]error{
199+
*meta.GlobalKey(fmt.Sprintf("allow-%s-healthchecks", fakeGCPCluster.ObjectMeta.Name)): &googleapi.Error{Code: http.StatusBadRequest},
200+
},
201+
},
202+
wantErr: true,
203+
},
204+
{
205+
name: "firewall return no error using shared vpc",
206+
scope: func() Scope { return clusterScopeSharedVpc },
207+
mockFirewalls: &cloud.MockFirewalls{
208+
ProjectRouter: &cloud.SingleProjectRouter{ID: "my-proj"},
209+
Objects: map[meta.Key]*cloud.MockFirewallsObj{
210+
*meta.GlobalKey(fmt.Sprintf("allow-%s-healthchecks", fakeGCPCluster.ObjectMeta.Name)): {},
211+
},
212+
},
213+
},
214+
}
215+
for _, tt := range tests {
216+
t.Run(tt.name, func(t *testing.T) {
217+
ctx := context.TODO()
218+
s := New(tt.scope())
219+
s.firewalls = tt.mockFirewalls
220+
err := s.Reconcile(ctx)
221+
if (err != nil) != tt.wantErr {
222+
t.Errorf("Service.Reconcile() error = %v, wantErr %v", err, tt.wantErr)
223+
return
224+
}
225+
if tt.assert != nil {
226+
err = tt.assert(ctx, tt)
227+
if err != nil {
228+
t.Errorf("firewall rule was not created as expected: %v", err)
229+
return
230+
}
231+
}
232+
})
233+
}
234+
}
235+
236+
func TestService_Delete(t *testing.T) {
237+
fakec := fake.NewClientBuilder().
238+
WithScheme(scheme.Scheme).
239+
Build()
240+
241+
clusterScope, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
242+
Client: fakec,
243+
Cluster: fakeCluster,
244+
GCPCluster: fakeGCPCluster,
245+
GCPServices: scope.GCPServices{
246+
Compute: &compute.Service{},
247+
},
248+
})
249+
if err != nil {
250+
t.Fatal(err)
251+
}
252+
253+
clusterScopeSharedVpc, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
254+
Client: fakec,
255+
Cluster: fakeCluster,
256+
GCPCluster: fakeGCPClusterSharedVPC,
257+
GCPServices: scope.GCPServices{
258+
Compute: &compute.Service{},
259+
},
260+
})
261+
if err != nil {
262+
t.Fatal(err)
263+
}
264+
265+
tests := []testCase{
266+
{
267+
name: "firewall rule does not exist, should do nothing",
268+
scope: func() Scope { return clusterScope },
269+
mockFirewalls: &cloud.MockFirewalls{
270+
ProjectRouter: &cloud.SingleProjectRouter{ID: "my-proj"},
271+
DeleteError: map[meta.Key]error{
272+
*meta.GlobalKey(fmt.Sprintf("allow-%s-healthchecks", fakeGCPCluster.ObjectMeta.Name)): &googleapi.Error{Code: http.StatusNotFound},
273+
},
274+
},
275+
},
276+
{
277+
name: "error deleting firewall rule, should return error",
278+
scope: func() Scope { return clusterScope },
279+
mockFirewalls: &cloud.MockFirewalls{
280+
ProjectRouter: &cloud.SingleProjectRouter{ID: "my-proj"},
281+
DeleteError: map[meta.Key]error{
282+
*meta.GlobalKey(fmt.Sprintf("allow-%s-healthchecks", fakeGCPCluster.ObjectMeta.Name)): &googleapi.Error{Code: http.StatusBadRequest},
283+
},
284+
},
285+
wantErr: true,
286+
},
287+
{
288+
name: "firewall rule deletion with shared vpc",
289+
scope: func() Scope { return clusterScopeSharedVpc },
290+
mockFirewalls: &cloud.MockFirewalls{
291+
ProjectRouter: &cloud.SingleProjectRouter{ID: "my-proj"},
292+
DeleteError: map[meta.Key]error{
293+
*meta.GlobalKey(fmt.Sprintf("allow-%s-healthchecks", *fakeGCPCluster.Spec.Network.Name)): &googleapi.Error{Code: http.StatusNotFound},
294+
},
295+
},
296+
},
297+
}
298+
for _, tt := range tests {
299+
t.Run(tt.name, func(t *testing.T) {
300+
ctx := context.TODO()
301+
s := New(tt.scope())
302+
s.firewalls = tt.mockFirewalls
303+
err := s.Delete(ctx)
304+
if (err != nil) != tt.wantErr {
305+
t.Errorf("Service.Delete() error = %v, wantErr %v", err, tt.wantErr)
306+
return
307+
}
308+
})
309+
}
310+
}

0 commit comments

Comments
 (0)