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

     1  package asset
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"sort"
     9  
    10  	"github.com/pkg/errors"
    11  	"github.com/sirupsen/logrus"
    12  	"sigs.k8s.io/controller-runtime/pkg/client"
    13  )
    14  
    15  const (
    16  	// ClusterCreationError is the error when terraform fails, implying infrastructure failures
    17  	ClusterCreationError = "failed to create cluster"
    18  	// InstallConfigError wraps all configuration errors in one single error
    19  	InstallConfigError = "failed to create install config"
    20  )
    21  
    22  // Asset used to install OpenShift.
    23  type Asset interface {
    24  	// Dependencies returns the assets upon which this asset directly depends.
    25  	Dependencies() []Asset
    26  
    27  	// Generate generates this asset given the states of its parent assets.
    28  	Generate(context.Context, Parents) error
    29  
    30  	// Name returns the human-friendly name of the asset.
    31  	Name() string
    32  }
    33  
    34  // WritableAsset is an Asset that has files that can be written to disk.
    35  // It can also be loaded from disk.
    36  type WritableAsset interface {
    37  	Asset
    38  
    39  	// Files returns the files to write.
    40  	Files() []*File
    41  
    42  	// Load returns the on-disk asset if it exists.
    43  	// The asset object should be changed only when it's loaded successfully.
    44  	Load(FileFetcher) (found bool, err error)
    45  }
    46  
    47  // WritableRuntimeAsset is a WriteableAsset that has files that can be written to disk,
    48  // in addition to a manifest file that contains the runtime object.
    49  type WritableRuntimeAsset interface {
    50  	WritableAsset
    51  
    52  	// RuntimeFiles returns the manifest files along with their
    53  	// instantiated runtime object.
    54  	RuntimeFiles() []*RuntimeFile
    55  }
    56  
    57  // File is a file for an Asset.
    58  type File struct {
    59  	// Filename is the name of the file.
    60  	Filename string
    61  	// Data is the contents of the file.
    62  	Data []byte
    63  }
    64  
    65  // RuntimeFile is a file that contains a manifest file and a runtime object.
    66  type RuntimeFile struct {
    67  	File
    68  
    69  	Object client.Object `json:"-"`
    70  }
    71  
    72  // PersistToFile writes all of the files of the specified asset into the specified
    73  // directory.
    74  func PersistToFile(asset WritableAsset, directory string) error {
    75  	for _, f := range asset.Files() {
    76  		if f == nil {
    77  			panic("asset.Files() returned nil")
    78  		}
    79  		path := filepath.Join(directory, f.Filename)
    80  		if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil {
    81  			return errors.Wrap(err, "failed to create dir")
    82  		}
    83  		if err := os.WriteFile(path, f.Data, 0o640); err != nil { //nolint:gosec // no sensitive info
    84  			return errors.Wrap(err, "failed to write file")
    85  		}
    86  	}
    87  	return nil
    88  }
    89  
    90  // DeleteAssetFromDisk removes all the files for asset from disk.
    91  // this is function is not safe for calling concurrently on the same directory.
    92  func DeleteAssetFromDisk(asset WritableAsset, directory string) error {
    93  	logrus.Debugf("Purging asset %q from disk", asset.Name())
    94  	for _, f := range asset.Files() {
    95  		path := filepath.Join(directory, f.Filename)
    96  		if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
    97  			return errors.Wrap(err, "failed to remove file")
    98  		}
    99  
   100  		dir := filepath.Dir(path)
   101  		ok, err := isDirEmpty(dir)
   102  		if err != nil && !os.IsNotExist(err) {
   103  			return errors.Wrap(err, "failed to read directory")
   104  		}
   105  		if ok {
   106  			if err := os.Remove(dir); err != nil {
   107  				return errors.Wrap(err, "failed to remove directory")
   108  			}
   109  		}
   110  	}
   111  	return nil
   112  }
   113  
   114  func isDirEmpty(name string) (bool, error) {
   115  	f, err := os.Open(name)
   116  	if err != nil {
   117  		return false, err
   118  	}
   119  	defer f.Close()
   120  
   121  	_, err = f.Readdirnames(1) // Or f.Readdir(1)
   122  	if err == io.EOF {
   123  		return true, nil
   124  	}
   125  	return false, err // Either not empty or error, suits both cases
   126  }
   127  
   128  // SortFiles sorts the specified files by file name.
   129  func SortFiles(files []*File) {
   130  	sort.Slice(files, func(i, j int) bool { return files[i].Filename < files[j].Filename })
   131  }
   132  
   133  // SortManifestFiles sorts the specified files by file name.
   134  func SortManifestFiles(files []*RuntimeFile) {
   135  	sort.Slice(files, func(i, j int) bool { return files[i].Filename < files[j].Filename })
   136  }