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 }