github.com/openshift/installer@v1.4.17/pkg/asset/manifests/gcp/cluster.go (about) 1 package gcp 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "time" 8 9 "github.com/apparentlymart/go-cidr/cidr" 10 "google.golang.org/api/compute/v1" 11 "google.golang.org/api/option" 12 corev1 "k8s.io/api/core/v1" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/apimachinery/pkg/util/sets" 15 "k8s.io/utils/ptr" 16 capg "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1" 17 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 18 19 "github.com/openshift/installer/pkg/asset" 20 "github.com/openshift/installer/pkg/asset/installconfig" 21 gcpic "github.com/openshift/installer/pkg/asset/installconfig/gcp" 22 "github.com/openshift/installer/pkg/asset/manifests/capiutils" 23 gcpconsts "github.com/openshift/installer/pkg/constants/gcp" 24 "github.com/openshift/installer/pkg/types" 25 "github.com/openshift/installer/pkg/types/gcp" 26 ) 27 28 // InstanceGroupRoleTag is the tag used in the instance 29 // group name to maintain compatibility between MAPI & CAPI. 30 const InstanceGroupRoleTag = "master" 31 32 // GenerateClusterAssets generates the manifests for the cluster-api. 33 func GenerateClusterAssets(installConfig *installconfig.InstallConfig, clusterID *installconfig.ClusterID) (*capiutils.GenerateClusterAssetsOutput, error) { 34 manifests := []*asset.RuntimeFile{} 35 const description = "Created By OpenShift Installer" 36 37 networkName := fmt.Sprintf("%s-network", clusterID.InfraID) 38 if installConfig.Config.GCP.Network != "" { 39 networkName = installConfig.Config.GCP.Network 40 } 41 42 networkProject := installConfig.Config.GCP.ProjectID 43 if installConfig.Config.GCP.NetworkProjectID != "" { 44 networkProject = installConfig.Config.GCP.NetworkProjectID 45 } 46 47 controlPlaneSubnetName := gcp.DefaultSubnetName(clusterID.InfraID, "master") 48 controlPlaneSubnetCidr := "" 49 if installConfig.Config.GCP.ControlPlaneSubnet != "" { 50 controlPlaneSubnetName = installConfig.Config.GCP.ControlPlaneSubnet 51 52 controlPlaneSubnet, err := getSubnet(context.TODO(), networkProject, installConfig.Config.GCP.Region, controlPlaneSubnetName) 53 if err != nil { 54 return nil, fmt.Errorf("failed to get control plane subnet: %w", err) 55 } 56 // IpCidr is the IPv4 version, the IPv6 version can be accessed as well 57 controlPlaneSubnetCidr = controlPlaneSubnet.IpCidrRange 58 } 59 60 controlPlane := capg.SubnetSpec{ 61 Name: controlPlaneSubnetName, 62 CidrBlock: controlPlaneSubnetCidr, 63 Description: ptr.To(description), 64 Region: installConfig.Config.GCP.Region, 65 } 66 67 computeSubnetName := gcp.DefaultSubnetName(clusterID.InfraID, "worker") 68 computeSubnetCidr := "" 69 if installConfig.Config.GCP.ComputeSubnet != "" { 70 computeSubnetName = installConfig.Config.GCP.ComputeSubnet 71 72 computeSubnet, err := getSubnet(context.TODO(), networkProject, installConfig.Config.GCP.Region, computeSubnetName) 73 if err != nil { 74 return nil, fmt.Errorf("failed to get compute subnet: %w", err) 75 } 76 // IpCidr is the IPv4 version, the IPv6 version can be accessed as well 77 computeSubnetCidr = computeSubnet.IpCidrRange 78 } 79 80 compute := capg.SubnetSpec{ 81 Name: computeSubnetName, 82 CidrBlock: computeSubnetCidr, 83 Description: ptr.To(description), 84 Region: installConfig.Config.GCP.Region, 85 } 86 87 // Add the CIDR information. 88 machineV4CIDRs := []string{} 89 for _, network := range installConfig.Config.Networking.MachineNetwork { 90 if network.CIDR.IPNet.IP.To4() != nil { 91 machineV4CIDRs = append(machineV4CIDRs, network.CIDR.IPNet.String()) 92 } 93 } 94 95 if len(machineV4CIDRs) == 0 { 96 return nil, fmt.Errorf("failed to parse machine CIDRs") 97 } 98 99 _, ipv4Net, err := net.ParseCIDR(machineV4CIDRs[0]) 100 if err != nil { 101 return nil, fmt.Errorf("failed to parse machine network CIDR: %w", err) 102 } 103 104 if installConfig.Config.GCP.ControlPlaneSubnet == "" { 105 masterCIDR, err := cidr.Subnet(ipv4Net, 1, 0) 106 if err != nil { 107 return nil, fmt.Errorf("failed to create the master subnet %w", err) 108 } 109 controlPlane.CidrBlock = masterCIDR.String() 110 } 111 112 if installConfig.Config.GCP.ComputeSubnet == "" { 113 computeCIDR, err := cidr.Subnet(ipv4Net, 1, 1) 114 if err != nil { 115 return nil, fmt.Errorf("failed to create the compute subnet %w", err) 116 } 117 compute.CidrBlock = computeCIDR.String() 118 } 119 120 subnets := []capg.SubnetSpec{controlPlane, compute} 121 // Subnets should never be auto created, even in shared VPC installs 122 autoCreateSubnets := false 123 124 labels := map[string]string{} 125 labels[fmt.Sprintf(gcpconsts.ClusterIDLabelFmt, clusterID.InfraID)] = "owned" 126 labels[fmt.Sprintf("capg-cluster-%s", clusterID.InfraID)] = "owned" 127 for _, label := range installConfig.Config.GCP.UserLabels { 128 labels[label.Key] = label.Value 129 } 130 131 capgLoadBalancerType := capg.InternalExternal 132 if installConfig.Config.Publish == types.InternalPublishingStrategy { 133 capgLoadBalancerType = capg.Internal 134 } 135 136 gcpCluster := &capg.GCPCluster{ 137 ObjectMeta: metav1.ObjectMeta{ 138 Name: clusterID.InfraID, 139 Namespace: capiutils.Namespace, 140 }, 141 Spec: capg.GCPClusterSpec{ 142 Project: installConfig.Config.GCP.ProjectID, 143 Region: installConfig.Config.GCP.Region, 144 ControlPlaneEndpoint: clusterv1.APIEndpoint{Port: 6443}, 145 Network: capg.NetworkSpec{ 146 // TODO: Need a network project for installs where the network resources will exist in another 147 // project such as shared vpc installs 148 Name: ptr.To(networkName), 149 Subnets: subnets, 150 AutoCreateSubnetworks: ptr.To(autoCreateSubnets), 151 }, 152 AdditionalLabels: labels, 153 FailureDomains: findFailureDomains(installConfig), 154 LoadBalancer: capg.LoadBalancerSpec{ 155 APIServerInstanceGroupTagOverride: ptr.To(InstanceGroupRoleTag), 156 LoadBalancerType: ptr.To(capgLoadBalancerType), 157 }, 158 ResourceManagerTags: GetTagsFromInstallConfig(installConfig), 159 }, 160 } 161 gcpCluster.SetGroupVersionKind(capg.GroupVersion.WithKind("GCPCluster")) 162 163 // Set the network project during shared vpc installs 164 if installConfig.Config.GCP.NetworkProjectID != "" { 165 gcpCluster.Spec.Network.HostProject = ptr.To(installConfig.Config.GCP.NetworkProjectID) 166 } 167 168 manifests = append(manifests, &asset.RuntimeFile{ 169 Object: gcpCluster, 170 File: asset.File{Filename: "02_gcp-cluster.yaml"}, 171 }) 172 173 return &capiutils.GenerateClusterAssetsOutput{ 174 Manifests: manifests, 175 InfrastructureRefs: []*corev1.ObjectReference{ 176 { 177 APIVersion: capg.GroupVersion.String(), 178 Kind: "GCPCluster", 179 Name: gcpCluster.Name, 180 Namespace: gcpCluster.Namespace, 181 }, 182 }, 183 }, nil 184 } 185 186 // findFailureDomains will find the failure domains or availability zones for the GCP platform. 187 // When the default machine platform is defined, take any zone from the compute node(s) and 188 // any defined in the control plane node(s). When the default machine platform is not defined, 189 // only use zones if both the compute and control plane node availability zones exist. 190 func findFailureDomains(installConfig *installconfig.InstallConfig) []string { 191 zones := sets.New[string]() 192 193 var controlPlaneZones, computeZones []string 194 if installConfig.Config.ControlPlane.Platform.GCP != nil { 195 controlPlaneZones = installConfig.Config.ControlPlane.Platform.GCP.Zones 196 } 197 198 if installConfig.Config.Compute[0].Platform.GCP != nil { 199 computeZones = installConfig.Config.Compute[0].Platform.GCP.Zones 200 } 201 202 def := installConfig.Config.GCP.DefaultMachinePlatform 203 if def != nil && len(def.Zones) > 0 { 204 for _, zone := range def.Zones { 205 zones.Insert(zone) 206 } 207 208 for _, zone := range controlPlaneZones { 209 zones.Insert(zone) 210 } 211 212 for _, zone := range computeZones { 213 zones.Insert(zone) 214 } 215 } else if len(controlPlaneZones) > 0 && len(computeZones) > 0 { 216 for _, zone := range controlPlaneZones { 217 zones.Insert(zone) 218 } 219 for _, zone := range computeZones { 220 zones.Insert(zone) 221 } 222 } 223 224 return zones.UnsortedList() 225 } 226 227 // getSubnet will find a subnet in a project by the name. The matching subnet structure will be returned if 228 // one is found. 229 func getSubnet(ctx context.Context, project, region, subnetName string) (*compute.Subnetwork, error) { 230 ctx, cancel := context.WithTimeout(ctx, time.Minute*1) 231 defer cancel() 232 233 ssn, err := gcpic.GetSession(ctx) 234 if err != nil { 235 return nil, fmt.Errorf("failed to get session: %w", err) 236 } 237 238 computeService, err := compute.NewService(ctx, option.WithCredentials(ssn.Credentials)) 239 if err != nil { 240 return nil, fmt.Errorf("failed to create compute service: %w", err) 241 } 242 243 subnetService := compute.NewSubnetworksService(computeService) 244 subnet, err := subnetService.Get(project, region, subnetName).Context(ctx).Do() 245 if err != nil { 246 return nil, fmt.Errorf("failed to find subnet %s: %w", subnetName, err) 247 } else if subnet == nil { 248 return nil, fmt.Errorf("subnet %s is empty", subnetName) 249 } 250 251 return subnet, nil 252 } 253 254 // GetTagsFromInstallConfig will return a slice of ResourceManagerTags from UserTags in install-config. 255 func GetTagsFromInstallConfig(installConfig *installconfig.InstallConfig) []capg.ResourceManagerTag { 256 tags := make([]capg.ResourceManagerTag, len(installConfig.Config.Platform.GCP.UserTags)) 257 for i, tag := range installConfig.Config.Platform.GCP.UserTags { 258 tags[i] = capg.ResourceManagerTag{ 259 ParentID: tag.ParentID, 260 Key: tag.Key, 261 Value: tag.Value, 262 } 263 } 264 265 return tags 266 }