Skip to content

Commit 141c227

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 447992d commit 141c227

13 files changed

+270
-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+
// LoadBalancerSpec 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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,19 @@ 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:Required
123+
// +kubebuilder:validation:MinLength=1
124+
// +kubebuilder:validation:MaxLength=16
125+
// +kubebuilder:validation:Pattern=`(^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)`
126+
// +optional
127+
APIServerInstanceGroupTagOverride *string `json:"apiServerInstanceGroupTagOverride,omitempty"`
128+
}
129+
117130
// SubnetSpec configures an GCP Subnet.
118131
type SubnetSpec struct {
119132
// 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+
APIServerInstanceGroupTagOverride() *string
6465
}
6566

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

cloud/scope/cluster.go

Lines changed: 10 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+
// APIServerInstanceGroupTagOverride returns the configure override.
134+
func (s *ClusterScope) APIServerInstanceGroupTagOverride() *string {
135+
return s.GCPCluster.Spec.LoadBalancer.APIServerInstanceGroupTagOverride
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,12 @@ 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 := infrav1.APIServerRoleTagValue
348+
if s.GCPCluster.Spec.LoadBalancer.APIServerInstanceGroupTagOverride != nil {
349+
tag = *s.GCPCluster.Spec.LoadBalancer.APIServerInstanceGroupTagOverride
350+
}
342351
return &compute.InstanceGroup{
343-
Name: fmt.Sprintf("%s-%s-%s", s.Name(), infrav1.APIServerRoleTagValue, zone),
352+
Name: fmt.Sprintf("%s-%s-%s", s.Name(), tag, zone),
344353
NamedPorts: []*compute.NamedPort{
345354
{
346355
Name: "apiserver",

cloud/scope/machine.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ 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 := infrav1.APIServerRoleTagValue
132+
if m.ClusterGetter.APIServerInstanceGroupTagOverride() != nil {
133+
tag = *m.ClusterGetter.APIServerInstanceGroupTagOverride()
134+
}
135+
return fmt.Sprintf("%s-%s-%s", m.ClusterGetter.Name(), tag, m.Zone())
132136
}
133137

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

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,19 @@ spec:
9999
items:
100100
type: string
101101
type: array
102+
loadBalancer:
103+
description: LoadBalancerSpec contains configuration for one or more
104+
LoadBalancers.
105+
properties:
106+
apiServerInstanceGroupTagOverride:
107+
description: APIServerInstanceGroupTagOverride overrides the default
108+
setting for the tag used when creating the API Server Instance
109+
Group.
110+
maxLength: 16
111+
minLength: 1
112+
pattern: (^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)
113+
type: string
114+
type: object
102115
network:
103116
description: NetworkSpec encapsulates all things related to GCP network.
104117
properties:

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ spec:
113113
items:
114114
type: string
115115
type: array
116+
loadBalancer:
117+
description: LoadBalancerSpec contains configuration for one
118+
or more LoadBalancers.
119+
properties:
120+
apiServerInstanceGroupTagOverride:
121+
description: APIServerInstanceGroupTagOverride overrides
122+
the default setting for the tag used when creating the
123+
API Server Instance Group.
124+
maxLength: 16
125+
minLength: 1
126+
pattern: (^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)
127+
type: string
128+
type: object
116129
network:
117130
description: NetworkSpec encapsulates all things related to
118131
GCP network.

0 commit comments

Comments
 (0)