Skip to content

Commit 318600d

Browse files
committed
Allow configuration of API server instance group name
Provides the ability to override the API server instance group name to allow compatibility when using the Openshift Machine API.
1 parent 1848941 commit 318600d

13 files changed

+256
-2
lines changed

api/v1beta1/gcpcluster_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ type GCPClusterSpec struct {
6464
// supplied then the credentials of the controller will be used.
6565
// +optional
6666
CredentialsRef *ObjectReference `json:"credentialsRef,omitempty"`
67+
68+
// LoadBalancer contains configuration for one or more LoadBalancers.
69+
// +optional
70+
LoadBalancer LoadBalancerSpec `json:"loadBalancer,omitempty"`
6771
}
6872

6973
// GCPClusterStatus defines the observed state of GCPCluster.

api/v1beta1/types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ type NetworkSpec struct {
114114
LoadBalancerBackendPort *int32 `json:"loadBalancerBackendPort,omitempty"`
115115
}
116116

117+
// LoadBalancerSpec contains configuration for one or more LoadBalancers.
118+
type LoadBalancerSpec struct {
119+
120+
// APIServerInstanceGroupTagOverride overrides the default setting for the
121+
// tag used when creating the API Server Instance Group.
122+
// +kubebuilder:validation:Optional
123+
// +kubebuilder:validation:MaxLength=16
124+
// +kubebuilder:validation:Pattern=`(^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)`
125+
// +optional
126+
APIServerInstanceGroupTagOverride *string `json:"apiServerInstanceGroupTagOverride,omitempty"`
127+
}
128+
117129
// SubnetSpec configures an GCP Subnet.
118130
type SubnetSpec struct {
119131
// Name defines a unique identifier to reference this resource.

api/v1beta1/zz_generated.deepcopy.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cloud/interfaces.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ type ClusterGetter interface {
6161
FailureDomains() clusterv1.FailureDomains
6262
ControlPlaneEndpoint() clusterv1.APIEndpoint
6363
ResourceManagerTags() infrav1.ResourceManagerTags
64+
LoadBalancer() infrav1.LoadBalancerSpec
6465
}
6566

6667
// ClusterSetter is an interface which can set cluster information.

cloud/scope/cluster.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ func (s *ClusterScope) AdditionalLabels() infrav1.Labels {
130130
return s.GCPCluster.Spec.AdditionalLabels
131131
}
132132

133+
// LoadBalancer returns the LoadBalancer configuration.
134+
func (s *ClusterScope) LoadBalancer() infrav1.LoadBalancerSpec {
135+
return s.GCPCluster.Spec.LoadBalancer
136+
}
137+
133138
// ResourceManagerTags returns ResourceManagerTags from the scope's GCPCluster. The returned value will never be nil.
134139
func (s *ClusterScope) ResourceManagerTags() infrav1.ResourceManagerTags {
135140
if len(s.GCPCluster.Spec.ResourceManagerTags) == 0 {
@@ -339,8 +344,9 @@ func (s *ClusterScope) HealthCheckSpec() *compute.HealthCheck {
339344
// InstanceGroupSpec returns google compute instance-group spec.
340345
func (s *ClusterScope) InstanceGroupSpec(zone string) *compute.InstanceGroup {
341346
port := ptr.Deref(s.GCPCluster.Spec.Network.LoadBalancerBackendPort, 6443)
347+
tag := ptr.Deref(s.GCPCluster.Spec.LoadBalancer.APIServerInstanceGroupTagOverride, infrav1.APIServerRoleTagValue)
342348
return &compute.InstanceGroup{
343-
Name: fmt.Sprintf("%s-%s-%s", s.Name(), infrav1.APIServerRoleTagValue, zone),
349+
Name: fmt.Sprintf("%s-%s-%s", s.Name(), tag, zone),
344350
NamedPorts: []*compute.NamedPort{
345351
{
346352
Name: "apiserver",

cloud/scope/machine.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ func (m *MachineScope) Namespace() string {
128128

129129
// ControlPlaneGroupName returns the control-plane instance group name.
130130
func (m *MachineScope) ControlPlaneGroupName() string {
131-
return fmt.Sprintf("%s-%s-%s", m.ClusterGetter.Name(), infrav1.APIServerRoleTagValue, m.Zone())
131+
tag := ptr.Deref(m.ClusterGetter.LoadBalancer().APIServerInstanceGroupTagOverride, infrav1.APIServerRoleTagValue)
132+
return fmt.Sprintf("%s-%s-%s", m.ClusterGetter.Name(), tag, m.Zone())
132133
}
133134

134135
// IsControlPlane returns true if the machine is a control plane.

cloud/scope/managedcluster.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ func (s *ManagedClusterScope) AdditionalLabels() infrav1.Labels {
133133
return s.GCPManagedCluster.Spec.AdditionalLabels
134134
}
135135

136+
// LoadBalancer returns the LoadBalancer configuration.
137+
func (s *ManagedClusterScope) LoadBalancer() infrav1.LoadBalancerSpec {
138+
return s.GCPManagedCluster.Spec.LoadBalancer
139+
}
140+
136141
// ResourceManagerTags returns ResourceManagerTags from cluster. The returned value will never be nil.
137142
func (s *ManagedClusterScope) ResourceManagerTags() infrav1.ResourceManagerTags {
138143
if len(s.GCPManagedCluster.Spec.ResourceManagerTags) == 0 {
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
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 loadbalancers
18+
19+
import (
20+
"context"
21+
"net/http"
22+
"testing"
23+
24+
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
25+
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
26+
"github.com/google/go-cmp/cmp"
27+
"google.golang.org/api/compute/v1"
28+
"google.golang.org/api/googleapi"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/client-go/kubernetes/scheme"
31+
infrav1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
32+
"sigs.k8s.io/cluster-api-provider-gcp/cloud/scope"
33+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
34+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
35+
)
36+
37+
func init() {
38+
_ = clusterv1.AddToScheme(scheme.Scheme)
39+
_ = infrav1.AddToScheme(scheme.Scheme)
40+
}
41+
42+
func getBaseClusterScope() (*scope.ClusterScope, error) {
43+
fakec := fake.NewClientBuilder().
44+
WithScheme(scheme.Scheme).
45+
Build()
46+
47+
fakeCluster := &clusterv1.Cluster{
48+
ObjectMeta: metav1.ObjectMeta{
49+
Name: "my-cluster",
50+
Namespace: "default",
51+
},
52+
Spec: clusterv1.ClusterSpec{},
53+
}
54+
55+
fakeGCPCluster := &infrav1.GCPCluster{
56+
ObjectMeta: metav1.ObjectMeta{
57+
Name: "my-cluster",
58+
Namespace: "default",
59+
},
60+
Spec: infrav1.GCPClusterSpec{
61+
Project: "my-proj",
62+
Region: "us-central1",
63+
},
64+
Status: infrav1.GCPClusterStatus{
65+
FailureDomains: clusterv1.FailureDomains{
66+
"us-central1-a": clusterv1.FailureDomainSpec{ControlPlane: true},
67+
},
68+
},
69+
}
70+
clusterScope, err := scope.NewClusterScope(context.TODO(), scope.ClusterScopeParams{
71+
Client: fakec,
72+
Cluster: fakeCluster,
73+
GCPCluster: fakeGCPCluster,
74+
GCPServices: scope.GCPServices{
75+
Compute: &compute.Service{},
76+
},
77+
})
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
return clusterScope, nil
83+
}
84+
85+
func TestService_createOrGetInstanceGroup(t *testing.T) {
86+
tests := []struct {
87+
name string
88+
scope func(s *scope.ClusterScope) Scope
89+
mockInstanceGroup *cloud.MockInstanceGroups
90+
want []*compute.InstanceGroup
91+
wantErr bool
92+
}{
93+
{
94+
name: "error getting instanceGroup with non 404 error code (should return an error)",
95+
scope: func(s *scope.ClusterScope) Scope { return s },
96+
mockInstanceGroup: &cloud.MockInstanceGroups{
97+
ProjectRouter: &cloud.SingleProjectRouter{ID: "proj-id"},
98+
Objects: map[meta.Key]*cloud.MockInstanceGroupsObj{},
99+
GetHook: func(_ context.Context, _ *meta.Key, _ *cloud.MockInstanceGroups) (bool, *compute.InstanceGroup, error) {
100+
return true, &compute.InstanceGroup{}, &googleapi.Error{Code: http.StatusBadRequest}
101+
},
102+
},
103+
want: []*compute.InstanceGroup{},
104+
wantErr: true,
105+
},
106+
{
107+
name: "instanceGroup name is overridden (should create instanceGroup)",
108+
scope: func(s *scope.ClusterScope) Scope {
109+
var tagOverride = "master"
110+
s.GCPCluster.Spec.LoadBalancer = infrav1.LoadBalancerSpec{
111+
APIServerInstanceGroupTagOverride: &tagOverride,
112+
}
113+
return s
114+
},
115+
mockInstanceGroup: &cloud.MockInstanceGroups{
116+
ProjectRouter: &cloud.SingleProjectRouter{ID: "proj-id"},
117+
Objects: map[meta.Key]*cloud.MockInstanceGroupsObj{},
118+
},
119+
want: []*compute.InstanceGroup{
120+
{
121+
Name: "my-cluster-master-us-central1-a",
122+
NamedPorts: []*compute.NamedPort{{Name: "apiserver", Port: 6443}},
123+
SelfLink: "https://www.googleapis.com/compute/v1/projects/proj-id/zones/us-central1-a/instanceGroups/my-cluster-master-us-central1-a",
124+
},
125+
},
126+
},
127+
{
128+
name: "instanceGroup does not exist (should create instanceGroup)",
129+
scope: func(s *scope.ClusterScope) Scope { return s },
130+
mockInstanceGroup: &cloud.MockInstanceGroups{
131+
ProjectRouter: &cloud.SingleProjectRouter{ID: "proj-id"},
132+
Objects: map[meta.Key]*cloud.MockInstanceGroupsObj{},
133+
},
134+
want: []*compute.InstanceGroup{
135+
{
136+
Name: "my-cluster-apiserver-us-central1-a",
137+
NamedPorts: []*compute.NamedPort{{Name: "apiserver", Port: 6443}},
138+
SelfLink: "https://www.googleapis.com/compute/v1/projects/proj-id/zones/us-central1-a/instanceGroups/my-cluster-apiserver-us-central1-a",
139+
},
140+
},
141+
},
142+
}
143+
for _, tt := range tests {
144+
t.Run(tt.name, func(t *testing.T) {
145+
ctx := context.TODO()
146+
147+
clusterScope, err := getBaseClusterScope()
148+
if err != nil {
149+
t.Fatal(err)
150+
}
151+
s := New(tt.scope(clusterScope))
152+
s.instancegroups = tt.mockInstanceGroup
153+
got, err := s.createOrGetInstanceGroups(ctx)
154+
if (err != nil) != tt.wantErr {
155+
t.Errorf("Service s.createOrGetInstanceGroups() error = %v, wantErr %v", err, tt.wantErr)
156+
return
157+
}
158+
159+
if d := cmp.Diff(tt.want, got); d != "" {
160+
t.Errorf("Service s.createOrGetInstanceGroups() mismatch (-want +got):\n%s", d)
161+
}
162+
})
163+
}
164+
}

config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclusters.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ spec:
108108
items:
109109
type: string
110110
type: array
111+
loadBalancer:
112+
description: LoadBalancer contains configuration for one or more LoadBalancers.
113+
properties:
114+
apiServerInstanceGroupTagOverride:
115+
description: |-
116+
APIServerInstanceGroupTagOverride overrides the default setting for the
117+
tag used when creating the API Server Instance Group.
118+
maxLength: 16
119+
pattern: (^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)
120+
type: string
121+
type: object
111122
network:
112123
description: NetworkSpec encapsulates all things related to GCP network.
113124
properties:

config/crd/bases/infrastructure.cluster.x-k8s.io_gcpclustertemplates.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,18 @@ spec:
123123
items:
124124
type: string
125125
type: array
126+
loadBalancer:
127+
description: LoadBalancer contains configuration for one or
128+
more LoadBalancers.
129+
properties:
130+
apiServerInstanceGroupTagOverride:
131+
description: |-
132+
APIServerInstanceGroupTagOverride overrides the default setting for the
133+
tag used when creating the API Server Instance Group.
134+
maxLength: 16
135+
pattern: (^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)
136+
type: string
137+
type: object
126138
network:
127139
description: NetworkSpec encapsulates all things related to
128140
GCP network.

0 commit comments

Comments
 (0)