func()

in pkg/util/cluster/cluster.go [429:632]


func (c *Cluster) Create(ctx context.Context) error {
	c.log.Info("Creating cluster")
	clusterGet, err := c.openshiftclusters.Get(ctx, c.Config.VnetResourceGroup, c.Config.ClusterName)
	c.log.Info("Got cluster ref")
	if err == nil {
		if clusterGet.Properties.ProvisioningState == api.ProvisioningStateFailed {
			return fmt.Errorf("cluster exists and is in failed provisioning state, please delete and retry: %s, %s", clusterGet.ID, c.Config.VnetResourceGroup)
		}
		c.log.Print("cluster already exists, skipping create")
		return nil
	}

	appDetails := appDetails{}
	if !c.Config.UseWorkloadIdentity {
		c.log.Info("Creating app")
		appDetails, err = c.createApp(ctx, c.Config.ClusterName)
		if err != nil {
			return err
		}
	}

	visibility := api.VisibilityPublic

	if c.Config.IsPrivate || c.Config.NoInternet {
		visibility = api.VisibilityPrivate
	}

	if c.Config.IsCI {
		c.log.Infof("creating resource group")
		_, err = c.groups.CreateOrUpdate(ctx, c.Config.VnetResourceGroup, mgmtfeatures.ResourceGroup{
			Location: to.StringPtr(c.Config.Location),
		})
		if err != nil {
			return err
		}
	}

	asset, err := assets.EmbeddedFiles.ReadFile(generator.FileClusterPredeploy)
	if err != nil {
		return err
	}

	var template map[string]interface{}
	err = json.Unmarshal(asset, &template)
	if err != nil {
		return err
	}

	addressPrefix, masterSubnet, workerSubnet, err := c.generateSubnets()

	if err != nil {
		return err
	}

	diskEncryptionSetName := fmt.Sprintf(
		"%s%s",
		c.Config.VnetResourceGroup,
		generator.SharedDiskEncryptionSetNameSuffix,
	)

	var kvName string
	if !c.Config.IsCI {
		if len(c.Config.VnetResourceGroup) > 10 {
			// keyvault names need to have a maximum length of 24,
			// so we need to cut off some chars if the resource group name is too long
			kvName = c.Config.VnetResourceGroup[:10] + generator.SharedDiskEncryptionKeyVaultNameSuffix
		} else {
			kvName = c.Config.VnetResourceGroup + generator.SharedDiskEncryptionKeyVaultNameSuffix
		}
	} else {
		// if DES already exists in RG, then reuse KV hosting the key of this DES,
		// otherwise, name is limited to 24 characters, but must be globally unique,
		// so we generate a name randomly until it is available
		diskEncryptionSet, err := c.diskEncryptionSetsClient.Get(ctx, c.Config.VnetResourceGroup, diskEncryptionSetName)
		if err == nil {
			if diskEncryptionSet.EncryptionSetProperties == nil ||
				diskEncryptionSet.EncryptionSetProperties.ActiveKey == nil ||
				diskEncryptionSet.EncryptionSetProperties.ActiveKey.SourceVault == nil ||
				diskEncryptionSet.EncryptionSetProperties.ActiveKey.SourceVault.ID == nil {
				return fmt.Errorf("no valid Key Vault found in Disk Encryption Set: %v. Delete the Disk Encryption Set and retry", diskEncryptionSet)
			}
			ID := *diskEncryptionSet.EncryptionSetProperties.ActiveKey.SourceVault.ID
			var found bool
			_, kvName, found = strings.Cut(ID, "/providers/Microsoft.KeyVault/vaults/")
			if !found {
				return fmt.Errorf("could not find Key Vault name in ID: %v", ID)
			}
		} else {
			if autorestErr, ok := err.(autorest.DetailedError); !ok ||
				autorestErr.Response == nil ||
				autorestErr.Response.StatusCode != http.StatusNotFound {
				return fmt.Errorf("failed to get Disk Encryption Set: %v", err)
			}
			for {
				kvName = "kv-" + uuid.DefaultGenerator.Generate()[:21]
				result, err := c.vaultsClient.CheckNameAvailability(
					ctx,
					sdkkeyvault.VaultCheckNameAvailabilityParameters{Name: &kvName, Type: to.StringPtr("Microsoft.KeyVault/vaults")},
					nil,
				)
				if err != nil {
					return err
				}

				if result.NameAvailable == nil {
					return fmt.Errorf("have unexpected nil NameAvailable for key vault: %v", kvName)
				}

				if *result.NameAvailable {
					break
				}
				c.log.Infof("key vault %v is not available and we will try an other one", kvName)
			}
		}
	}

	parameters := map[string]*arm.ParametersParameter{
		"clusterName":         {Value: c.Config.ClusterName},
		"ci":                  {Value: c.Config.IsCI},
		"vnetAddressPrefix":   {Value: addressPrefix},
		"masterAddressPrefix": {Value: masterSubnet},
		"workerAddressPrefix": {Value: workerSubnet},
		"kvName":              {Value: kvName},
	}

	// TODO: ick
	if os.Getenv("NO_INTERNET") != "" {
		parameters["routes"] = &arm.ParametersParameter{
			Value: []sdknetwork.Route{
				{
					Properties: &sdknetwork.RoutePropertiesFormat{
						AddressPrefix: pointerutils.ToPtr("0.0.0.0/0"),
						NextHopType:   pointerutils.ToPtr(sdknetwork.RouteNextHopTypeNone),
					},
					Name: pointerutils.ToPtr("blackhole"),
				},
			},
		}
	}

	armctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
	defer cancel()

	c.log.Info("predeploying ARM template")
	err = c.deployments.CreateOrUpdateAndWait(armctx, c.Config.VnetResourceGroup, c.Config.ClusterName, mgmtfeatures.Deployment{
		Properties: &mgmtfeatures.DeploymentProperties{
			Template:   template,
			Parameters: parameters,
			Mode:       mgmtfeatures.Incremental,
		},
	})
	if err != nil {
		return err
	}

	diskEncryptionSetID := fmt.Sprintf(
		"/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/diskEncryptionSets/%s",
		c.Config.SubscriptionID,
		c.Config.VnetResourceGroup,
		diskEncryptionSetName,
	)

	if c.Config.UseWorkloadIdentity {
		c.log.Info("creating WIs")
		if err := c.SetupWorkloadIdentity(ctx, c.Config.VnetResourceGroup); err != nil {
			return fmt.Errorf("error setting up Workload Identity Roles: %w", err)
		}
	} else {
		c.log.Info("creating Classic role assignments")
		c.SetupServicePrincipalRoleAssignments(ctx, diskEncryptionSetID, appDetails.SPId)
	}
	fipsMode := true

	// Don't install with FIPS in a local dev, non-CI environment
	if !c.Config.IsCI && c.Config.IsLocalDevelopmentMode() {
		fipsMode = false
	}

	c.log.Info("creating cluster")
	err = c.createCluster(ctx, c.Config.VnetResourceGroup, c.Config.ClusterName, appDetails.applicationId, appDetails.applicationSecret, diskEncryptionSetID, visibility, c.Config.OSClusterVersion, fipsMode)

	if err != nil {
		return err
	}

	if c.Config.IsCI {
		c.log.Info("fixing up NSGs")
		err = c.fixupNSGs(ctx, c.Config.VnetResourceGroup, c.Config.ClusterName)
		if err != nil {
			return err
		}

		if env.IsLocalDevelopmentMode() {
			c.log.Info("peering subnets to CI infra")
			err = c.peerSubnetsToCI(ctx, c.Config.VnetResourceGroup)
			if err != nil {
				return err
			}
		}
	}

	c.log.Info("done")
	return nil
}