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 }