Skip to content

Commit 6801574

Browse files
authored
Merge pull request #1506 from justinsb/machinepool
Minimal MachinePool support
2 parents 7e551ab + 793ea56 commit 6801574

27 files changed

Lines changed: 2580 additions & 119 deletions

api/v1beta1/conditions_consts.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
Copyright 2025 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 v1beta1
18+
19+
const (
20+
// WaitingForClusterInfrastructureReason used when machine is waiting for cluster infrastructure to be ready before proceeding.
21+
WaitingForClusterInfrastructureReason = "WaitingForClusterInfrastructure"
22+
// WaitingForBootstrapDataReason used when machine is waiting for bootstrap data to be ready before proceeding.
23+
WaitingForBootstrapDataReason = "WaitingForBootstrapData"
24+
)

api/v1beta1/labels.go

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

115115
// InternalRoleTagValue describes the value for the internal role.
116116
InternalRoleTagValue = "api-internal"
117+
118+
// ConfigHashKey holds the full hash of the desired state of a resource.
119+
// Note that label values are limited to 63 characters in GCP, so we can use
120+
// base32 encoding if we want to store a full sha256 hash.
121+
ConfigHashKey = NameGCPProviderPrefix + "config-hash"
117122
)
118123

119124
// ClusterTagKey generates the key for resources associated with a cluster.

cloud/interfaces.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ type MachineGetter interface {
9090
ControlPlaneGroupName() string
9191
GetInstanceID() *string
9292
GetProviderID() string
93-
GetBootstrapData() (string, error)
93+
GetBootstrapData(ctx context.Context) (string, error)
9494
GetInstanceStatus() *infrav1.InstanceStatus
9595
}
9696

cloud/scope/machine.go

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"strings"
2626

2727
"github.com/go-logr/logr"
28-
2928
"github.com/pkg/errors"
3029
"golang.org/x/mod/semver"
3130
"google.golang.org/api/compute/v1"
@@ -41,6 +40,13 @@ import (
4140
"sigs.k8s.io/controller-runtime/pkg/client"
4241
)
4342

