Skip to content

Commit 175ba26

Browse files
Implement the reconciliation loop for GCPManagedMachinePool
1 parent e6111c7 commit 175ba26

21 files changed

+1385
-83
lines changed

cloud/scope/managedcontrolplane.go

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ package scope
1919
import (
2020
"context"
2121
"fmt"
22-
"strings"
22+
23+
"sigs.k8s.io/cluster-api-provider-gcp/util/location"
2324

2425
"google.golang.org/api/option"
2526

@@ -30,6 +31,7 @@ import (
3031
"github.com/pkg/errors"
3132
infrav1exp "sigs.k8s.io/cluster-api-provider-gcp/exp/api/v1beta1"
3233
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
34+
clusterv1exp "sigs.k8s.io/cluster-api/exp/api/v1beta1"
3335
"sigs.k8s.io/cluster-api/util/patch"
3436
"sigs.k8s.io/controller-runtime/pkg/client"
3537
)
@@ -124,6 +126,9 @@ type ManagedControlPlaneScope struct {
124126
mcClient *container.ClusterManagerClient
125127
credentialsClient *credentials.IamCredentialsClient
126128
credential *Credential
129+
130+
AllMachinePools []clusterv1exp.MachinePool
131+
AllManagedMachinePools []infrav1exp.GCPManagedMachinePool
127132
}
128133

129134
// PatchObject persists the managed control plane configuration and status.
@@ -171,19 +176,36 @@ func (s *ManagedControlPlaneScope) GetCredential() *Credential {
171176
return s.credential
172177
}
173178

174-
func parseLocation(location string) (region string, zone *string) {
175-
parts := strings.Split(location, "-")
176-
region = strings.Join(parts[:2], "-")
177-
if len(parts) == 3 {
178-
return region, &parts[2]
179+
// GetAllNodePools gets all node pools for the control plane.
180+
func (s *ManagedControlPlaneScope) GetAllNodePools(ctx context.Context) ([]infrav1exp.GCPManagedMachinePool, []clusterv1exp.MachinePool, error) {
181+
if s.AllManagedMachinePools == nil || len(s.AllManagedMachinePools) == 0 {
182+
listOptions := []client.ListOption{
183+
client.InNamespace(s.GCPManagedControlPlane.Namespace),
184+
client.MatchingLabels(map[string]string{clusterv1.ClusterLabelName: s.Cluster.Name}),
185+
}
186+
187+
machinePoolList := &clusterv1exp.MachinePoolList{}
188+
if err := s.client.List(ctx, machinePoolList, listOptions...); err != nil {
189+
return nil, nil, err
190+
}
191+
managedMachinePoolList := &infrav1exp.GCPManagedMachinePoolList{}
192+
if err := s.client.List(ctx, managedMachinePoolList, listOptions...); err != nil {
193+
return nil, nil, err
194+
}
195+
if len(machinePoolList.Items) != len(managedMachinePoolList.Items) {
196+
return nil, nil, fmt.Errorf("machinePoolList length (%d) != managedMachinePoolList length (%d)", len(machinePoolList.Items), len(managedMachinePoolList.Items))
197+
}
198+
s.AllMachinePools = machinePoolList.Items
199+
s.AllManagedMachinePools = managedMachinePoolList.Items
179200
}
180-
return region, nil
201+
202+
return s.AllManagedMachinePools, s.AllMachinePools, nil
181203
}
182204

183205
// Region returns the region of the GKE cluster.
184206
func (s *ManagedControlPlaneScope) Region() string {
185-
region, _ := parseLocation(s.GCPManagedControlPlane.Spec.Location)
186-
return region
207+
loc, _ := location.Parse(s.GCPManagedControlPlane.Spec.Location)
208+
return loc.Region
187209
}
188210

189211
// ClusterLocation returns the location of the cluster.
@@ -196,6 +218,11 @@ func (s *ManagedControlPlaneScope) ClusterFullName() string {
196218
return fmt.Sprintf("%s/clusters/%s", s.ClusterLocation(), s.GCPManagedControlPlane.Spec.ClusterName)
197219
}
198220

221+
// ClusterName returns the name of the cluster.
222+
func (s *ManagedControlPlaneScope) ClusterName() string {
223+
return s.GCPManagedControlPlane.Spec.ClusterName
224+
}
225+
199226
// SetEndpoint sets the Endpoint of GCPManagedControlPlane.
200227
func (s *ManagedControlPlaneScope) SetEndpoint(host string) {
201228
s.GCPManagedControlPlane.Spec.Endpoint = clusterv1.APIEndpoint{

cloud/scope/managedmachinepool.go

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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 scope
18+
19+
import (
20+
"context"
21+
"fmt"
22+
23+
"sigs.k8s.io/cluster-api-provider-gcp/util/location"
24+
25+
"google.golang.org/api/option"
26+
"sigs.k8s.io/cluster-api/util/conditions"
27+
28+
compute "cloud.google.com/go/compute/apiv1"
29+
container "cloud.google.com/go/container/apiv1"
30+
"cloud.google.com/go/container/apiv1/containerpb"
31+
"github.com/pkg/errors"
32+
infrav1exp "sigs.k8s.io/cluster-api-provider-gcp/exp/api/v1beta1"
33+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
34+
clusterv1exp "sigs.k8s.io/cluster-api/exp/api/v1beta1"
35+
"sigs.k8s.io/cluster-api/util/patch"
36+
"sigs.k8s.io/controller-runtime/pkg/client"
37+
)
38+
39+
// ManagedMachinePoolScopeParams defines the input parameters used to create a new Scope.
40+
type ManagedMachinePoolScopeParams struct {
41+
ManagedClusterClient *container.ClusterManagerClient
42+
InstanceGroupManagersClient *compute.InstanceGroupManagersClient
43+
Client client.Client
44+
Cluster *clusterv1.Cluster
45+
MachinePool *clusterv1exp.MachinePool
46+
GCPManagedCluster *infrav1exp.GCPManagedCluster
47+
GCPManagedControlPlane *infrav1exp.GCPManagedControlPlane
48+
GCPManagedMachinePool *infrav1exp.GCPManagedMachinePool
49+
}
50+
51+
// NewManagedMachinePoolScope creates a new Scope from the supplied parameters.
52+
// This is meant to be called for each reconcile iteration.
53+
func NewManagedMachinePoolScope(ctx context.Context, params ManagedMachinePoolScopeParams) (*ManagedMachinePoolScope, error) {
54+
if params.Cluster == nil {
55+
return nil, errors.New("failed to generate new scope from nil Cluster")
56+
}
57+
if params.MachinePool == nil {
58+
return nil, errors.New("failed to generate new scope from nil MachinePool")
59+
}
60+
if params.GCPManagedCluster == nil {
61+
return nil, errors.New("failed to generate new scope from nil GCPManagedCluster")
62+
}
63+
if params.GCPManagedControlPlane == nil {
64+
return nil, errors.New("failed to generate new scope from nil GCPManagedControlPlane")
65+
}
66+
if params.GCPManagedMachinePool == nil {
67+
return nil, errors.New("failed to generate new scope from nil GCPManagedMachinePool")
68+
}
69+
70+
var credentialData []byte
71+
var err error
72+
if params.GCPManagedCluster.Spec.CredentialsRef != nil {
73+
credentialData, err = getCredentialDataFromRef(ctx, params.GCPManagedCluster.Spec.CredentialsRef, params.Client)
74+
} else {
75+
credentialData, err = getCredentialDataFromMount()
76+
}
77+
if err != nil {
78+
return nil, errors.Errorf("failed to get credential data: %v", err)
79+
}
80+
81+
if params.ManagedClusterClient == nil {
82+
var managedClusterClient *container.ClusterManagerClient
83+
managedClusterClient, err = container.NewClusterManagerClient(ctx, option.WithCredentialsJSON(credentialData))
84+
if err != nil {
85+
return nil, errors.Errorf("failed to create gcp managed cluster client: %v", err)
86+
}
87+
params.ManagedClusterClient = managedClusterClient
88+
}
89+
if params.InstanceGroupManagersClient == nil {
90+
var instanceGroupManagersClient *compute.InstanceGroupManagersClient
91+
instanceGroupManagersClient, err = compute.NewInstanceGroupManagersRESTClient(ctx, option.WithCredentialsJSON(credentialData))
92+
if err != nil {
93+
return nil, errors.Errorf("failed to create gcp instance group manager client: %v", err)
94+
}
95+
params.InstanceGroupManagersClient = instanceGroupManagersClient
96+
}
97+
98+
helper, err := patch.NewHelper(params.GCPManagedMachinePool, params.Client)
99+
if err != nil {
100+
return nil, errors.Wrap(err, "failed to init patch helper")
101+
}
102+
103+
return &ManagedMachinePoolScope{
104+
client: params.Client,
105+
Cluster: params.Cluster,
106+
MachinePool: params.MachinePool,
107+
GCPManagedControlPlane: params.GCPManagedControlPlane,
108+
GCPManagedMachinePool: params.GCPManagedMachinePool,
109+
mcClient: params.ManagedClusterClient,
110+
migClient: params.InstanceGroupManagersClient,
111+
patchHelper: helper,
112+
}, nil
113+
}
114+
115+
// ManagedMachinePoolScope defines the basic context for an actuator to operate upon.
116+
type ManagedMachinePoolScope struct {
117+
client client.Client
118+
patchHelper *patch.Helper
119+
120+
Cluster *clusterv1.Cluster
121+
MachinePool *clusterv1exp.MachinePool
122+
GCPManagedCluster *infrav1exp.GCPManagedCluster
123+
GCPManagedControlPlane *infrav1exp.GCPManagedControlPlane
124+
GCPManagedMachinePool *infrav1exp.GCPManagedMachinePool
125+
mcClient *container.ClusterManagerClient
126+
migClient *compute.InstanceGroupManagersClient
127+
}
128+
129+
// PatchObject persists the managed control plane configuration and status.
130+
func (s *ManagedMachinePoolScope) PatchObject() error {
131+
return s.patchHelper.Patch(
132+
context.TODO(),
133+
s.GCPManagedMachinePool,
134+
patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
135+
infrav1exp.GKEMachinePoolReadyCondition,
136+
infrav1exp.GKEMachinePoolCreatingCondition,
137+
infrav1exp.GKEMachinePoolUpdatingCondition,
138+
infrav1exp.GKEMachinePoolDeletingCondition,
139+
}})
140+
}
141+
142+
// Close closes the current scope persisting the managed control plane configuration and status.
143+
func (s *ManagedMachinePoolScope) Close() error {
144+
s.mcClient.Close()
145+
s.migClient.Close()
146+
return s.PatchObject()
147+
}
148+
149+
// ConditionSetter return a condition setter (which is GCPManagedMachinePool itself).
150+
func (s *ManagedMachinePoolScope) ConditionSetter() conditions.Setter {
151+
return s.GCPManagedMachinePool
152+
}
153+
154+
// ManagedMachinePoolClient returns a client used to interact with GKE.
155+
func (s *ManagedMachinePoolScope) ManagedMachinePoolClient() *container.ClusterManagerClient {
156+
return s.mcClient
157+
}
158+
159+
// InstanceGroupManagersClient returns a client used to interact with GCP MIG.
160+
func (s *ManagedMachinePoolScope) InstanceGroupManagersClient() *compute.InstanceGroupManagersClient {
161+
return s.migClient
162+
}
163+
164+
// NodePoolVersion returns the k8s version of the node pool.
165+
func (s *ManagedMachinePoolScope) NodePoolVersion() *string {
166+
return s.MachinePool.Spec.Template.Spec.Version
167+
}
168+
169+
// ConvertToSdkNodePool converts a node pool to format that is used by GCP SDK.
170+
func ConvertToSdkNodePool(nodePool infrav1exp.GCPManagedMachinePool, machinePool clusterv1exp.MachinePool) *containerpb.NodePool {
171+
nodePoolName := nodePool.Spec.NodePoolName
172+
if len(nodePoolName) == 0 {
173+
nodePoolName = nodePool.Name
174+
}
175+
sdkNodePool := containerpb.NodePool{
176+
Name: nodePoolName,
177+
InitialNodeCount: nodePool.Spec.InitialNodeCount,
178+
Config: &containerpb.NodeConfig{
179+
Labels: nodePool.Spec.KubernetesLabels,
180+
Taints: infrav1exp.ConvertToSdkTaint(nodePool.Spec.KubernetesTaints),
181+
Metadata: nodePool.Spec.AdditionalLabels,
182+
},
183+
}
184+
if nodePool.Spec.Scaling != nil {
185+
sdkNodePool.Autoscaling = &containerpb.NodePoolAutoscaling{
186+
Enabled: true,
187+
MinNodeCount: *nodePool.Spec.Scaling.MinCount,
188+
MaxNodeCount: *nodePool.Spec.Scaling.MaxCount,
189+
}
190+
}
191+
if machinePool.Spec.Template.Spec.Version != nil {
192+
sdkNodePool.Version = *machinePool.Spec.Template.Spec.Version
193+
}
194+
return &sdkNodePool
195+
}
196+
197+
// ConvertToSdkNodePools converts node pools to format that is used by GCP SDK.
198+
func ConvertToSdkNodePools(nodePools []infrav1exp.GCPManagedMachinePool, machinePools []clusterv1exp.MachinePool) []*containerpb.NodePool {
199+
res := []*containerpb.NodePool{}
200+
for i := range nodePools {
201+
res = append(res, ConvertToSdkNodePool(nodePools[i], machinePools[i]))
202+
}
203+
return res
204+
}
205+
206+
// SetReplicas sets the replicas count in status.
207+
func (s *ManagedMachinePoolScope) SetReplicas(replicas int32) {
208+
s.GCPManagedMachinePool.Status.Replicas = replicas
209+
}
210+
211+
// NodePoolName returns the node pool name.
212+
func (s *ManagedMachinePoolScope) NodePoolName() string {
213+
if len(s.GCPManagedMachinePool.Spec.NodePoolName) > 0 {
214+
return s.GCPManagedMachinePool.Spec.NodePoolName
215+
}
216+
return s.GCPManagedMachinePool.Name
217+
}
218+
219+
// Region returns the region of the GKE node pool.
220+
func (s *ManagedMachinePoolScope) Region() string {
221+
loc, _ := location.Parse(s.GCPManagedControlPlane.Spec.Location)
222+
return loc.Region
223+
}
224+
225+
// NodePoolLocation returns the location of the node pool.
226+
func (s *ManagedMachinePoolScope) NodePoolLocation() string {
227+
return fmt.Sprintf("projects/%s/locations/%s/clusters/%s", s.GCPManagedControlPlane.Spec.Project, s.Region(), s.GCPManagedControlPlane.Spec.ClusterName)
228+
}
229+
230+
// NodePoolFullName returns the full name of the node pool.
231+
func (s *ManagedMachinePoolScope) NodePoolFullName() string {
232+
return fmt.Sprintf("%s/nodePools/%s", s.NodePoolLocation(), s.NodePoolName())
233+
}

cloud/services/container/clusters/kubeconfig.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ func (s *Service) updateCAPIKubeconfigSecret(ctx context.Context, configSecret *
198198
}
199199

200200
func (s *Service) getKubeConfigContextName(isUser bool) string {
201-
contextName := fmt.Sprintf("gke_%s_%s_%s", s.scope.GCPManagedControlPlane.Spec.Project, s.scope.GCPManagedControlPlane.Spec.Location, s.scope.GCPManagedControlPlane.Name)
201+
contextName := fmt.Sprintf("gke_%s_%s_%s", s.scope.GCPManagedControlPlane.Spec.Project, s.scope.GCPManagedControlPlane.Spec.Location, s.scope.ClusterName())
202202
if isUser {
203203
contextName = fmt.Sprintf("%s-user", contextName)
204204
}

0 commit comments

Comments
 (0)