github.com/openshift/installer@v1.4.17/pkg/asset/machines/gcp/gcpmachines.go (about)

     1  // Package gcp generates Machine objects for gcp.
     2  package gcp
     3  
     4  import (
     5  	"fmt"
     6  
     7  	"github.com/sirupsen/logrus"
     8  	compute "google.golang.org/api/compute/v1"
     9  	v1 "k8s.io/api/core/v1"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/utils/ptr"
    12  	capg "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
    13  	capi "sigs.k8s.io/cluster-api/api/v1beta1"
    14  
    15  	"github.com/openshift/installer/pkg/asset"
    16  	"github.com/openshift/installer/pkg/asset/installconfig"
    17  	gcpmanifests "github.com/openshift/installer/pkg/asset/manifests/gcp"
    18  	gcpconsts "github.com/openshift/installer/pkg/constants/gcp"
    19  	"github.com/openshift/installer/pkg/types"
    20  	gcptypes "github.com/openshift/installer/pkg/types/gcp"
    21  )
    22  
    23  const (
    24  	masterRole = "master"
    25  
    26  	kmsKeyNameFmt = "projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s"
    27  )
    28  
    29  func generateDiskEncryptionKeyLink(kmsKey *gcptypes.KMSKeyReference, projectID string) string {
    30  	if kmsKey.ProjectID != "" {
    31  		projectID = kmsKey.ProjectID
    32  	}
    33  
    34  	return fmt.Sprintf(kmsKeyNameFmt, projectID, kmsKey.Location, kmsKey.KeyRing, kmsKey.Name)
    35  }
    36  
    37  // GenerateMachines returns manifests and runtime objects to provision control plane nodes using CAPI.
    38  func GenerateMachines(installConfig *installconfig.InstallConfig, infraID string, pool *types.MachinePool, imageName string) ([]*asset.RuntimeFile, error) {
    39  	var result []*asset.RuntimeFile
    40  	if poolPlatform := pool.Platform.Name(); poolPlatform != gcptypes.Name {
    41  		return nil, fmt.Errorf("non-GCP machine-pool: %q", poolPlatform)
    42  	}
    43  	mpool := pool.Platform.GCP
    44  
    45  	total := int64(1)
    46  	if pool.Replicas != nil {
    47  		total = *pool.Replicas
    48  	}
    49  
    50  	// Create GCP and CAPI machines for all master replicas in pool
    51  	for idx := int64(0); idx < total; idx++ {
    52  		name := fmt.Sprintf("%s-%s-%d", infraID, pool.Name, idx)
    53  		gcpMachine, err := createGCPMachine(name, installConfig, infraID, mpool, imageName)
    54  		if err != nil {
    55  			return nil, fmt.Errorf("failed to create control plane (%d): %w", idx, err)
    56  		}
    57  
    58  		result = append(result, &asset.RuntimeFile{
    59  			File:   asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", gcpMachine.Name)},
    60  			Object: gcpMachine,
    61  		})
    62  
    63  		dataSecret := fmt.Sprintf("%s-%s", infraID, masterRole)
    64  		capiMachine := createCAPIMachine(gcpMachine.Name, dataSecret, infraID)
    65  
    66  		if len(mpool.Zones) > 0 {
    67  			// When there are fewer zones than the number of control plane instances,
    68  			// cycle through the zones where the instances will reside.
    69  			zone := mpool.Zones[int(idx)%len(mpool.Zones)]
    70  			capiMachine.Spec.FailureDomain = ptr.To(zone)
    71  		}
    72  
    73  		result = append(result, &asset.RuntimeFile{
    74  			File:   asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", capiMachine.Name)},
    75  			Object: capiMachine,
    76  		})
    77  	}
    78  	return result, nil
    79  }
    80  
    81  // GenerateBootstrapMachines returns a manifest and runtime object for a bootstrap node using CAPI.
    82  func GenerateBootstrapMachines(name string, installConfig *installconfig.InstallConfig, infraID string, pool *types.MachinePool, imageName string) ([]*asset.RuntimeFile, error) {
    83  	var result []*asset.RuntimeFile
    84  	if poolPlatform := pool.Platform.Name(); poolPlatform != gcptypes.Name {
    85  		return nil, fmt.Errorf("non-GCP machine-pool: %q", poolPlatform)
    86  	}
    87  	mpool := pool.Platform.GCP
    88  
    89  	// Create one GCP and CAPI machine for bootstrap
    90  	bootstrapGCPMachine, err := createGCPMachine(name, installConfig, infraID, mpool, imageName)
    91  	if err != nil {
    92  		return nil, fmt.Errorf("failed to create bootstrap machine: %w", err)
    93  	}
    94  
    95  	// Identify this as a bootstrap machine
    96  	bootstrapGCPMachine.Labels["install.openshift.io/bootstrap"] = ""
    97  
    98  	bootstrapMachineIsPublic := installConfig.Config.Publish == types.ExternalPublishingStrategy
    99  	bootstrapGCPMachine.Spec.PublicIP = ptr.To(bootstrapMachineIsPublic)
   100  
   101  	result = append(result, &asset.RuntimeFile{
   102  		File:   asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", bootstrapGCPMachine.Name)},
   103  		Object: bootstrapGCPMachine,
   104  	})
   105  
   106  	dataSecret := fmt.Sprintf("%s-%s", infraID, "bootstrap")
   107  	bootstrapCapiMachine := createCAPIMachine(bootstrapGCPMachine.Name, dataSecret, infraID)
   108  
   109  	result = append(result, &asset.RuntimeFile{
   110  		File:   asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", bootstrapCapiMachine.Name)},
   111  		Object: bootstrapCapiMachine,
   112  	})
   113  	return result, nil
   114  }
   115  
   116  // Create a CAPG-specific machine.
   117  func createGCPMachine(name string, installConfig *installconfig.InstallConfig, infraID string, mpool *gcptypes.MachinePool, imageName string) (*capg.GCPMachine, error) {
   118  	// Use the rhcosImage as image name if not defined
   119  	osImage := imageName
   120  	if mpool.OSImage != nil {
   121  		osImage = fmt.Sprintf("projects/%s/global/images/%s", mpool.OSImage.Project, mpool.OSImage.Name)
   122  		logrus.Debugf("overriding gcp machine image: %s", osImage)
   123  	}
   124  
   125  	masterSubnet := installConfig.Config.Platform.GCP.ControlPlaneSubnet
   126  	if masterSubnet == "" {
   127  		masterSubnet = gcptypes.DefaultSubnetName(infraID, masterRole)
   128  	}
   129  
   130  	gcpMachine := &capg.GCPMachine{
   131  		ObjectMeta: metav1.ObjectMeta{
   132  			Name: name,
   133  			Labels: map[string]string{
   134  				"cluster.x-k8s.io/control-plane": "",
   135  			},
   136  		},
   137  		Spec: capg.GCPMachineSpec{
   138  			InstanceType:          mpool.InstanceType,
   139  			Subnet:                ptr.To(masterSubnet),
   140  			AdditionalLabels:      getLabelsFromInstallConfig(installConfig, infraID),
   141  			Image:                 ptr.To(osImage),
   142  			RootDeviceType:        ptr.To(capg.DiskType(mpool.OSDisk.DiskType)),
   143  			RootDeviceSize:        mpool.OSDisk.DiskSizeGB,
   144  			AdditionalNetworkTags: mpool.Tags,
   145  			ResourceManagerTags:   gcpmanifests.GetTagsFromInstallConfig(installConfig),
   146  		},
   147  	}
   148  	gcpMachine.SetGroupVersionKind(capg.GroupVersion.WithKind("GCPMachine"))
   149  	// Set optional values from machinepool
   150  	if mpool.OnHostMaintenance != "" {
   151  		gcpMachine.Spec.OnHostMaintenance = ptr.To(capg.HostMaintenancePolicy(mpool.OnHostMaintenance))
   152  	}
   153  	if mpool.ConfidentialCompute != "" {
   154  		gcpMachine.Spec.ConfidentialCompute = ptr.To(capg.ConfidentialComputePolicy(mpool.ConfidentialCompute))
   155  	}
   156  	if mpool.SecureBoot != "" {
   157  		shieldedInstanceConfig := capg.GCPShieldedInstanceConfig{}
   158  		shieldedInstanceConfig.SecureBoot = capg.SecureBootPolicyEnabled
   159  		gcpMachine.Spec.ShieldedInstanceConfig = ptr.To(shieldedInstanceConfig)
   160  	}
   161  
   162  	serviceAccountEmail := gcptypes.GetConfiguredServiceAccount(installConfig.Config.Platform.GCP, mpool)
   163  	if serviceAccountEmail == "" {
   164  		serviceAccountEmail = gcptypes.GetDefaultServiceAccount(installConfig.Config.Platform.GCP, infraID, masterRole[0:1])
   165  	}
   166  	serviceAccount := &capg.ServiceAccount{
   167  		Email: serviceAccountEmail,
   168  		// Set scopes to value defined at
   169  		// https://cloud.google.com/compute/docs/access/service-accounts#scopes_best_practice
   170  		Scopes: []string{compute.CloudPlatformScope},
   171  	}
   172  
   173  	gcpMachine.Spec.ServiceAccount = serviceAccount
   174  
   175  	if mpool.OSDisk.EncryptionKey != nil {
   176  		encryptionKey := &capg.CustomerEncryptionKey{
   177  			KeyType: capg.CustomerManagedKey,
   178  			ManagedKey: &capg.ManagedKey{
   179  				KMSKeyName: generateDiskEncryptionKeyLink(mpool.OSDisk.EncryptionKey.KMSKey, installConfig.Config.GCP.ProjectID),
   180  			},
   181  		}
   182  		if mpool.OSDisk.EncryptionKey.KMSKeyServiceAccount != "" {
   183  			encryptionKey.KMSKeyServiceAccount = ptr.To(mpool.OSDisk.EncryptionKey.KMSKeyServiceAccount)
   184  		}
   185  		gcpMachine.Spec.RootDiskEncryptionKey = encryptionKey
   186  	}
   187  
   188  	return gcpMachine, nil
   189  }
   190  
   191  // Create a CAPI machine based on the CAPG machine.
   192  func createCAPIMachine(name string, dataSecret string, infraID string) *capi.Machine {
   193  	machine := &capi.Machine{
   194  		ObjectMeta: metav1.ObjectMeta{
   195  			Name: name,
   196  			Labels: map[string]string{
   197  				"cluster.x-k8s.io/control-plane": "",
   198  			},
   199  		},
   200  		Spec: capi.MachineSpec{
   201  			ClusterName: infraID,
   202  			Bootstrap: capi.Bootstrap{
   203  				DataSecretName: ptr.To(dataSecret),
   204  			},
   205  			InfrastructureRef: v1.ObjectReference{
   206  				APIVersion: capg.GroupVersion.String(),
   207  				Kind:       "GCPMachine",
   208  				Name:       name,
   209  			},
   210  		},
   211  	}
   212  	machine.SetGroupVersionKind(capi.GroupVersion.WithKind("Machine"))
   213  
   214  	return machine
   215  }
   216  
   217  func getLabelsFromInstallConfig(installConfig *installconfig.InstallConfig, infraID string) map[string]string {
   218  	ic := installConfig.Config
   219  
   220  	userLabels := map[string]string{}
   221  	for _, label := range ic.Platform.GCP.UserLabels {
   222  		userLabels[label.Key] = label.Value
   223  	}
   224  	// add OCP default label
   225  	userLabels[fmt.Sprintf(gcpconsts.ClusterIDLabelFmt, infraID)] = "owned"
   226  
   227  	return userLabels
   228  }