Skip to content

Commit 7211b96

Browse files
committed
feat: changes to for gke e2e testing
Adds GKE e2e tests. These tests will need to be expanded over time. Adds a result of adding these tests various other changes where made including: - Adding docs about the permissions required - Removed initial node count of the AWSManagedMachinePool - Added validation to error if replicas isn't a multiple of 3 for a region based cluster (which all gke clusters are initially) Signed-off-by: Richard Case <[email protected]>
1 parent 7edcd71 commit 7211b96

34 files changed

+1118
-153
lines changed

cloud/defaults.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
/*
22
Copyright 2021 The Kubernetes Authors.
3-
43
Licensed under the Apache License, Version 2.0 (the "License");
54
you may not use this file except in compliance with the License.
65
You may obtain a copy of the License at
7-
86
http://www.apache.org/licenses/LICENSE-2.0
9-
107
Unless required by applicable law or agreed to in writing, software
118
distributed under the License is distributed on an "AS IS" BASIS,
129
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,6 +14,6 @@ limitations under the License.
1714
package cloud
1815

1916
const (
20-
// ProviderIDPrefix is the gce provider id prefix.
21-
ProviderIDPrefix = "gce://"
17+
// DefaultNumRegionsPerZone is the default number of zones per region.
18+
DefaultNumRegionsPerZone = 3
2219
)

cloud/providerid/doc.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
Copyright 2023 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 providerid implements functionality for creating kubernetes provider ids for nodes.
18+
package providerid

cloud/providerid/providerid.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Copyright 2023 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 providerid
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
"path"
23+
24+
"sigs.k8s.io/cluster-api-provider-gcp/util/resourceurl"
25+
)
26+
27+
const (
28+
// Prefix is the gce provider id prefix.
29+
Prefix = "gce://"
30+
)
31+
32+
// ProviderID represents the id for a GCP cluster.
33+
type ProviderID interface {
34+
Project() string
35+
Location() string
36+
Name() string
37+
fmt.Stringer
38+
}
39+
40+
// NewFromResourceURL creates a provider from a GCP resource url.
41+
func NewFromResourceURL(url string) (ProviderID, error) {
42+
resourceURL, err := resourceurl.Parse(url)
43+
if err != nil {
44+
return nil, fmt.Errorf("parsing resource url %s: %w", url, err)
45+
}
46+
47+
return New(resourceURL.Project, resourceURL.Location, resourceURL.Name)
48+
}
49+
50+
// New creates a new provider id.
51+
func New(project, location, name string) (ProviderID, error) {
52+
if project == "" {
53+
return nil, errors.New("project required for provider id")
54+
}
55+
if location == "" {
56+
return nil, errors.New("location required for provider id")
57+
}
58+
if name == "" {
59+
return nil, errors.New("name required for provider id")
60+
}
61+
62+
return &providerID{
63+
project: project,
64+
location: location,
65+
name: name,
66+
}, nil
67+
}
68+
69+
type providerID struct {
70+
project string
71+
location string
72+
name string
73+
}
74+
75+
func (p *providerID) Project() string {
76+
return p.project
77+
}
78+
79+
func (p *providerID) Location() string {
80+
return p.location
81+
}
82+
83+
func (p *providerID) Name() string {
84+
return p.name
85+
}
86+
87+
func (p *providerID) String() string {
88+
return Prefix + path.Join(p.project, p.location, p.name)
89+
}

cloud/providerid/providerid_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
Copyright 2023 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 providerid_test
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/gomega"
23+
"sigs.k8s.io/cluster-api-provider-gcp/cloud/providerid"
24+
)
25+
26+
func TestProviderID_New(t *testing.T) {
27+
RegisterTestingT(t)
28+
29+
testCases := []struct {
30+
testname string
31+
project string
32+
location string
33+
name string
34+
expectedProviderID string
35+
expectError bool
36+
}{
37+
{
38+
testname: "no project, should fail",
39+
project: "",
40+
location: "eu-west4",
41+
name: "vm1",
42+
expectError: true,
43+
},
44+
{
45+
testname: "no location, should fail",
46+
project: "proj1",
47+
location: "",
48+
name: "vm1",
49+
expectError: true,
50+
},
51+
{
52+
testname: "no name, should fail",
53+
project: "proj1",
54+
location: "eu-west4",
55+
name: "",
56+
expectError: true,
57+
},
58+
{
59+
testname: "with all details, should pass",
60+
project: "proj1",
61+
location: "eu-west4",
62+
name: "vm1",
63+
expectError: false,
64+
expectedProviderID: "gce://proj1/eu-west4/vm1",
65+
},
66+
}
67+
68+
for _, tc := range testCases {
69+
t.Run(tc.testname, func(t *testing.T) {
70+
providerID, err := providerid.New(tc.project, tc.location, tc.name)
71+
72+
if tc.expectError {
73+
Expect(err).To(HaveOccurred())
74+
} else {
75+
Expect(err).NotTo(HaveOccurred())
76+
Expect(providerID.String()).To(Equal(tc.expectedProviderID))
77+
}
78+
})
79+
}
80+
}
81+
82+
func TestProviderID_NewFromResourceURL(t *testing.T) {
83+
RegisterTestingT(t)
84+
85+
testCases := []struct {
86+
testname string
87+
resourceURL string
88+
expectedProviderID string
89+
expectError bool
90+
}{
91+
{
92+
testname: "invalid url, should fail",
93+
resourceURL: "hvfnhdkdk",
94+
expectError: true,
95+
},
96+
{
97+
testname: "valid instance url, should pass",
98+
resourceURL: "https://www.googleapis.com/compute/v1/projects/myproject/zones/europe-west2-a/instances/gke-capg-dskczmdculd-capg-e2e-ebs0oy--014f89ba-sx2p",
99+
expectError: false,
100+
expectedProviderID: "gce://myproject/europe-west2-a/gke-capg-dskczmdculd-capg-e2e-ebs0oy--014f89ba-sx2p",
101+
},
102+
}
103+
104+
for _, tc := range testCases {
105+
t.Run(tc.testname, func(t *testing.T) {
106+
providerID, err := providerid.NewFromResourceURL(tc.resourceURL)
107+
108+
if tc.expectError {
109+
Expect(err).To(HaveOccurred())
110+
} else {
111+
Expect(err).NotTo(HaveOccurred())
112+
Expect(providerID.String()).To(Equal(tc.expectedProviderID))
113+
}
114+
})
115+
}
116+
}

cloud/scope/machine.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"k8s.io/utils/pointer"
3535
infrav1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
3636
"sigs.k8s.io/cluster-api-provider-gcp/cloud"
37+
"sigs.k8s.io/cluster-api-provider-gcp/cloud/providerid"
3738
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3839
"sigs.k8s.io/cluster-api/controllers/noderefutil"
3940
capierrors "sigs.k8s.io/cluster-api/errors"
@@ -169,8 +170,8 @@ func (m *MachineScope) GetProviderID() string {
169170

170171
// SetProviderID sets the GCPMachine providerID in spec.
171172
func (m *MachineScope) SetProviderID() {
172-
providerID := cloud.ProviderIDPrefix + path.Join(m.ClusterGetter.Project(), m.Zone(), m.Name())
173-
m.GCPMachine.Spec.ProviderID = pointer.StringPtr(providerID)
173+
providerID, _ := providerid.New(m.ClusterGetter.Project(), m.Zone(), m.Name())
174+
m.GCPMachine.Spec.ProviderID = pointer.StringPtr(providerID.String())
174175
}
175176

176177
// GetInstanceStatus returns the GCPMachine instance status.

cloud/scope/managedcontrolplane.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,8 @@ func (s *ManagedControlPlaneScope) SetEndpoint(host string) {
215215
Port: APIServerPort,
216216
}
217217
}
218+
219+
// IsAutopilotCluster returns true if this is an autopilot cluster.
220+
func (s *ManagedControlPlaneScope) IsAutopilotCluster() bool {
221+
return s.GCPManagedControlPlane.Spec.EnableAutopilot
222+
}

cloud/scope/managedmachinepool.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222

23+
"sigs.k8s.io/cluster-api-provider-gcp/cloud"
2324
"sigs.k8s.io/cluster-api-provider-gcp/util/location"
2425

2526
"sigs.k8s.io/cluster-api/util/conditions"
@@ -153,14 +154,18 @@ func (s *ManagedMachinePoolScope) NodePoolVersion() *string {
153154
}
154155

155156
// ConvertToSdkNodePool converts a node pool to format that is used by GCP SDK.
156-
func ConvertToSdkNodePool(nodePool infrav1exp.GCPManagedMachinePool, machinePool clusterv1exp.MachinePool) *containerpb.NodePool {
157+
func ConvertToSdkNodePool(nodePool infrav1exp.GCPManagedMachinePool, machinePool clusterv1exp.MachinePool, regional bool) *containerpb.NodePool {
158+
replicas := *machinePool.Spec.Replicas
159+
if regional {
160+
replicas /= cloud.DefaultNumRegionsPerZone
161+
}
157162
nodePoolName := nodePool.Spec.NodePoolName
158163
if len(nodePoolName) == 0 {
159164
nodePoolName = nodePool.Name
160165
}
161166
sdkNodePool := containerpb.NodePool{
162167
Name: nodePoolName,
163-
InitialNodeCount: nodePool.Spec.InitialNodeCount,
168+
InitialNodeCount: replicas,
164169
Config: &containerpb.NodeConfig{
165170
Labels: nodePool.Spec.KubernetesLabels,
166171
Taints: infrav1exp.ConvertToSdkTaint(nodePool.Spec.KubernetesTaints),
@@ -181,10 +186,10 @@ func ConvertToSdkNodePool(nodePool infrav1exp.GCPManagedMachinePool, machinePool
181186
}
182187

183188
// ConvertToSdkNodePools converts node pools to format that is used by GCP SDK.
184-
func ConvertToSdkNodePools(nodePools []infrav1exp.GCPManagedMachinePool, machinePools []clusterv1exp.MachinePool) []*containerpb.NodePool {
189+
func ConvertToSdkNodePools(nodePools []infrav1exp.GCPManagedMachinePool, machinePools []clusterv1exp.MachinePool, regional bool) []*containerpb.NodePool {
185190
res := []*containerpb.NodePool{}
186191
for i := range nodePools {
187-
res = append(res, ConvertToSdkNodePool(nodePools[i], machinePools[i]))
192+
res = append(res, ConvertToSdkNodePool(nodePools[i], machinePools[i], regional))
188193
}
189194
return res
190195
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
Copyright 2023 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 clusters
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/pkg/errors"
23+
)
24+
25+
var (
26+
// ErrAutopilotClusterMachinePoolsNotAllowed is used when there are machine pools specified for an autopilot enabled cluster.
27+
ErrAutopilotClusterMachinePoolsNotAllowed = errors.New("cannot use machine pools with an autopilot enabled cluster")
28+
)
29+
30+
// NewErrUnexpectedClusterStatus creates a new error for an unexpected cluster status.
31+
func NewErrUnexpectedClusterStatus(status string) error {
32+
return &errUnexpectedClusterStatus{status}
33+
}
34+
35+
type errUnexpectedClusterStatus struct {
36+
status string
37+
}
38+
39+
func (e *errUnexpectedClusterStatus) Error() string {
40+
return fmt.Sprintf("unexpected error status: %s", e.status)
41+
}

0 commit comments

Comments
 (0)