github.com/openshift/installer@v1.4.17/pkg/asset/machines/vsphere/capimachines.go (about) 1 package vsphere 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "strings" 9 10 "github.com/sirupsen/logrus" 11 v1 "k8s.io/api/core/v1" 12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 "k8s.io/apimachinery/pkg/runtime" 14 "k8s.io/utils/ptr" 15 capv "sigs.k8s.io/cluster-api-provider-vsphere/apis/v1beta1" 16 capi "sigs.k8s.io/cluster-api/api/v1beta1" 17 18 machinev1 "github.com/openshift/api/machine/v1beta1" 19 "github.com/openshift/installer/pkg/asset" 20 "github.com/openshift/installer/pkg/asset/installconfig/vsphere" 21 "github.com/openshift/installer/pkg/asset/manifests/capiutils" 22 "github.com/openshift/installer/pkg/types" 23 "github.com/openshift/installer/pkg/utils" 24 ) 25 26 const ( 27 masterRole = "master" 28 ) 29 30 // ProviderSpecFromRawExtension unmarshals the JSON-encoded spec. 31 func ProviderSpecFromRawExtension(rawExtension *runtime.RawExtension) (*machinev1.VSphereMachineProviderSpec, error) { 32 if rawExtension == nil { 33 return &machinev1.VSphereMachineProviderSpec{}, nil 34 } 35 36 spec := new(machinev1.VSphereMachineProviderSpec) 37 if err := json.Unmarshal(rawExtension.Raw, &spec); err != nil { 38 return nil, fmt.Errorf("error unmarshalling providerSpec: %w", err) 39 } 40 41 return spec, nil 42 } 43 44 func getNetworkInventoryPath(vcenterContext vsphere.VCenterContext, networkName string, providerSpec *machinev1.VSphereMachineProviderSpec) (string, error) { 45 // if networkName is a path, we'll assume that a full path was provided by the admin 46 if strings.Contains(networkName, "/") { 47 return networkName, nil 48 } 49 50 // else, we'll dereference the network name to a full path using the resource pool 51 for _, clusterNetworkMap := range vcenterContext.ClusterNetworkMap { 52 if _, networkInContext := clusterNetworkMap.NetworkNames[networkName]; !networkInContext { 53 continue 54 } 55 56 for _, resourcePool := range clusterNetworkMap.ResourcePools { 57 if resourcePool.InventoryPath == providerSpec.Workspace.ResourcePool { 58 return clusterNetworkMap.NetworkNames[networkName], nil 59 } 60 } 61 62 // This is a case for UPI (terraform or powercli) the resource pool will not exist 63 // prior to running openshift-install create manifests. 64 // This also will keep backward compatibility as this was not required to CAPI implementation. 65 if strings.Contains(providerSpec.Workspace.ResourcePool, clusterNetworkMap.Cluster) { 66 logrus.Debugf("using cluster %s as selector for network device path %s", clusterNetworkMap.Cluster, networkName) 67 return clusterNetworkMap.NetworkNames[networkName], nil 68 } 69 } 70 return "", fmt.Errorf("unable to find network %s in resource pool %s", networkName, providerSpec.Workspace.ResourcePool) 71 } 72 73 // GenerateMachines returns a list of capi machines. 74 func GenerateMachines(ctx context.Context, clusterID string, config *types.InstallConfig, pool *types.MachinePool, osImage string, role string, metadata *vsphere.Metadata) ([]*asset.RuntimeFile, error) { 75 data, err := Machines(clusterID, config, pool, osImage, role, "") 76 if err != nil { 77 return nil, fmt.Errorf("unable to retrieve machines: %w", err) 78 } 79 machines := data.Machines 80 81 capvMachines := make([]*capv.VSphereMachine, 0, len(machines)) 82 result := make([]*asset.RuntimeFile, 0, len(machines)) 83 staticIP := false 84 85 for mIndex, machine := range machines { 86 providerSpec, ok := machine.Spec.ProviderSpec.Value.Object.(*machinev1.VSphereMachineProviderSpec) 87 if !ok { 88 return nil, errors.New("unable to convert ProviderSpec to VSphereMachineProviderSpec") 89 } 90 91 vcenterContext := metadata.VCenterContexts[providerSpec.Workspace.Server] 92 resourcePool := providerSpec.Workspace.ResourcePool 93 94 customVMXKeys := map[string]string{ 95 "guestinfo.hostname": machine.Name, 96 "guestinfo.domain": strings.TrimSuffix(config.ClusterDomain(), "."), 97 "stealclock.enable": "TRUE", 98 } 99 100 capvNetworkDevices := []capv.NetworkDeviceSpec{} 101 for _, networkDevice := range providerSpec.Network.Devices { 102 networkName, err := getNetworkInventoryPath(vcenterContext, networkDevice.NetworkName, providerSpec) 103 if err != nil { 104 return nil, fmt.Errorf("unable to get network inventory path: %w", err) 105 } 106 deviceSpec := capv.NetworkDeviceSpec{ 107 NetworkName: networkName, 108 DHCP4: true, 109 } 110 111 // Static IP configured. Add kargs. 112 if len(networkDevice.AddressesFromPools) > 0 { 113 staticIP = true 114 kargs, err := utils.ConstructNetworkKargsFromMachine(data.IPClaims, data.IPAddresses, &machines[mIndex], networkDevice) 115 if err != nil { 116 return nil, fmt.Errorf("unable to get static ip config for machine %v: %w", machine.Name, err) 117 } 118 customVMXKeys["guestinfo.afterburn.initrd.network-kargs"] = kargs 119 } 120 capvNetworkDevices = append(capvNetworkDevices, deviceSpec) 121 } 122 123 vsphereMachine := &capv.VSphereMachine{ 124 ObjectMeta: metav1.ObjectMeta{ 125 Namespace: capiutils.Namespace, 126 Name: machine.Name, 127 Labels: map[string]string{ 128 "cluster.x-k8s.io/control-plane": "", 129 }, 130 }, 131 Spec: capv.VSphereMachineSpec{ 132 VirtualMachineCloneSpec: capv.VirtualMachineCloneSpec{ 133 CloneMode: capv.FullClone, 134 CustomVMXKeys: customVMXKeys, 135 Network: capv.NetworkSpec{ 136 Devices: capvNetworkDevices, 137 }, 138 Folder: providerSpec.Workspace.Folder, 139 Template: providerSpec.Template, 140 Datacenter: providerSpec.Workspace.Datacenter, 141 Server: providerSpec.Workspace.Server, 142 NumCPUs: providerSpec.NumCPUs, 143 NumCoresPerSocket: providerSpec.NumCoresPerSocket, 144 MemoryMiB: providerSpec.MemoryMiB, 145 DiskGiB: providerSpec.DiskGiB, 146 Datastore: providerSpec.Workspace.Datastore, 147 ResourcePool: resourcePool, 148 }, 149 }, 150 } 151 vsphereMachine.SetGroupVersionKind(capv.GroupVersion.WithKind("VSphereMachine")) 152 capvMachines = append(capvMachines, vsphereMachine) 153 154 result = append(result, &asset.RuntimeFile{ 155 File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", vsphereMachine.Name)}, 156 Object: vsphereMachine, 157 }) 158 159 // Need to determine the infrastructure ref since there may be multi vcenters. 160 clusterName := clusterID 161 for index, vcenter := range config.Platform.VSphere.VCenters { 162 if vcenter.Server == providerSpec.Workspace.Server { 163 clusterName = fmt.Sprintf("%v-%d", clusterID, index) 164 break 165 } 166 } 167 168 // Create capi machine for vspheremachine 169 machine := &capi.Machine{ 170 ObjectMeta: metav1.ObjectMeta{ 171 Namespace: capiutils.Namespace, 172 Name: vsphereMachine.Name, 173 Labels: map[string]string{ 174 "cluster.x-k8s.io/control-plane": "", 175 }, 176 }, 177 Spec: capi.MachineSpec{ 178 ClusterName: clusterName, 179 Bootstrap: capi.Bootstrap{ 180 DataSecretName: ptr.To(fmt.Sprintf("%s-%s", clusterID, role)), 181 }, 182 InfrastructureRef: v1.ObjectReference{ 183 APIVersion: capv.GroupVersion.String(), 184 Kind: "VSphereMachine", 185 Name: vsphereMachine.Name, 186 }, 187 }, 188 } 189 machine.SetGroupVersionKind(capi.GroupVersion.WithKind("Machine")) 190 191 result = append(result, &asset.RuntimeFile{ 192 File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", machine.Name)}, 193 Object: machine, 194 }) 195 } 196 197 // as part of provisioning control plane nodes, we need to create a bootstrap node as well 198 if role == masterRole { 199 customVMXKeys := map[string]string{} 200 201 // If we detected static IP for masters, lets apply to bootstrap as well. 202 if staticIP { 203 kargs, err := utils.ConstructKargsForBootstrap(config) 204 if err != nil { 205 return nil, fmt.Errorf("unable to get static ip config for bootstrap: %w", err) 206 } 207 customVMXKeys["guestinfo.afterburn.initrd.network-kargs"] = kargs 208 } 209 210 bootstrapSpec := capvMachines[0].Spec 211 bootstrapSpec.CustomVMXKeys = customVMXKeys 212 bootstrapVSphereMachine := &capv.VSphereMachine{ 213 ObjectMeta: metav1.ObjectMeta{ 214 Name: fmt.Sprintf("%s-bootstrap", clusterID), 215 Labels: map[string]string{ 216 "cluster.x-k8s.io/control-plane": "", 217 }, 218 }, 219 Spec: bootstrapSpec, 220 } 221 bootstrapVSphereMachine.SetGroupVersionKind(capv.GroupVersion.WithKind("VSphereMachine")) 222 223 result = append(result, &asset.RuntimeFile{ 224 File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", bootstrapVSphereMachine.Name)}, 225 Object: bootstrapVSphereMachine, 226 }) 227 228 // Need to determine the infrastructure ref since there may be multi vcenters. 229 clusterName := clusterID 230 for index, vcenter := range config.Platform.VSphere.VCenters { 231 if vcenter.Server == bootstrapSpec.Server { 232 clusterName = fmt.Sprintf("%v-%d", clusterID, index) 233 break 234 } 235 } 236 237 bootstrapMachine := &capi.Machine{ 238 ObjectMeta: metav1.ObjectMeta{ 239 Name: bootstrapVSphereMachine.Name, 240 Labels: map[string]string{ 241 "cluster.x-k8s.io/control-plane": "", 242 }, 243 }, 244 Spec: capi.MachineSpec{ 245 ClusterName: clusterName, 246 Bootstrap: capi.Bootstrap{ 247 DataSecretName: ptr.To(fmt.Sprintf("%s-bootstrap", clusterID)), 248 }, 249 InfrastructureRef: v1.ObjectReference{ 250 APIVersion: capv.GroupVersion.String(), 251 Kind: "VSphereMachine", 252 Name: bootstrapVSphereMachine.Name, 253 }, 254 }, 255 } 256 bootstrapMachine.SetGroupVersionKind(capi.GroupVersion.WithKind("Machine")) 257 result = append(result, &asset.RuntimeFile{ 258 File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", bootstrapVSphereMachine.Name)}, 259 Object: bootstrapMachine, 260 }) 261 } 262 return result, nil 263 }