43+
// Constants for GCP OnHostMaintenance values.
44+
// These are not exported, because they are not _our_ API, but they are used in multiple places.
45+
const (
46+
onHostMaintenanceTerminate = "TERMINATE"
47+
onHostMaintenanceMigrate = "MIGRATE"
48+
)
49+
4450
// MachineScopeParams defines the input parameters used to create a new MachineScope.
4551
type MachineScopeParams struct {
4652
Client client.Client
@@ -320,12 +326,12 @@ func instanceAdditionalDiskSpec(ctx context.Context, spec []infrav1.AttachedDisk
320326
}
321327

322328
// InstanceNetworkInterfaceSpec returns compute network interface spec.
323-
func (m *MachineScope) InstanceNetworkInterfaceSpec() *compute.NetworkInterface {
329+
func InstanceNetworkInterfaceSpec(cluster cloud.ClusterGetter, publicIP *bool, subnet *string, aliasIPRanges []infrav1.AliasIPRange) *compute.NetworkInterface {
324330
networkInterface := &compute.NetworkInterface{
325-
Network: path.Join("projects", m.ClusterGetter.NetworkProject(), "global", "networks", m.ClusterGetter.NetworkName()),
331+
Network: path.Join("projects", cluster.NetworkProject(), "global", "networks", cluster.NetworkName()),
326332
}
327333

328-
if m.GCPMachine.Spec.PublicIP != nil && *m.GCPMachine.Spec.PublicIP {
334+
if publicIP != nil && *publicIP {
329335
networkInterface.AccessConfigs = []*compute.AccessConfig{
330336
{
331337
Type: "ONE_TO_ONE_NAT",
@@ -334,22 +340,22 @@ func (m *MachineScope) InstanceNetworkInterfaceSpec() *compute.NetworkInterface
334340
}
335341
}
336342

337-
if m.GCPMachine.Spec.Subnet != nil {
338-
networkInterface.Subnetwork = path.Join("projects", m.ClusterGetter.NetworkProject(), "regions", m.ClusterGetter.Region(), "subnetworks", *m.GCPMachine.Spec.Subnet)
343+
if subnet != nil {
344+
networkInterface.Subnetwork = path.Join("projects", cluster.NetworkProject(), "regions", cluster.Region(), "subnetworks", *subnet)
339345
}
340346

341-
networkInterface.AliasIpRanges = m.InstanceNetworkInterfaceAliasIPRangesSpec()
347+
networkInterface.AliasIpRanges = InstanceNetworkInterfaceAliasIPRangesSpec(aliasIPRanges)
342348

343349
return networkInterface
344350
}
345351

346352
// InstanceNetworkInterfaceAliasIPRangesSpec returns a slice of Alias IP Range specs.
347-
func (m *MachineScope) InstanceNetworkInterfaceAliasIPRangesSpec() []*compute.AliasIpRange {
348-
if len(m.GCPMachine.Spec.AliasIPRanges) == 0 {
353+
func InstanceNetworkInterfaceAliasIPRangesSpec(spec []infrav1.AliasIPRange) []*compute.AliasIpRange {
354+
if len(spec) == 0 {
349355
return nil
350356
}
351-
aliasIPRanges := make([]*compute.AliasIpRange, 0, len(m.GCPMachine.Spec.AliasIPRanges))
352-
for _, alias := range m.GCPMachine.Spec.AliasIPRanges {
357+
aliasIPRanges := make([]*compute.AliasIpRange, 0, len(spec))
358+
for _, alias := range spec {
353359
aliasIPRange := &compute.AliasIpRange{
354360
IpCidrRange: alias.IPCidrRange,
355361
SubnetworkRangeName: alias.SubnetworkRangeName,
@@ -377,9 +383,9 @@ func instanceServiceAccountsSpec(serviceAccount *infrav1.ServiceAccount) *comput
377383
}
378384

379385
// InstanceAdditionalMetadataSpec returns additional metadata spec.
380-
func (m *MachineScope) InstanceAdditionalMetadataSpec() *compute.Metadata {
386+
func InstanceAdditionalMetadataSpec(spec []infrav1.MetadataItem) *compute.Metadata {
381387
metadata := new(compute.Metadata)
382-
for _, additionalMetadata := range m.GCPMachine.Spec.AdditionalMetadata {
388+
for _, additionalMetadata := range spec {
383389
metadata.Items = append(metadata.Items, &compute.MetadataItems{
384390
Key: additionalMetadata.Key,
385391
Value: additionalMetadata.Value,
@@ -469,9 +475,9 @@ func (m *MachineScope) InstanceSpec(log logr.Logger) *compute.Instance {
469475
if m.GCPMachine.Spec.OnHostMaintenance != nil {
470476
switch *m.GCPMachine.Spec.OnHostMaintenance {
471477
case infrav1.HostMaintenancePolicyMigrate:
472-
instance.Scheduling.OnHostMaintenance = "MIGRATE"
478+
instance.Scheduling.OnHostMaintenance = onHostMaintenanceMigrate
473479
case infrav1.HostMaintenancePolicyTerminate:
474-
instance.Scheduling.OnHostMaintenance = "TERMINATE"
480+
instance.Scheduling.OnHostMaintenance = onHostMaintenanceTerminate
475481
default:
476482
log.Error(errors.New("Invalid value"), "Unknown OnHostMaintenance value", "Spec.OnHostMaintenance", *m.GCPMachine.Spec.OnHostMaintenance)
477483
}
@@ -496,12 +502,13 @@ func (m *MachineScope) InstanceSpec(log logr.Logger) *compute.Instance {
496502

497503
instance.Disks = append(instance.Disks, m.InstanceImageSpec())
498504
instance.Disks = append(instance.Disks, instanceAdditionalDiskSpec(ctx, m.GCPMachine.Spec.AdditionalDisks, m.GCPMachine.Spec.RootDiskEncryptionKey, m.Zone(), m.ResourceManagerTags())...)
499-
instance.Metadata = m.InstanceAdditionalMetadataSpec()
505+
506+
instance.Metadata = InstanceAdditionalMetadataSpec(m.GCPMachine.Spec.AdditionalMetadata)
500507
instance.ServiceAccounts = append(instance.ServiceAccounts, instanceServiceAccountsSpec(m.GCPMachine.Spec.ServiceAccount))
501-
instance.NetworkInterfaces = append(instance.NetworkInterfaces, m.InstanceNetworkInterfaceSpec())
508+
instance.NetworkInterfaces = append(instance.NetworkInterfaces, InstanceNetworkInterfaceSpec(m.ClusterGetter, m.GCPMachine.Spec.PublicIP, m.GCPMachine.Spec.Subnet, m.GCPMachine.Spec.AliasIPRanges))
502509
instance.GuestAccelerators = instanceGuestAcceleratorsSpec(m.GCPMachine.Spec.GuestAccelerators)
503510
if len(instance.GuestAccelerators) > 0 {
504-
instance.Scheduling.OnHostMaintenance = "TERMINATE"
511+
instance.Scheduling.OnHostMaintenance = onHostMaintenanceTerminate
505512
}
506513

507514
return instance
@@ -510,15 +517,20 @@ func (m *MachineScope) InstanceSpec(log logr.Logger) *compute.Instance {
510517
// ANCHOR_END: MachineInstanceSpec
511518

512519
// GetBootstrapData returns the bootstrap data from the secret in the Machine's bootstrap.dataSecretName.
513-
func (m *MachineScope) GetBootstrapData() (string, error) {
514-
if m.Machine.Spec.Bootstrap.DataSecretName == nil {
520+
func (m *MachineScope) GetBootstrapData(ctx context.Context) (string, error) {
521+
return GetBootstrapData(ctx, m.client, m.Machine, m.Machine.Spec.Bootstrap)
522+
}
523+
524+
// GetBootstrapData returns the bootstrap data from the secret in the Machine's bootstrap.dataSecretName.
525+
func GetBootstrapData(ctx context.Context, client client.Client, parent client.Object, bootstrap clusterv1.Bootstrap) (string, error) {
526+
if bootstrap.DataSecretName == nil {
515527
return "", errors.New("error retrieving bootstrap data: linked Machine's bootstrap.dataSecretName is nil")
516528
}
517529

518530
secret := &corev1.Secret{}
519-
key := types.NamespacedName{Namespace: m.Namespace(), Name: *m.Machine.Spec.Bootstrap.DataSecretName}
520-
if err := m.client.Get(context.TODO(), key, secret); err != nil {
521-
return "", errors.Wrapf(err, "failed to retrieve bootstrap data secret for GCPMachine %s/%s", m.Namespace(), m.Name())
531+
key := types.NamespacedName{Namespace: parent.GetNamespace(), Name: *bootstrap.DataSecretName}
532+
if err := client.Get(ctx, key, secret); err != nil {
533+
return "", errors.Wrapf(err, "failed to retrieve bootstrap data secret %s/%s", key.Namespace, key.Name)
522534
}
523535

524536
value, ok := secret.Data["value"]

cloud/scope/machine_test.go

Lines changed: 4 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -78,39 +78,14 @@ func TestMachineLocalSSDDiskType(t *testing.T) {
7878

7979
// TestInstanceNetworkInterfaceAliasIPRangesSpec tests the InstanceNetworkInterfaceAliasIPRangesSpec function
8080
func TestInstanceNetworkInterfaceAliasIPRangesSpec(t *testing.T) {
81-
// Register the GCPMachine and GCPMachineList in a schema.
82-
schema, err := infrav1.SchemeBuilder.Register(&infrav1.GCPMachine{}, &infrav1.GCPMachineList{}).Build()
83-
assert.Nil(t, err)
84-
85-
// Create a controller fake client.
86-
testClient := fake.NewClientBuilder().WithScheme(schema).Build()
87-
88-
// Test machine parameter
89-
failureDomain := "us-central1-a"
90-
testMachine := clusterv1.Machine{
91-
Spec: clusterv1.MachineSpec{
92-
FailureDomain: failureDomain,
93-
},
94-
}
95-
9681
t.Run("should return nil for empty alias IP ranges", func(t *testing.T) {
9782
testGCPMachine := infrav1.GCPMachine{
9883
Spec: infrav1.GCPMachineSpec{
9984
AliasIPRanges: []infrav1.AliasIPRange{},
10085
},
10186
}
10287

103-
testScopeParams := MachineScopeParams{
104-
Client: testClient,
105-
Machine: &testMachine,
106-
GCPMachine: &testGCPMachine,
107-
}
108-
109-
testMachineScope, err := NewMachineScope(testScopeParams)
110-
assert.Nil(t, err)
111-
assert.NotNil(t, testMachineScope)
112-
113-
result := testMachineScope.InstanceNetworkInterfaceAliasIPRangesSpec()
88+
result := InstanceNetworkInterfaceAliasIPRangesSpec(testGCPMachine.Spec.AliasIPRanges)
11489
assert.Nil(t, result)
11590
})
11691

@@ -126,17 +101,7 @@ func TestInstanceNetworkInterfaceAliasIPRangesSpec(t *testing.T) {
126101
},
127102
}
128103

129-
testScopeParams := MachineScopeParams{
130-
Client: testClient,
131-
Machine: &testMachine,
132-
GCPMachine: &testGCPMachine,
133-
}
134-
135-
testMachineScope, err := NewMachineScope(testScopeParams)
136-
assert.Nil(t, err)
137-
assert.NotNil(t, testMachineScope)
138-
139-
result := testMachineScope.InstanceNetworkInterfaceAliasIPRangesSpec()
104+
result := InstanceNetworkInterfaceAliasIPRangesSpec(testGCPMachine.Spec.AliasIPRanges)
140105
assert.NotNil(t, result)
141106
assert.Len(t, result, 1)
142107
assert.Equal(t, "10.0.0.0/24", result[0].IpCidrRange)
@@ -159,17 +124,7 @@ func TestInstanceNetworkInterfaceAliasIPRangesSpec(t *testing.T) {
159124
},
160125
}
161126

162-
testScopeParams := MachineScopeParams{
163-
Client: testClient,
164-
Machine: &testMachine,
165-
GCPMachine: &testGCPMachine,
166-
}
167-
168-
testMachineScope, err := NewMachineScope(testScopeParams)
169-
assert.Nil(t, err)
170-
assert.NotNil(t, testMachineScope)
171-
172-
result := testMachineScope.InstanceNetworkInterfaceAliasIPRangesSpec()
127+
result := InstanceNetworkInterfaceAliasIPRangesSpec(testGCPMachine.Spec.AliasIPRanges)
173128
assert.NotNil(t, result)
174129
assert.Len(t, result, 2)
175130
assert.Equal(t, "10.0.0.0/24", result[0].IpCidrRange)
@@ -190,17 +145,7 @@ func TestInstanceNetworkInterfaceAliasIPRangesSpec(t *testing.T) {
190145
},
191146
}
192147

193-
testScopeParams := MachineScopeParams{
194-
Client: testClient,
195-
Machine: &testMachine,
196-
GCPMachine: &testGCPMachine,
197-
}
198-
199-
testMachineScope, err := NewMachineScope(testScopeParams)
200-
assert.Nil(t, err)
201-
assert.NotNil(t, testMachineScope)
202-
203-
result := testMachineScope.InstanceNetworkInterfaceAliasIPRangesSpec()
148+
result := InstanceNetworkInterfaceAliasIPRangesSpec(testGCPMachine.Spec.AliasIPRanges)
204149
assert.NotNil(t, result)
205150
assert.Len(t, result, 1)
206151
assert.Equal(t, "10.100.0.0/24", result[0].IpCidrRange)

0 commit comments

Comments
 (0)