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  }