Skip to content

Commit 3b50513

Browse files
stephenfinMaysaMacedomandre
committed
cluster-api: Implement machine generation for CAPO
Signed-off-by: Stephen Finucane <[email protected]> Co-authored-by: Maysa Macedo <[email protected]> Co-authored-by: Martin André <[email protected]>
1 parent 98afd8e commit 3b50513

File tree

3 files changed

+246
-1
lines changed

3 files changed

+246
-1
lines changed

pkg/asset/machines/clusterapi.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@ import (
2323
"github.com/openshift/installer/pkg/asset/installconfig"
2424
"github.com/openshift/installer/pkg/asset/machines/aws"
2525
"github.com/openshift/installer/pkg/asset/machines/gcp"
26+
"github.com/openshift/installer/pkg/asset/machines/openstack"
2627
vspherecapi "github.com/openshift/installer/pkg/asset/machines/vsphere"
2728
"github.com/openshift/installer/pkg/asset/manifests/capiutils"
2829
"github.com/openshift/installer/pkg/asset/rhcos"
2930
"github.com/openshift/installer/pkg/clusterapi"
31+
rhcosutils "github.com/openshift/installer/pkg/rhcos"
3032
awstypes "github.com/openshift/installer/pkg/types/aws"
3133
awsdefaults "github.com/openshift/installer/pkg/types/aws/defaults"
3234
azuretypes "github.com/openshift/installer/pkg/types/azure"
3335
gcptypes "github.com/openshift/installer/pkg/types/gcp"
36+
openstacktypes "github.com/openshift/installer/pkg/types/openstack"
3437
vspheretypes "github.com/openshift/installer/pkg/types/vsphere"
3538
)
3639

@@ -293,6 +296,27 @@ func (c *ClusterAPI) Generate(dependencies asset.Parents) error {
293296
if err != nil {
294297
return fmt.Errorf("unable to generate CAPI machines for vSphere %w", err)
295298
}
299+
case openstacktypes.Name:
300+
mpool := defaultOpenStackMachinePoolPlatform()
301+
mpool.Set(ic.Platform.OpenStack.DefaultMachinePlatform)
302+
mpool.Set(pool.Platform.OpenStack)
303+
pool.Platform.OpenStack = &mpool
304+
305+
imageName, _ := rhcosutils.GenerateOpenStackImageName(string(*rhcosImage), clusterID.InfraID)
306+
307+
for _, role := range []string{"master", "bootstrap"} {
308+
openStackMachines, err := openstack.GenerateMachines(
309+
clusterID.InfraID,
310+
ic,
311+
&pool,
312+
imageName,
313+
role,
314+
)
315+
if err != nil {
316+
return fmt.Errorf("failed to create machine objects: %w", err)
317+
}
318+
c.FileList = append(c.FileList, openStackMachines...)
319+
}
296320
default:
297321
// TODO: support other platforms
298322
}

