github.com/openshift/installer@v1.4.17/pkg/asset/cluster/cluster.go (about)

     1  package cluster
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/pkg/errors"
    11  	"github.com/sirupsen/logrus"
    12  
    13  	"github.com/openshift/installer/pkg/asset"
    14  	"github.com/openshift/installer/pkg/asset/cluster/aws"
    15  	"github.com/openshift/installer/pkg/asset/cluster/azure"
    16  	"github.com/openshift/installer/pkg/asset/cluster/openstack"
    17  	"github.com/openshift/installer/pkg/asset/cluster/tfvars"
    18  	"github.com/openshift/installer/pkg/asset/ignition/bootstrap"
    19  	"github.com/openshift/installer/pkg/asset/ignition/machine"
    20  	"github.com/openshift/installer/pkg/asset/installconfig"
    21  	"github.com/openshift/installer/pkg/asset/kubeconfig"
    22  	"github.com/openshift/installer/pkg/asset/machines"
    23  	"github.com/openshift/installer/pkg/asset/manifests"
    24  	capimanifests "github.com/openshift/installer/pkg/asset/manifests/clusterapi"
    25  	"github.com/openshift/installer/pkg/asset/password"
    26  	"github.com/openshift/installer/pkg/asset/quota"
    27  	"github.com/openshift/installer/pkg/asset/rhcos"
    28  	"github.com/openshift/installer/pkg/clusterapi"
    29  	infra "github.com/openshift/installer/pkg/infrastructure/platform"
    30  	typesaws "github.com/openshift/installer/pkg/types/aws"
    31  	typesazure "github.com/openshift/installer/pkg/types/azure"
    32  	typesopenstack "github.com/openshift/installer/pkg/types/openstack"
    33  )
    34  
    35  var (
    36  	// InstallDir is the directory containing install assets.
    37  	InstallDir string
    38  )
    39  
    40  // Cluster uses the terraform executable to launch a cluster
    41  // with the given terraform tfvar and generated templates.
    42  type Cluster struct {
    43  	FileList []*asset.File
    44  }
    45  
    46  var _ asset.WritableAsset = (*Cluster)(nil)
    47  
    48  // Name returns the human-friendly name of the asset.
    49  func (c *Cluster) Name() string {
    50  	return "Cluster"
    51  }
    52  
    53  // Dependencies returns the direct dependency for launching
    54  // the cluster.
    55  func (c *Cluster) Dependencies() []asset.Asset {
    56  	return []asset.Asset{
    57  		&installconfig.ClusterID{},
    58  		&installconfig.InstallConfig{},
    59  		// PlatformCredsCheck, PlatformPermsCheck, PlatformProvisionCheck, and VCenterContexts.
    60  		// perform validations & check perms required to provision infrastructure.
    61  		// We do not actually use them in this asset directly, hence
    62  		// they are put in the dependencies but not fetched in Generate.
    63  		&installconfig.PlatformCredsCheck{},
    64  		&installconfig.PlatformPermsCheck{},
    65  		&installconfig.PlatformProvisionCheck{},
    66  		new(rhcos.Image),
    67  		&quota.PlatformQuotaCheck{},
    68  		&tfvars.TerraformVariables{},
    69  		&password.KubeadminPassword{},
    70  		&manifests.Manifests{},
    71  		&capimanifests.Cluster{},
    72  		&kubeconfig.AdminClient{},
    73  		&bootstrap.Bootstrap{},
    74  		&machine.Master{},
    75  		&machines.Worker{},
    76  		&machines.ClusterAPI{},
    77  		new(rhcos.Image),
    78  		&manifests.Manifests{},
    79  	}
    80  }
    81  
    82  // Generate launches the cluster and generates the terraform state file on disk.
    83  func (c *Cluster) Generate(ctx context.Context, parents asset.Parents) (err error) {
    84  	if InstallDir == "" {
    85  		logrus.Fatalf("InstallDir has not been set for the %q asset", c.Name())
    86  	}
    87  
    88  	clusterID := &installconfig.ClusterID{}
    89  	installConfig := &installconfig.InstallConfig{}
    90  	rhcosImage := new(rhcos.Image)
    91  	terraformVariables := &tfvars.TerraformVariables{}
    92  	parents.Get(clusterID, installConfig, terraformVariables, rhcosImage)
    93  
    94  	if fs := installConfig.Config.FeatureSet; strings.HasSuffix(string(fs), "NoUpgrade") {
    95  		logrus.Warnf("FeatureSet %q is enabled. This FeatureSet does not allow upgrades and may affect the supportability of the cluster.", fs)
    96  	}
    97  
    98  	if installConfig.Config.Platform.None != nil {
    99  		return errors.New("cluster cannot be created with platform set to 'none'")
   100  	}
   101  
   102  	if installConfig.Config.BootstrapInPlace != nil {
   103  		return errors.New("cluster cannot be created with bootstrapInPlace set")
   104  	}
   105  
   106  	platform := installConfig.Config.Platform.Name()
   107  
   108  	if azure := installConfig.Config.Platform.Azure; azure != nil && azure.CloudName == typesazure.StackCloud {
   109  		platform = typesazure.StackTerraformName
   110  	}
   111  
   112  	// TODO(padillon): determine whether CAPI handles tagging shared subnets, in which case we should be able
   113  	// to encapsulate these into the terraform package.
   114  	logrus.Infof("Creating infrastructure resources...")
   115  	switch platform {
   116  	case typesaws.Name:
   117  		if err := aws.PreTerraform(ctx, clusterID.InfraID, installConfig); err != nil {
   118  			return err
   119  		}
   120  	case typesazure.Name, typesazure.StackTerraformName:
   121  		if err := azure.PreTerraform(ctx, clusterID.InfraID, installConfig); err != nil {
   122  			return err
   123  		}
   124  	case typesopenstack.Name:
   125  		var tfvarsFile *asset.File
   126  		for _, f := range terraformVariables.Files() {
   127  			if f.Filename == tfvars.TfPlatformVarsFileName {
   128  				tfvarsFile = f
   129  				break
   130  			}
   131  		}
   132  		if err := openstack.PreTerraform(ctx, tfvarsFile, installConfig, clusterID, rhcosImage); err != nil {
   133  			return err
   134  		}
   135  	}
   136  
   137  	provider, err := infra.ProviderForPlatform(platform, installConfig.Config.EnabledFeatureGates())
   138  	if err != nil {
   139  		return fmt.Errorf("error getting infrastructure provider: %w", err)
   140  	}
   141  	files, err := provider.Provision(ctx, InstallDir, parents)
   142  	if files != nil {
   143  		c.FileList = append(c.FileList, files...) // append state files even in case of failure
   144  	}
   145  	if err != nil {
   146  		return fmt.Errorf("%s: %w", asset.ClusterCreationError, err)
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  // Files returns the FileList generated by the asset.
   153  func (c *Cluster) Files() []*asset.File {
   154  	return c.FileList
   155  }
   156  
   157  // Load returns error if the tfstate file is already on-disk, because we want to
   158  // prevent user from accidentally re-launching the cluster.
   159  func (c *Cluster) Load(f asset.FileFetcher) (found bool, err error) {
   160  	matches, err := filepath.Glob("terraform(.*)?.tfstate")
   161  	if err != nil {
   162  		return true, err
   163  	}
   164  	if len(matches) != 0 {
   165  		return true, fmt.Errorf("terraform state files already exist.  There may already be a running cluster")
   166  	}
   167  
   168  	matches, err = filepath.Glob(filepath.Join(InstallDir, clusterapi.ArtifactsDir, "envtest.kubeconfig"))
   169  	if err != nil {
   170  		return true, fmt.Errorf("error checking for existence of envtest.kubeconfig: %w", err)
   171  	}
   172  
   173  	// Cluster-API based installs can be re-entered, but this is an experimental feature
   174  	// that should be opted into and only used for testing and development.
   175  	reentrant := strings.EqualFold(os.Getenv("OPENSHIFT_INSTALL_REENTRANT"), "true")
   176  
   177  	if !reentrant && len(matches) != 0 {
   178  		return true, fmt.Errorf("local infrastructure provisioning artifacts already exist. There may already be a running cluster")
   179  	}
   180  	return false, nil
   181  }