github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/internal/runner/localstorage.go (about) 1 // Copyright 2018-2020 (c) Cognizant Digital Business, Evolutionary AI. All rights reserved. Issued under the Apache 2.0 License. 2 3 package runner 4 5 // This file contains the implementation for the storage sub system that will 6 // be used by the runner to retrieve storage from local storage 7 8 import ( 9 "archive/tar" 10 "bufio" 11 "compress/bzip2" 12 "compress/gzip" 13 "context" 14 "io" 15 "io/ioutil" 16 "os" 17 "path/filepath" 18 19 "github.com/go-stack/stack" 20 21 "github.com/jjeffery/kv" // MIT License 22 ) 23 24 type localStorage struct { 25 } 26 27 // NewLocalStorage is used to allocate and initialize a struct that acts as a receiver 28 // 29 func NewLocalStorage() (s *localStorage, err kv.Error) { 30 return &localStorage{}, nil 31 } 32 33 // Close is a NoP unless overridden 34 func (s *localStorage) Close() { 35 } 36 37 // Hash returns a platform specific hash of the contents of the file that can be used by caching and other functions 38 // to track storage changes etc 39 // 40 func (s *localStorage) Hash(ctx context.Context, name string) (hash string, err kv.Error) { 41 return filepath.Base(name), nil 42 } 43 44 // Gather is used to retrieve files prefixed with a specific key. It is used to retrieve the individual files 45 // associated with a previous Hoard operation 46 // 47 func (s *localStorage) Gather(ctx context.Context, keyPrefix string, outputDir string, tap io.Writer) (warnings []kv.Error, err kv.Error) { 48 return warnings, kv.NewError("unimplemented").With("stack", stack.Trace().TrimRuntime()) 49 } 50 51 // Fetch is used to retrieve a file from a well known disk directory and either 52 // copy it directly into a directory, or unpack the file into the same directory. 53 // 54 // Calling this function with output not being a valid directory will result in an error 55 // being returned. 56 // 57 // The tap can be used to make a side copy of the content that is being read. 58 // 59 func (s *localStorage) Fetch(ctx context.Context, name string, unpack bool, output string, tap io.Writer) (warns []kv.Error, err kv.Error) { 60 61 kv := kv.With("output", output).With("name", name) 62 63 // Make sure output is an existing directory 64 info, errGo := os.Stat(output) 65 if errGo != nil { 66 return warns, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 67 } 68 if !info.IsDir() { 69 return warns, kv.NewError(output+" is not a directory").With("stack", stack.Trace().TrimRuntime()) 70 } 71 72 fileType, err := MimeFromExt(name) 73 if err != nil { 74 warns = append(warns, kv.Wrap(err).With("fn", name).With("type", fileType).With("stack", stack.Trace().TrimRuntime())) 75 } else { 76 warns = append(warns, kv.NewError("debug").With("fn", name).With("type", fileType).With("stack", stack.Trace().TrimRuntime())) 77 } 78 79 obj, errGo := os.Open(filepath.Clean(name)) 80 if errGo != nil { 81 return warns, kv.Wrap(errGo, "could not open file "+name).With("stack", stack.Trace().TrimRuntime()) 82 } 83 defer obj.Close() 84 85 return fetcher(obj, name, output, fileType, unpack) 86 } 87 88 func addReader(obj *os.File, fileType string) (inReader io.ReadCloser, err kv.Error) { 89 switch fileType { 90 case "application/x-gzip", "application/zip": 91 reader, errGo := gzip.NewReader(obj) 92 if errGo != nil { 93 return reader, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 94 } 95 inReader = reader 96 case "application/bzip2", "application/octet-stream": 97 inReader = ioutil.NopCloser(bzip2.NewReader(obj)) 98 default: 99 inReader = ioutil.NopCloser(obj) 100 } 101 return inReader, err 102 } 103 104 func fetcher(obj *os.File, name string, output string, fileType string, unpack bool) (warns []kv.Error, err kv.Error) { 105 // If the unpack flag is set then use a tar decompressor and unpacker 106 // but first make sure the output location is an existing directory 107 if unpack { 108 109 inReader, err := addReader(obj, fileType) 110 if err != nil { 111 return warns, err 112 } 113 defer inReader.Close() 114 115 tarReader := tar.NewReader(inReader) 116 117 for { 118 header, errGo := tarReader.Next() 119 if errGo == io.EOF { 120 break 121 } else if errGo != nil { 122 return warns, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 123 } 124 125 path := filepath.Join(output, header.Name) 126 info := header.FileInfo() 127 if info.IsDir() { 128 if errGo = os.MkdirAll(path, info.Mode()); errGo != nil { 129 return warns, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 130 } 131 continue 132 } 133 134 file, errGo := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode()) 135 if errGo != nil { 136 return warns, kv.Wrap(errGo).With("file", path).With("stack", stack.Trace().TrimRuntime()) 137 } 138 139 _, errGo = io.Copy(file, tarReader) 140 file.Close() 141 if errGo != nil { 142 return warns, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 143 } 144 } 145 } else { 146 fn := filepath.Join(output, filepath.Base(name)) 147 f, errGo := os.Create(fn) 148 if errGo != nil { 149 return warns, kv.Wrap(errGo).With("outputFile", fn).With("stack", stack.Trace().TrimRuntime()) 150 } 151 defer f.Close() 152 153 outf := bufio.NewWriter(f) 154 if _, errGo = io.Copy(outf, obj); errGo != nil { 155 return warns, kv.Wrap(errGo).With("outputFile", fn).With("stack", stack.Trace().TrimRuntime()) 156 } 157 outf.Flush() 158 } 159 return warns, nil 160 } 161 162 // Hoard is not a supported feature of local caching 163 // 164 func (s *localStorage) Hoard(ctx context.Context, src string, destPrefix string) (warns []kv.Error, err kv.Error) { 165 return warns, kv.NewError("localized storage caches do not support write through saving of files").With("stack", stack.Trace().TrimRuntime()) 166 } 167 168 // Deposit is not a supported feature of local caching 169 // 170 func (s *localStorage) Deposit(ctx context.Context, src string, dest string) (warns []kv.Error, err kv.Error) { 171 return warns, kv.NewError("localized storage caches do not support write through saving of files").With("stack", stack.Trace().TrimRuntime()) 172 }