Skip to content

Commit be92b73

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 be92b73

13 files changed

+268
-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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ 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+
// TODO - additional fields will be added to support Internal LoadBalancer configuration
130+
}
131+
117132
// SubnetSpec configures an GCP Subnet.
118133
type SubnetSpec struct {
119134
// 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: 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+
// 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,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.LoadBalancer().APIServerInstanceGroupTagOverride != nil {
133+
tag = *m.ClusterGetter.LoadBalancer().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+
// 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(ctx context.Context, key *meta.Key, m *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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ spec:
9999
items:
100100
type: string
101101
type: array
102+
loadBalancer:
103+
description: LoadBalancer contains configuration for one or more LoadBalancers.
104+
properties:
105+
apiServerInstanceGroupTagOverride:
106+
description: APIServerInstanceGroupTagOverride overrides the default
107+
setting for the tag used when creating the API Server Instance
108+
Group.
109+
maxLength: 16
110+
minLength: 1
111+
pattern: (^[1-9][0-9]{0,31}$)|(^[a-z][a-z0-9-]{4,28}[a-z0-9]$)
112+
type: string
113+
type: object
102114
network:
103115
description: NetworkSpec encapsulates all things related to GCP network.
104116
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: LoadBalancer contains configuration for one or
118+
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)