Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the reconciliation loop for GCPManagedMachinePool (GKE Part 4) #789

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 36 additions & 9 deletions cloud/scope/managedcontrolplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ package scope
import (
"context"
"fmt"
"strings"

"sigs.k8s.io/cluster-api-provider-gcp/util/location"

"google.golang.org/api/option"

Expand All @@ -30,6 +31,7 @@ import (
"github.com/pkg/errors"
infrav1exp "sigs.k8s.io/cluster-api-provider-gcp/exp/api/v1beta1"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
clusterv1exp "sigs.k8s.io/cluster-api/exp/api/v1beta1"
"sigs.k8s.io/cluster-api/util/patch"
"sigs.k8s.io/controller-runtime/pkg/client"
)
Expand Down Expand Up @@ -124,6 +126,9 @@ type ManagedControlPlaneScope struct {
mcClient *container.ClusterManagerClient
credentialsClient *credentials.IamCredentialsClient
credential *Credential

AllMachinePools []clusterv1exp.MachinePool
AllManagedMachinePools []infrav1exp.GCPManagedMachinePool
}

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

func parseLocation(location string) (region string, zone *string) {
parts := strings.Split(location, "-")
region = strings.Join(parts[:2], "-")
if len(parts) == 3 {
return region, &parts[2]
// GetAllNodePools gets all node pools for the control plane.
func (s *ManagedControlPlaneScope) GetAllNodePools(ctx context.Context) ([]infrav1exp.GCPManagedMachinePool, []clusterv1exp.MachinePool, error) {
if s.AllManagedMachinePools == nil || len(s.AllManagedMachinePools) == 0 {
listOptions := []client.ListOption{
client.InNamespace(s.GCPManagedControlPlane.Namespace),
client.MatchingLabels(map[string]string{clusterv1.ClusterLabelName: s.Cluster.Name}),
}

machinePoolList := &clusterv1exp.MachinePoolList{}
if err := s.client.List(ctx, machinePoolList, listOptions...); err != nil {
return nil, nil, err
}
managedMachinePoolList := &infrav1exp.GCPManagedMachinePoolList{}
if err := s.client.List(ctx, managedMachinePoolList, listOptions...); err != nil {
return nil, nil, err
}
if len(machinePoolList.Items) != len(managedMachinePoolList.Items) {
return nil, nil, fmt.Errorf("machinePoolList length (%d) != managedMachinePoolList length (%d)", len(machinePoolList.Items), len(managedMachinePoolList.Items))
}
s.AllMachinePools = machinePoolList.Items
s.AllManagedMachinePools = managedMachinePoolList.Items
}
return region, nil

return s.AllManagedMachinePools, s.AllMachinePools, nil
}

// Region returns the region of the GKE cluster.
func (s *ManagedControlPlaneScope) Region() string {
region, _ := parseLocation(s.GCPManagedControlPlane.Spec.Location)
return region
loc, _ := location.Parse(s.GCPManagedControlPlane.Spec.Location)
return loc.Region
}

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

// ClusterName returns the name of the cluster.
func (s *ManagedControlPlaneScope) ClusterName() string {
return s.GCPManagedControlPlane.Spec.ClusterName
}

// SetEndpoint sets the Endpoint of GCPManagedControlPlane.
func (s *ManagedControlPlaneScope) SetEndpoint(host string) {
s.GCPManagedControlPlane.Spec.Endpoint = clusterv1.APIEndpoint{
Expand Down
233 changes: 233 additions & 0 deletions cloud/scope/managedmachinepool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package scope

import (
"context"
"fmt"

"sigs.k8s.io/cluster-api-provider-gcp/util/location"

"google.golang.org/api/option"
"sigs.k8s.io/cluster-api/util/conditions"

compute "cloud.google.com/go/compute/apiv1"
container "cloud.google.com/go/container/apiv1"
"cloud.google.com/go/container/apiv1/containerpb"
"github.com/pkg/errors"
infrav1exp "sigs.k8s.io/cluster-api-provider-gcp/exp/api/v1beta1"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
clusterv1exp "sigs.k8s.io/cluster-api/exp/api/v1beta1"
"sigs.k8s.io/cluster-api/util/patch"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// ManagedMachinePoolScopeParams defines the input parameters used to create a new Scope.
type ManagedMachinePoolScopeParams struct {
ManagedClusterClient *container.ClusterManagerClient
InstanceGroupManagersClient *compute.InstanceGroupManagersClient
Client client.Client
Cluster *clusterv1.Cluster
MachinePool *clusterv1exp.MachinePool
GCPManagedCluster *infrav1exp.GCPManagedCluster
GCPManagedControlPlane *infrav1exp.GCPManagedControlPlane
GCPManagedMachinePool *infrav1exp.GCPManagedMachinePool
richardchen331 marked this conversation as resolved.
Show resolved Hide resolved
}

// NewManagedMachinePoolScope creates a new Scope from the supplied parameters.
// This is meant to be called for each reconcile iteration.
func NewManagedMachinePoolScope(ctx context.Context, params ManagedMachinePoolScopeParams) (*ManagedMachinePoolScope, error) {
if params.Cluster == nil {
return nil, errors.New("failed to generate new scope from nil Cluster")
}
if params.MachinePool == nil {
return nil, errors.New("failed to generate new scope from nil MachinePool")
}
if params.GCPManagedCluster == nil {
return nil, errors.New("failed to generate new scope from nil GCPManagedCluster")
}
if params.GCPManagedControlPlane == nil {
return nil, errors.New("failed to generate new scope from nil GCPManagedControlPlane")
}
if params.GCPManagedMachinePool == nil {
return nil, errors.New("failed to generate new scope from nil GCPManagedMachinePool")
}

var credentialData []byte
var err error
if params.GCPManagedCluster.Spec.CredentialsRef != nil {
credentialData, err = getCredentialDataFromRef(ctx, params.GCPManagedCluster.Spec.CredentialsRef, params.Client)
} else {
credentialData, err = getCredentialDataFromMount()
}
if err != nil {
return nil, errors.Errorf("failed to get credential data: %v", err)
}

if params.ManagedClusterClient == nil {
var managedClusterClient *container.ClusterManagerClient
managedClusterClient, err = container.NewClusterManagerClient(ctx, option.WithCredentialsJSON(credentialData))
if err != nil {
return nil, errors.Errorf("failed to create gcp managed cluster client: %v", err)
}
params.ManagedClusterClient = managedClusterClient
}
if params.InstanceGroupManagersClient == nil {
var instanceGroupManagersClient *compute.InstanceGroupManagersClient
instanceGroupManagersClient, err = compute.NewInstanceGroupManagersRESTClient(ctx, option.WithCredentialsJSON(credentialData))
if err != nil {
return nil, errors.Errorf("failed to create gcp instance group manager client: %v", err)
}
params.InstanceGroupManagersClient = instanceGroupManagersClient
}

helper, err := patch.NewHelper(params.GCPManagedMachinePool, params.Client)
if err != nil {
return nil, errors.Wrap(err, "failed to init patch helper")
}

return &ManagedMachinePoolScope{
client: params.Client,
Cluster: params.Cluster,
MachinePool: params.MachinePool,
GCPManagedControlPlane: params.GCPManagedControlPlane,
GCPManagedMachinePool: params.GCPManagedMachinePool,
mcClient: params.ManagedClusterClient,
migClient: params.InstanceGroupManagersClient,
patchHelper: helper,
}, nil
}

// ManagedMachinePoolScope defines the basic context for an actuator to operate upon.
type ManagedMachinePoolScope struct {
client client.Client
patchHelper *patch.Helper

Cluster *clusterv1.Cluster
MachinePool *clusterv1exp.MachinePool
GCPManagedCluster *infrav1exp.GCPManagedCluster
GCPManagedControlPlane *infrav1exp.GCPManagedControlPlane
GCPManagedMachinePool *infrav1exp.GCPManagedMachinePool
mcClient *container.ClusterManagerClient
migClient *compute.InstanceGroupManagersClient
}

// PatchObject persists the managed control plane configuration and status.
func (s *ManagedMachinePoolScope) PatchObject() error {
return s.patchHelper.Patch(
context.TODO(),
s.GCPManagedMachinePool,
patch.WithOwnedConditions{Conditions: []clusterv1.ConditionType{
infrav1exp.GKEMachinePoolReadyCondition,
infrav1exp.GKEMachinePoolCreatingCondition,
infrav1exp.GKEMachinePoolUpdatingCondition,
infrav1exp.GKEMachinePoolDeletingCondition,
}})
}

// Close closes the current scope persisting the managed control plane configuration and status.
func (s *ManagedMachinePoolScope) Close() error {
s.mcClient.Close()
s.migClient.Close()
return s.PatchObject()
}

// ConditionSetter return a condition setter (which is GCPManagedMachinePool itself).
func (s *ManagedMachinePoolScope) ConditionSetter() conditions.Setter {
return s.GCPManagedMachinePool
}

// ManagedMachinePoolClient returns a client used to interact with GKE.
func (s *ManagedMachinePoolScope) ManagedMachinePoolClient() *container.ClusterManagerClient {
return s.mcClient
}

// InstanceGroupManagersClient returns a client used to interact with GCP MIG.
func (s *ManagedMachinePoolScope) InstanceGroupManagersClient() *compute.InstanceGroupManagersClient {
return s.migClient
}

// NodePoolVersion returns the k8s version of the node pool.
func (s *ManagedMachinePoolScope) NodePoolVersion() *string {
return s.MachinePool.Spec.Template.Spec.Version
}

// ConvertToSdkNodePool converts a node pool to format that is used by GCP SDK.
func ConvertToSdkNodePool(nodePool infrav1exp.GCPManagedMachinePool, machinePool clusterv1exp.MachinePool) *containerpb.NodePool {
nodePoolName := nodePool.Spec.NodePoolName
if len(nodePoolName) == 0 {
nodePoolName = nodePool.Name
}
sdkNodePool := containerpb.NodePool{
Name: nodePoolName,
InitialNodeCount: nodePool.Spec.InitialNodeCount,
Config: &containerpb.NodeConfig{
Labels: nodePool.Spec.KubernetesLabels,
Taints: infrav1exp.ConvertToSdkTaint(nodePool.Spec.KubernetesTaints),
Metadata: nodePool.Spec.AdditionalLabels,
},
}
if nodePool.Spec.Scaling != nil {
sdkNodePool.Autoscaling = &containerpb.NodePoolAutoscaling{
Enabled: true,
MinNodeCount: *nodePool.Spec.Scaling.MinCount,
MaxNodeCount: *nodePool.Spec.Scaling.MaxCount,
}
}
if machinePool.Spec.Template.Spec.Version != nil {
sdkNodePool.Version = *machinePool.Spec.Template.Spec.Version
}
return &sdkNodePool
}

// ConvertToSdkNodePools converts node pools to format that is used by GCP SDK.
func ConvertToSdkNodePools(nodePools []infrav1exp.GCPManagedMachinePool, machinePools []clusterv1exp.MachinePool) []*containerpb.NodePool {
res := []*containerpb.NodePool{}
for i := range nodePools {
res = append(res, ConvertToSdkNodePool(nodePools[i], machinePools[i]))
}
return res
}

// SetReplicas sets the replicas count in status.
func (s *ManagedMachinePoolScope) SetReplicas(replicas int32) {
s.GCPManagedMachinePool.Status.Replicas = replicas
}

// NodePoolName returns the node pool name.
func (s *ManagedMachinePoolScope) NodePoolName() string {
if len(s.GCPManagedMachinePool.Spec.NodePoolName) > 0 {
return s.GCPManagedMachinePool.Spec.NodePoolName
}
return s.GCPManagedMachinePool.Name
}

// Region returns the region of the GKE node pool.
func (s *ManagedMachinePoolScope) Region() string {
loc, _ := location.Parse(s.GCPManagedControlPlane.Spec.Location)
return loc.Region
}

// NodePoolLocation returns the location of the node pool.
func (s *ManagedMachinePoolScope) NodePoolLocation() string {
return fmt.Sprintf("projects/%s/locations/%s/clusters/%s", s.GCPManagedControlPlane.Spec.Project, s.Region(), s.GCPManagedControlPlane.Spec.ClusterName)
}

// NodePoolFullName returns the full name of the node pool.
func (s *ManagedMachinePoolScope) NodePoolFullName() string {
return fmt.Sprintf("%s/nodePools/%s", s.NodePoolLocation(), s.NodePoolName())
}
2 changes: 1 addition & 1 deletion cloud/services/container/clusters/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func (s *Service) updateCAPIKubeconfigSecret(ctx context.Context, configSecret *
}

func (s *Service) getKubeConfigContextName(isUser bool) string {
contextName := fmt.Sprintf("gke_%s_%s_%s", s.scope.GCPManagedControlPlane.Spec.Project, s.scope.GCPManagedControlPlane.Spec.Location, s.scope.GCPManagedControlPlane.Name)
contextName := fmt.Sprintf("gke_%s_%s_%s", s.scope.GCPManagedControlPlane.Spec.Project, s.scope.GCPManagedControlPlane.Spec.Location, s.scope.ClusterName())
if isUser {
contextName = fmt.Sprintf("%s-user", contextName)
}
Expand Down
Loading