Skip to content

Commit 0196c4b

Browse files
committed
MachinePool: populate providerIDList
1 parent c5fe453 commit 0196c4b

File tree

7 files changed

+133
-15
lines changed

7 files changed

+133
-15
lines changed

cloud/services/compute/instancegroupmanagers/instancegroupmanagers_reconcile.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package instancegroupmanagers
1919
import (
2020
"context"
2121
"fmt"
22+
"strings"
2223

24+
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/filter"
2325
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
2426
"google.golang.org/api/compute/v1"
2527
"sigs.k8s.io/cluster-api-provider-gcp/cloud/gcperrors"
@@ -28,16 +30,16 @@ import (
2830
)
2931

3032
// Reconcile reconcile machine instance.
31-
func (s *Service) Reconcile(ctx context.Context, instanceTemplateKey *meta.Key) error {
33+
func (s *Service) Reconcile(ctx context.Context, instanceTemplateKey *meta.Key) (*compute.InstanceGroupManager, error) {
3234
log := log.FromContext(ctx)
3335
log.Info("Reconciling instanceGroupManager resources")
3436
igm, err := s.createOrGet(ctx, instanceTemplateKey)
3537
if err != nil {
36-
return err
38+
return nil, err
3739
}
3840
log.V(2).Info("Reconciled instanceGroupManager", "selfLink", igm.SelfLink)
3941

40-
return nil
42+
return igm, nil
4143
}
4244

4345
// Delete deletes the GCP instanceGroupManager resource.
@@ -131,3 +133,33 @@ func (s *Service) createOrGet(ctx context.Context, instanceTemplateKey *meta.Key
131133

132134
return actual, nil
133135
}
136+
137+
// ListInstances lists instances in the the instanceGroup linked to the passed instanceGroupManager.
138+
func (s *Service) ListInstances(ctx context.Context, instanceGroupManager *compute.InstanceGroupManager) ([]*compute.InstanceWithNamedPorts, error) {
139+
log := log.FromContext(ctx)
140+
141+
var igKey *meta.Key
142+
{
143+
instanceGroup := instanceGroupManager.InstanceGroup
144+
instanceGroup = strings.TrimPrefix(instanceGroup, "https://www.googleapis.com/")
145+
instanceGroup = strings.TrimPrefix(instanceGroup, "compute/v1/")
146+
tokens := strings.Split(instanceGroup, "/")
147+
if len(tokens) == 6 && tokens[0] == "projects" && tokens[2] == "zones" && tokens[4] == "instanceGroups" {
148+
igKey = meta.ZonalKey(tokens[5], tokens[3])
149+
} else {
150+
return nil, fmt.Errorf("unexpected format for instanceGroup: %q", instanceGroup)
151+
}
152+
}
153+
154+
log.Info("Listing instances in instanceGroup", "instanceGroup", instanceGroupManager.InstanceGroup)
155+
listInstancesRequest := &compute.InstanceGroupsListInstancesRequest{
156+
InstanceState: "ALL",
157+
}
158+
instances, err := s.instanceGroups.ListInstances(ctx, igKey, listInstancesRequest, filter.None)
159+
if err != nil {
160+
log.Error(err, "Error listing instances in instanceGroup", "instanceGroup", instanceGroupManager.InstanceGroup)
161+
return nil, fmt.Errorf("listing instances in instanceGroup %q: %w", instanceGroupManager.InstanceGroup, err)
162+
}
163+
164+
return instances, nil
165+
}

cloud/services/compute/instancegroupmanagers/service.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type Scope interface {
5050
type Service struct {
5151
scope Scope
5252
instanceGroupManagers instanceGroupManagersClient
53+
instanceGroups k8scloud.InstanceGroups
5354
}
5455

5556
// var _ cloud.Reconciler = &Service{}
@@ -61,5 +62,6 @@ func New(scope Scope) *Service {
6162
return &Service{
6263
scope: scope,
6364
instanceGroupManagers: cloudScope.InstanceGroupManagers(),
65+
instanceGroups: cloudScope.InstanceGroups(),
6466
}
6567
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,13 @@ spec:
256256
preemptible:
257257
description: Preemptible defines if instance is preemptible
258258
type: boolean
259+
providerIDList:
260+
description: |-
261+
ProviderIDList are the identification IDs of machine instances provided by the provider.
262+
This field must match the provider IDs as seen on the node objects corresponding to a machine pool's machine instances.
263+
items:
264+
type: string
265+
type: array
259266
provisioningModel:
260267
description: |-
261268
ProvisioningModel defines if instance is spot.

exp/api/v1beta1/gcpmachinepool_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ const (
3232

3333
// GCPMachinePoolSpec defines the desired state of GCPMachinePool.
3434
type GCPMachinePoolSpec struct {
35+
// ProviderIDList are the identification IDs of machine instances provided by the provider.
36+
// This field must match the provider IDs as seen on the node objects corresponding to a machine pool's machine instances.
37+
// +optional
38+
ProviderIDList []string `json:"providerIDList,omitempty"`
39+
3540
// InstanceType is the type of instance to create. Example: n1.standard-2
3641
InstanceType string `json:"instanceType"`
3742

exp/api/v1beta1/gcpmachinepool_webhook.go

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ package v1beta1
1919
import (
2020
"context"
2121
"fmt"
22-
"reflect"
2322

2423
apierrors "k8s.io/apimachinery/pkg/api/errors"
2524
"k8s.io/apimachinery/pkg/runtime"
25+
"k8s.io/apimachinery/pkg/util/validation/field"
2626
ctrl "sigs.k8s.io/controller-runtime"
2727
logf "sigs.k8s.io/controller-runtime/pkg/log"
2828
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -61,23 +61,50 @@ func (*gcpMachinePoolWebhook) ValidateCreate(_ context.Context, obj runtime.Obje
6161

6262
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type.
6363
func (*gcpMachinePoolWebhook) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
64-
old, ok := oldObj.(*GCPMachinePool)
65-
if !ok {
66-
return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a GCPMachinePool but got a %T", oldObj))
67-
}
68-
6964
r, ok := newObj.(*GCPMachinePool)
7065
if !ok {
71-
return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a GCPMachinePool but got a %T", newObj))
66+
return nil, fmt.Errorf("expected a GCPMachinePool object but got %T", r)
7267
}
7368

7469
gcpMachinePoolLog.Info("Validating GCPMachinePool update", "name", r.Name)
7570

76-
if !reflect.DeepEqual(r.Spec, old.Spec) {
77-
return nil, apierrors.NewBadRequest("GCPMachinePool.Spec is immutable")
71+
var allErrs field.ErrorList
72+
73+
// allErrs = append(allErrs, r.validateDefaultCoolDown()...)
74+
// allErrs = append(allErrs, r.Spec.AdditionalTags.Validate()...)
75+
// allErrs = append(allErrs, r.validateSubnets()...)
76+
// allErrs = append(allErrs, r.validateAdditionalSecurityGroups()...)
77+
// allErrs = append(allErrs, r.validateSpotInstances()...)
78+
// allErrs = append(allErrs, r.validateRefreshPreferences()...)
79+
// allErrs = append(allErrs, r.validateLifecycleHooks()...)
80+
81+
if len(allErrs) == 0 {
82+
return nil, nil
7883
}
7984

80-
return nil, nil
85+
return nil, apierrors.NewInvalid(
86+
r.GroupVersionKind().GroupKind(),
87+
r.Name,
88+
allErrs,
89+
)
90+
91+
// old, ok := oldObj.(*GCPMachinePool)
92+
// if !ok {
93+
// return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a GCPMachinePool but got a %T", oldObj))
94+
// }
95+
96+
// r, ok := newObj.(*GCPMachinePool)
97+
// if !ok {
98+
// return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a GCPMachinePool but got a %T", newObj))
99+
// }
100+
101+
// gcpMachinePoolLog.Info("Validating GCPMachinePool update", "name", r.Name)
102+
103+
// if !reflect.DeepEqual(r.Spec, old.Spec) {
104+
// return nil, apierrors.NewBadRequest("GCPMachinePool.Spec is immutable")
105+
// }
106+
107+
// return nil, nil
81108
}
82109

83110
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type.

exp/api/v1beta1/zz_generated.deepcopy.go

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

exp/controllers/gcpmachinepool_controller.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ package controllers
2020
import (
2121
"context"
2222
"fmt"
23+
"strings"
24+
"time"
2325

2426
"github.com/google/go-cmp/cmp"
2527
corev1 "k8s.io/api/core/v1"
@@ -226,7 +228,8 @@ func (r *GCPMachinePoolReconciler) reconcile(ctx context.Context, machinePoolSco
226228
// set the InstanceTemplateReadyCondition condition
227229
conditions.MarkTrue(machinePoolScope.GCPMachinePool, expinfrav1.InstanceTemplateReadyCondition)
228230

229-
if err := instancegroupmanagers.New(machinePoolScope).Reconcile(ctx, instanceTemplateKey); err != nil {
231+
igm, err := instancegroupmanagers.New(machinePoolScope).Reconcile(ctx, instanceTemplateKey)
232+
if err != nil {
230233
log.Error(err, "Error reconciling instanceGroupManager")
231234
// record.Warnf(machineScope.GCPMachine, "GCPMachineReconcile", "Reconcile error - %v", err)
232235
conditions.MarkUnknown(machinePoolScope.GCPMachinePool, expinfrav1.MIGReadyCondition, expinfrav1.MIGNotFoundReason, "%s", err.Error())
@@ -236,7 +239,44 @@ func (r *GCPMachinePoolReconciler) reconcile(ctx context.Context, machinePoolSco
236239
// set the MIGReadyCondition condition
237240
conditions.MarkTrue(machinePoolScope.GCPMachinePool, expinfrav1.MIGReadyCondition)
238241

239-
return ctrl.Result{}, nil
242+
igmInstances, err := instancegroupmanagers.New(machinePoolScope).ListInstances(ctx, igm)
243+
if err != nil {
244+
log.Error(err, "Error listing instances in instanceGroupManager")
245+
return ctrl.Result{}, err
246+
}
247+
248+
providerIDList := make([]string, len(igmInstances))
249+
250+
for i, instance := range igmInstances {
251+
providerID := ""
252+
253+
// Convert instance URL to providerID format
254+
u := instance.Instance
255+
u = strings.TrimPrefix(u, "https://www.googleapis.com/compute/v1/")
256+
tokens := strings.Split(u, "/")
257+
if len(tokens) == 6 && tokens[0] == "projects" && tokens[2] == "zones" && tokens[4] == "instances" {
258+
providerID = fmt.Sprintf("gce://%s/%s/%s", tokens[1], tokens[3], tokens[5])
259+
} else {
260+
return ctrl.Result{}, fmt.Errorf("unexpected instance URL format: %s", instance.Instance)
261+
}
262+
263+
providerIDList[i] = providerID
264+
}
265+
266+
// FUTURE: do we need to verify that the instances are actually running / set ProviderIDList?
267+
machinePoolScope.GCPMachinePool.Spec.ProviderIDList = providerIDList
268+
machinePoolScope.GCPMachinePool.Status.Replicas = int32(len(providerIDList))
269+
machinePoolScope.GCPMachinePool.Status.Ready = true
270+
271+
return ctrl.Result{
272+
// Regularly update `AWSMachine` objects, for example if ASG was scaled or refreshed instances
273+
// TODO: Requeueing interval can be removed or prolonged once reconciliation of ASG EC2 instances
274+
// can be triggered by events (e.g. with feature gate `EventBridgeInstanceState`).
275+
// See https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/5323.
276+
RequeueAfter: 1 * time.Minute,
277+
}, nil
278+
279+
// return ctrl.Result{}, nil
240280
}
241281

242282
func (r *GCPMachinePoolReconciler) reconcileDelete(ctx context.Context, machinePoolScope *scope.MachinePoolScope) error {

0 commit comments

Comments
 (0)