pkg/asset/machines/openstack/machines.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ func generateProviderSpec(clusterID string, platform *openstack.Platform, mpool
203203
Subnets: []machinev1alpha1.SubnetParam{
204204
{
205205
Filter: machinev1alpha1.SubnetFilter{
206-
Name: fmt.Sprintf("%s-nodes", clusterID),
207206
Tags: fmt.Sprintf("openshiftClusterID=%s", clusterID),
208207
},
209208
},
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// Package openstack generates Machine objects for openstack.
2+
package openstack
3+
4+
import (
5+
"fmt"
6+
7+
v1 "k8s.io/api/core/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/utils/ptr"
10+
capo "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha7"
11+
capi "sigs.k8s.io/cluster-api/api/v1beta1"
12+
13+
machinev1 "github.com/openshift/api/machine/v1"
14+
"github.com/openshift/installer/pkg/asset"
15+
"github.com/openshift/installer/pkg/asset/manifests/capiutils"
16+
"github.com/openshift/installer/pkg/types"
17+
"github.com/openshift/installer/pkg/types/openstack"
18+
)
19+
20+
// GenerateMachines returns manifests and runtime objects to provision the control plane (including bootstrap, if applicable) nodes using CAPI.
21+
func GenerateMachines(clusterID string, config *types.InstallConfig, pool *types.MachinePool, osImage, role string) ([]*asset.RuntimeFile, error) {
22+
if configPlatform := config.Platform.Name(); configPlatform != openstack.Name {
23+
return nil, fmt.Errorf("non-OpenStack configuration: %q", configPlatform)
24+
}
25+
if poolPlatform := pool.Platform.Name(); poolPlatform != openstack.Name {
26+
return nil, fmt.Errorf("non-OpenStack machine-pool: %q", poolPlatform)
27+
}
28+
29+
mpool := pool.Platform.OpenStack
30+
trunkSupport, err := checkNetworkExtensionAvailability(config.Platform.OpenStack.Cloud, "trunk", nil)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
total := int64(1)
36+
if role == "master" && pool.Replicas != nil {
37+
total = *pool.Replicas
38+
}
39+
40+
var result []*asset.RuntimeFile
41+
failureDomains := failureDomainsFromSpec(*mpool)
42+
for idx := int64(0); idx < total; idx++ {
43+
failureDomain := failureDomains[uint(idx)%uint(len(failureDomains))]
44+
machineSpec, err := generateMachineSpec(
45+
clusterID,
46+
config.Platform.OpenStack,
47+
mpool,
48+
osImage,
49+
role,
50+
trunkSupport,
51+
failureDomain,
52+
)
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
machineName := fmt.Sprintf("%s-%s-%d", clusterID, pool.Name, idx)
58+
machineLabels := map[string]string{
59+
"cluster.x-k8s.io/control-plane": "",
60+
}
61+
if role == "bootstrap" {
62+
machineName = capiutils.GenerateBoostrapMachineName(clusterID)
63+
machineLabels = map[string]string{
64+
"cluster.x-k8s.io/control-plane": "",
65+
"install.openshift.io/bootstrap": "",
66+
}
67+
}
68+
openStackMachine := &capo.OpenStackMachine{
69+
ObjectMeta: metav1.ObjectMeta{
70+
Name: machineName,
71+
Labels: machineLabels,
72+
},
73+
Spec: *machineSpec,
74+
}
75+
openStackMachine.SetGroupVersionKind(capo.GroupVersion.WithKind("OpenStackMachine"))
76+
77+
result = append(result, &asset.RuntimeFile{
78+
File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", openStackMachine.Name)},
79+
Object: openStackMachine,
80+
})
81+
82+
// The instanceSpec used to create the server uses the failureDomain from CAPI Machine
83+
// defined bellow. This field must match a Key on FailureDomains stored in the cluster.
84+
// https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/main/controllers/openstackmachine_controller.go#L472
85+
// TODO (maysa): test this
86+
machine := &capi.Machine{
87+
ObjectMeta: metav1.ObjectMeta{
88+
Name: openStackMachine.Name,
89+
Labels: map[string]string{
90+
"cluster.x-k8s.io/control-plane": "",
91+
},
92+
},
93+
Spec: capi.MachineSpec{
94+
ClusterName: clusterID,
95+
Bootstrap: capi.Bootstrap{
96+
DataSecretName: ptr.To(fmt.Sprintf("%s-%s", clusterID, role)),
97+
},
98+
InfrastructureRef: v1.ObjectReference{
99+
APIVersion: "infrastructure.cluster.x-k8s.io/v1alpha7",
100+
Kind: "OpenStackMachine",
101+
Name: openStackMachine.Name,
102+
},
103+
FailureDomain: &failureDomain.AvailabilityZone,
104+
},
105+
}
106+
machine.SetGroupVersionKind(capi.GroupVersion.WithKind("Machine"))
107+
108+
result = append(result, &asset.RuntimeFile{
109+
File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", machine.Name)},
110+
Object: machine,
111+
})
112+
}
113+
return result, nil
114+
}
115+
116+
func generateMachineSpec(clusterID string, platform *openstack.Platform, mpool *openstack.MachinePool, osImage string, role string, trunkSupport bool, failureDomain machinev1.OpenStackFailureDomain) (*capo.OpenStackMachineSpec, error) {
117+
port := capo.PortOpts{}
118+
119+
addressPairs := []capo.AddressPair{}
120+
for _, apiVIP := range platform.APIVIPs {
121+
addressPairs = append(addressPairs, capo.AddressPair{IPAddress: apiVIP})
122+
}
123+
for _, ingressVIP := range platform.IngressVIPs {
124+
addressPairs = append(addressPairs, capo.AddressPair{IPAddress: ingressVIP})
125+
}
126+
127+
if platform.ControlPlanePort != nil {
128+
port.Network = &capo.NetworkFilter{
129+
Name: platform.ControlPlanePort.Network.Name,
130+
ID: platform.ControlPlanePort.Network.ID,
131+
}
132+
133+
var fixedIPs []capo.FixedIP
134+
for _, fixedIP := range platform.ControlPlanePort.FixedIPs {
135+
fixedIPs = append(fixedIPs, capo.FixedIP{
136+
Subnet: &capo.SubnetFilter{
137+
ID: fixedIP.Subnet.ID,
138+
Name: fixedIP.Subnet.Name,
139+
}})
140+
}
141+
port.FixedIPs = fixedIPs
142+
if len(addressPairs) > 0 {
143+
port.AllowedAddressPairs = addressPairs
144+
}
145+
} else {
146+
port = capo.PortOpts{
147+
FixedIPs: []capo.FixedIP{
148+
{
149+
Subnet: &capo.SubnetFilter{
150+
// NOTE(mandre) the format of the subnet name changes when letting CAPI create it.
151+
// So solely rely on tags for now.
152+
Tags: fmt.Sprintf("openshiftClusterID=%s", clusterID),
153+
},
154+
},
155+
},
156+
}
157+
if len(addressPairs) > 0 {
158+
port.AllowedAddressPairs = addressPairs
159+
}
160+
}
161+
162+
additionalPorts := make([]capo.PortOpts, 0, len(mpool.AdditionalNetworkIDs))
163+
for _, networkID := range mpool.AdditionalNetworkIDs {
164+
additionalPorts = append(additionalPorts, capo.PortOpts{
165+
Network: &capo.NetworkFilter{
166+
ID: networkID,
167+
},
168+
})
169+
}
170+
171+
securityGroups := []capo.SecurityGroupFilter{
172+
{
173+
// Bootstrap and Master share the same security group
174+
Name: fmt.Sprintf("%s-master", clusterID),
175+
},
176+
}
177+
178+
for _, securityGroup := range mpool.AdditionalSecurityGroupIDs {
179+
securityGroups = append(securityGroups, capo.SecurityGroupFilter{ID: securityGroup})
180+
}
181+
182+
// FIXME: Uncomment when the server group rework merged
183+
// https://github.com/kubernetes-sigs/cluster-api-provider-openstack/pull/1779
184+
// serverGroupName := clusterID + "-" + role
185+
spec := capo.OpenStackMachineSpec{
186+
CloudName: CloudName,
187+
Flavor: mpool.FlavorName,
188+
// TODO(stephenfin): Create credentials
189+
IdentityRef: &capo.OpenStackIdentityReference{
190+
Kind: "Secret",
191+
Name: "openstack-cloud-credentials",
192+
},
193+
// FIXME(stephenfin): We probably want a FIP for bootstrap?
194+
// TODO: This is an image name. Migrate to a filter with Name when API v1alpha8 is released.
195+
Image: osImage,
196+
Ports: append([]capo.PortOpts{port}, additionalPorts...),
197+
SecurityGroups: securityGroups,
198+
// FIXME: Uncomment when the server group rework merged
199+
// https://github.com/kubernetes-sigs/cluster-api-provider-openstack/pull/1779
200+
//ServerGroup: *capo.ServerGroupFilter{
201+
// "Name": serverGroupName,
202+
// },
203+
ServerMetadata: map[string]string{
204+
"Name": fmt.Sprintf("%s-%s", clusterID, role),
205+
"openshiftClusterID": clusterID,
206+
},
207+
Trunk: trunkSupport,
208+
Tags: []string{
209+
fmt.Sprintf("openshiftClusterID=%s", clusterID),
210+
},
211+
}
212+
213+
if mpool.RootVolume != nil {
214+
spec.RootVolume = &capo.RootVolume{
215+
Size: mpool.RootVolume.Size,
216+
VolumeType: failureDomain.RootVolume.VolumeType,
217+
AvailabilityZone: failureDomain.RootVolume.AvailabilityZone,
218+
}
219+
}
220+
221+
return &spec, nil
222+
}

0 commit comments

Comments
 (0)