github.imxd.top/openshift/source-to-image@v1.2.0/pkg/util/injection.go (about) 1 package util 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/openshift/source-to-image/pkg/api" 11 "github.com/openshift/source-to-image/pkg/util/fs" 12 ) 13 14 // FixInjectionsWithRelativePath fixes the injections that does not specify the 15 // destination directory or the directory is relative to use the provided 16 // working directory. 17 func FixInjectionsWithRelativePath(workdir string, injections api.VolumeList) api.VolumeList { 18 if len(injections) == 0 { 19 return injections 20 } 21 newList := api.VolumeList{} 22 for _, injection := range injections { 23 changed := false 24 if filepath.Clean(filepath.FromSlash(injection.Destination)) == "." { 25 injection.Destination = filepath.ToSlash(workdir) 26 changed = true 27 } 28 if filepath.ToSlash(injection.Destination)[0] != '/' { 29 injection.Destination = filepath.ToSlash(filepath.Join(workdir, injection.Destination)) 30 changed = true 31 } 32 if changed { 33 log.V(5).Infof("Using %q as a destination for injecting %q", injection.Destination, injection.Source) 34 } 35 newList = append(newList, injection) 36 } 37 return newList 38 } 39 40 // ListFilesToTruncate returns a flat list of all files that are injected into a 41 // container which need to be truncated. All files from nested directories are returned in the list. 42 func ListFilesToTruncate(fs fs.FileSystem, injections api.VolumeList) ([]string, error) { 43 result := []string{} 44 for _, s := range injections { 45 if s.Keep { 46 continue 47 } 48 files, err := ListFiles(fs, s) 49 if err != nil { 50 return nil, err 51 } 52 result = append(result, files...) 53 } 54 return result, nil 55 } 56 57 // ListFiles returns a flat list of all files injected into a container for the given `VolumeSpec`. 58 func ListFiles(fs fs.FileSystem, spec api.VolumeSpec) ([]string, error) { 59 result := []string{} 60 if _, err := os.Stat(spec.Source); err != nil { 61 return nil, err 62 } 63 err := fs.Walk(spec.Source, func(path string, f os.FileInfo, err error) error { 64 if err != nil { 65 return err 66 } 67 68 // Detected files will be truncated. k8s' AtomicWriter creates 69 // directories and symlinks to directories in order to inject files. 70 // An attempt to truncate either a dir or symlink to a dir will fail. 71 // Thus, we need to dereference symlinks to see if they might point 72 // to a directory. 73 // Do not try to simplify this logic to simply return nil if a symlink 74 // is detected. During the tar transfer to an assemble image, symlinked 75 // files are turned concrete (i.e. they will be turned into regular files 76 // containing the content of their target). These newly concrete files 77 // need to be truncated as well. 78 79 if f.Mode()&os.ModeSymlink != 0 { 80 linkDest, err := filepath.EvalSymlinks(path) 81 if err != nil { 82 return fmt.Errorf("unable to evaluate symlink [%v]: %v", path, err) 83 } 84 // Evaluate the destination of the link. 85 f, err = os.Lstat(linkDest) 86 if err != nil { 87 // This is not a fatal error. If AtomicWrite tried multiple times, a symlink might not point 88 // to a valid destination. 89 log.Warningf("Unable to lstat symlink destination [%s]->[%s]. err: %v. Partial atomic write?", path, linkDest, err) 90 return nil 91 } 92 } 93 94 if f.IsDir() { 95 return nil 96 } 97 98 newPath := filepath.ToSlash(filepath.Join(spec.Destination, strings.TrimPrefix(path, spec.Source))) 99 result = append(result, newPath) 100 return nil 101 }) 102 if err != nil { 103 return nil, err 104 } 105 return result, nil 106 } 107 108 // CreateTruncateFilesScript creates a shell script that contains truncation 109 // of all files we injected into the container. The path to the script is returned. 110 // When the scriptName is provided, it is also truncated together with all 111 // secrets. 112 func CreateTruncateFilesScript(files []string, scriptName string) (string, error) { 113 rmScript := "set -e\n" 114 for _, s := range files { 115 rmScript += fmt.Sprintf("truncate -s0 %q\n", s) 116 } 117 118 f, err := ioutil.TempFile("", "s2i-injection-remove") 119 if err != nil { 120 return "", err 121 } 122 if len(scriptName) > 0 { 123 rmScript += fmt.Sprintf("truncate -s0 %q\n", scriptName) 124 } 125 rmScript += "set +e\n" 126 err = ioutil.WriteFile(f.Name(), []byte(rmScript), 0700) 127 return f.Name(), err 128 } 129 130 // CreateInjectionResultFile creates a result file with the message from the provided injection 131 // error. The path to the result file is returned. If the provided error is nil, an empty file is 132 // created. 133 func CreateInjectionResultFile(injectErr error) (string, error) { 134 f, err := ioutil.TempFile("", "s2i-injection-result") 135 if err != nil { 136 return "", err 137 } 138 if injectErr != nil { 139 err = ioutil.WriteFile(f.Name(), []byte(injectErr.Error()), 0700) 140 } 141 return f.Name(), err 142 } 143 144 // HandleInjectionError handles the error caused by injection and provide 145 // reasonable suggestion to users. 146 func HandleInjectionError(p api.VolumeSpec, err error) error { 147 if err == nil { 148 return nil 149 } 150 if strings.Contains(err.Error(), "no such file or directory") { 151 log.Errorf("The destination directory for %q injection must exist in container (%q)", p.Source, p.Destination) 152 return err 153 } 154 log.Errorf("Error occurred during injecting %q to %q: %v", p.Source, p.Destination, err) 155 return err 156 }