github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/common/charms.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "archive/zip" 8 "io" 9 "io/ioutil" 10 "os" 11 "path" 12 "path/filepath" 13 14 "github.com/juju/errors" 15 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/state/storage" 18 ) 19 20 // ReadCharmFromStorage fetches the charm at the specified path from the store 21 // and copies it to a temp directory in dataDir. 22 func ReadCharmFromStorage(store storage.Storage, dataDir, storagePath string) (string, error) { 23 // Ensure the working directory exists. 24 tmpDir := filepath.Join(dataDir, "charm-get-tmp") 25 if err := os.MkdirAll(tmpDir, 0755); err != nil { 26 return "", errors.Annotate(err, "cannot create charms tmp directory") 27 } 28 29 // Use the storage to retrieve and save the charm archive. 30 reader, _, err := store.Get(storagePath) 31 if err != nil { 32 return "", errors.Annotate(err, "cannot get charm from model storage") 33 } 34 defer reader.Close() 35 36 charmFile, err := ioutil.TempFile(tmpDir, "charm") 37 if err != nil { 38 return "", errors.Annotate(err, "cannot create charm archive file") 39 } 40 if _, err = io.Copy(charmFile, reader); err != nil { 41 cleanupFile(charmFile) 42 return "", errors.Annotate(err, "error processing charm archive download") 43 } 44 charmFile.Close() 45 return charmFile.Name(), nil 46 } 47 48 // On windows we cannot remove a file until it has been closed 49 // If this poses an active problem somewhere else it will be refactored in 50 // utils and used everywhere. 51 func cleanupFile(file *os.File) { 52 // Errors are ignored because it is ok for this to be called when 53 // the file is already closed or has been moved. 54 file.Close() 55 os.Remove(file.Name()) 56 } 57 58 // CharmArchiveEntry retrieves the specified entry from the zip archive. 59 func CharmArchiveEntry(charmPath, entryPath string, wantIcon bool) ([]byte, error) { 60 // TODO(fwereade) 2014-01-27 bug #1285685 61 // This doesn't handle symlinks helpfully, and should be talking in 62 // terms of bundles rather than zip readers; but this demands thought 63 // and design and is not amenable to a quick fix. 64 zipReader, err := zip.OpenReader(charmPath) 65 if err != nil { 66 return nil, errors.Annotatef(err, "unable to read charm") 67 } 68 defer zipReader.Close() 69 for _, file := range zipReader.File { 70 if path.Clean(file.Name) != entryPath { 71 continue 72 } 73 fileInfo := file.FileInfo() 74 if fileInfo.IsDir() { 75 return nil, ¶ms.Error{ 76 Message: "directory listing not allowed", 77 Code: params.CodeForbidden, 78 } 79 } 80 contents, err := file.Open() 81 if err != nil { 82 return nil, errors.Annotatef(err, "unable to read file %q", entryPath) 83 } 84 defer contents.Close() 85 return ioutil.ReadAll(contents) 86 } 87 if wantIcon { 88 // An icon was requested but none was found in the archive so 89 // return the default icon instead. 90 return []byte(DefaultCharmIcon), nil 91 } 92 return nil, errors.NotFoundf("charm file") 93 }