github.com/openshift/installer@v1.4.17/pkg/clusterapi/providers.go (about)

     1  package clusterapi
     2  
     3  import (
     4  	"archive/zip"
     5  	"bytes"
     6  	"embed"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/pkg/errors"
    14  	"github.com/sirupsen/logrus"
    15  	"k8s.io/apimachinery/pkg/util/sets"
    16  )
    17  
    18  const (
    19  	zipFile = "cluster-api.zip"
    20  )
    21  
    22  var (
    23  	// ClusterAPI is the core provider for cluster-api.
    24  	ClusterAPI = Provider{
    25  		Name:    "cluster-api",
    26  		Sources: sets.New("cluster-api"),
    27  	}
    28  
    29  	// EnvTest is the provider for the local control plane.
    30  	EnvTest = Provider{
    31  		Name:    "envtest",
    32  		Sources: sets.New("kube-apiserver", "etcd"),
    33  	}
    34  
    35  	// AWS is the provider for creating resources in AWS.
    36  	AWS = infrastructureProvider("aws")
    37  	// Azure is the provider for creating resources in Azure.
    38  	Azure = infrastructureProvider("azure")
    39  	// AzureASO is a companion component to Azure that is used to create resources declaratively.
    40  	AzureASO = infrastructureProvider("azureaso")
    41  	// GCP is the provider for creating resources in GCP.
    42  	GCP = infrastructureProvider("gcp")
    43  	// IBMCloud is the provider for creating resources in IBM Cloud and powervs.
    44  	IBMCloud = infrastructureProvider("ibmcloud")
    45  	// Nutanix is the provider for creating resources in Nutanix.
    46  	Nutanix = infrastructureProvider("nutanix")
    47  	// OpenStack is the provider for creating resources in OpenStack.
    48  	OpenStack = infrastructureProvider("openstack")
    49  	// VSphere is the provider for creating resources in vSphere.
    50  	VSphere = infrastructureProvider("vsphere")
    51  )
    52  
    53  // Provider is a Cluster API provider.
    54  type Provider struct {
    55  	// Name of the provider.
    56  	Name string
    57  	// Sources of the provider.
    58  	Sources sets.Set[string]
    59  }
    60  
    61  // infrastructureProvider configures a infrastructureProvider built locally.
    62  func infrastructureProvider(name string) Provider {
    63  	return Provider{
    64  		Name: name,
    65  		Sources: sets.New(
    66  			fmt.Sprintf("cluster-api-provider-%s", name),
    67  		),
    68  	}
    69  }
    70  
    71  // Mirror is the embedded data for the providers.
    72  //
    73  //go:embed mirror/*
    74  var Mirror embed.FS
    75  
    76  // Extract extracts the provider from the embedded data into the specified directory.
    77  func (p Provider) Extract(dir string) error {
    78  	f, err := Mirror.Open(filepath.Join("mirror", zipFile))
    79  	if err != nil {
    80  		return errors.Wrap(err, "failed to open cluster api zip from mirror")
    81  	}
    82  	defer f.Close()
    83  	stat, err := f.Stat()
    84  	if err != nil {
    85  		return errors.Wrap(err, "failed to stat cluster api zip")
    86  	}
    87  	seek, ok := f.(io.ReaderAt)
    88  	if !ok {
    89  		// If the file does not support ReaderAt interface (<Go1.20)
    90  		// we need to read the whole file into memory.
    91  		b, err := io.ReadAll(f)
    92  		if err != nil {
    93  			return errors.Wrap(err, "failed to read cluster api zip")
    94  		}
    95  		seek = bytes.NewReader(b)
    96  	}
    97  
    98  	// Open a zip archive for reading.
    99  	r, err := zip.NewReader(seek, stat.Size())
   100  	if err != nil {
   101  		return errors.Wrap(err, "failed to open cluster api zip")
   102  	}
   103  
   104  	// Ensure the directory exists.
   105  	logrus.Debugf("Creating %s directory", dir)
   106  	if err := os.MkdirAll(dir, 0o777); err != nil {
   107  		return errors.Wrapf(err, "could not make directory for the %s provider", p.Name)
   108  	}
   109  
   110  	// Extract the files.
   111  	for _, f := range r.File {
   112  		name := f.Name
   113  		if !p.Sources.Has(name) {
   114  			continue
   115  		}
   116  		path, err := sanitizeArchivePath(dir, name)
   117  		if err != nil {
   118  			return errors.Wrapf(err, "failed to sanitize archive file %q", name)
   119  		}
   120  		logrus.Debugf("Extracting %s file", path)
   121  		if err := unpackFile(f, path); err != nil {
   122  			return errors.Wrapf(err, "failed to extract %q", path)
   123  		}
   124  	}
   125  	return nil
   126  }
   127  
   128  func unpackFile(f *zip.File, destPath string) error {
   129  	src, err := f.Open()
   130  	if err != nil {
   131  		return errors.Wrapf(err, "failed to open file %s", f.Name)
   132  	}
   133  	defer src.Close()
   134  	destFile, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o777)
   135  	if err != nil {
   136  		return err
   137  	}
   138  	defer destFile.Close()
   139  	if _, err := io.CopyN(destFile, src, f.FileInfo().Size()); err != nil {
   140  		return err
   141  	}
   142  	return nil
   143  }
   144  
   145  func sanitizeArchivePath(d, t string) (v string, err error) {
   146  	v = filepath.Join(d, t)
   147  	if strings.HasPrefix(v, filepath.Clean(d)) {
   148  		return v, nil
   149  	}
   150  
   151  	return "", fmt.Errorf("%s: %s", "content filepath is tainted", t)
   152  }
   153  
   154  // UnpackClusterAPIBinary unpacks the cluster-api binary from the embedded data so that it can be run to create the
   155  // infrastructure for the cluster.
   156  func UnpackClusterAPIBinary(dir string) error {
   157  	return ClusterAPI.Extract(dir)
   158  }
   159  
   160  // UnpackEnvtestBinaries unpacks the envtest binaries from the embedded data so that it can be run to create the
   161  // infrastructure for the cluster.
   162  func UnpackEnvtestBinaries(dir string) error {
   163  	return EnvTest.Extract(dir)
   164  }