github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/snap/cmd_download.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 import ( 23 "fmt" 24 "os" 25 "path/filepath" 26 "strings" 27 28 "github.com/jessevdk/go-flags" 29 30 "github.com/snapcore/snapd/asserts" 31 "github.com/snapcore/snapd/asserts/sysdb" 32 "github.com/snapcore/snapd/i18n" 33 "github.com/snapcore/snapd/image" 34 "github.com/snapcore/snapd/snap" 35 ) 36 37 type cmdDownload struct { 38 channelMixin 39 Revision string `long:"revision"` 40 Basename string `long:"basename"` 41 TargetDir string `long:"target-directory"` 42 43 CohortKey string `long:"cohort"` 44 Positional struct { 45 Snap remoteSnapName 46 } `positional-args:"true" required:"true"` 47 } 48 49 var shortDownloadHelp = i18n.G("Download the given snap") 50 var longDownloadHelp = i18n.G(` 51 The download command downloads the given snap and its supporting assertions 52 to the current directory with .snap and .assert file extensions, respectively. 53 `) 54 55 func init() { 56 addCommand("download", shortDownloadHelp, longDownloadHelp, func() flags.Commander { 57 return &cmdDownload{} 58 }, channelDescs.also(map[string]string{ 59 // TRANSLATORS: This should not start with a lowercase letter. 60 "revision": i18n.G("Download the given revision of a snap, to which you must have developer access"), 61 // TRANSLATORS: This should not start with a lowercase letter. 62 "cohort": i18n.G("Download from the given cohort"), 63 // TRANSLATORS: This should not start with a lowercase letter. 64 "basename": i18n.G("Use this basename for the snap and assertion files (defaults to <snap>_<revision>)"), 65 // TRANSLATORS: This should not start with a lowercase letter. 66 "target-directory": i18n.G("Download to this directory (defaults to the current directory)"), 67 }), []argDesc{{ 68 name: "<snap>", 69 // TRANSLATORS: This should not start with a lowercase letter. 70 desc: i18n.G("Snap name"), 71 }}) 72 } 73 74 func fetchSnapAssertions(tsto *image.ToolingStore, snapPath string, snapInfo *snap.Info) (string, error) { 75 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 76 Backstore: asserts.NewMemoryBackstore(), 77 Trusted: sysdb.Trusted(), 78 }) 79 if err != nil { 80 return "", err 81 } 82 83 assertPath := strings.TrimSuffix(snapPath, filepath.Ext(snapPath)) + ".assert" 84 w, err := os.Create(assertPath) 85 if err != nil { 86 return "", fmt.Errorf(i18n.G("cannot create assertions file: %v"), err) 87 } 88 defer w.Close() 89 90 encoder := asserts.NewEncoder(w) 91 save := func(a asserts.Assertion) error { 92 return encoder.Encode(a) 93 } 94 f := tsto.AssertionFetcher(db, save) 95 96 _, err = image.FetchAndCheckSnapAssertions(snapPath, snapInfo, f, db) 97 return assertPath, err 98 } 99 100 func (x *cmdDownload) Execute(args []string) error { 101 if strings.ContainsRune(x.Basename, filepath.Separator) { 102 return fmt.Errorf(i18n.G("cannot specify a path in basename (use --target-dir for that)")) 103 } 104 if err := x.setChannelFromCommandline(); err != nil { 105 return err 106 } 107 108 if len(args) > 0 { 109 return ErrExtraArgs 110 } 111 112 var revision snap.Revision 113 if x.Revision == "" { 114 revision = snap.R(0) 115 } else { 116 if x.Channel != "" { 117 return fmt.Errorf(i18n.G("cannot specify both channel and revision")) 118 } 119 if x.CohortKey != "" { 120 return fmt.Errorf(i18n.G("cannot specify both cohort and revision")) 121 } 122 var err error 123 revision, err = snap.ParseRevision(x.Revision) 124 if err != nil { 125 return err 126 } 127 } 128 129 snapName := string(x.Positional.Snap) 130 131 tsto, err := image.NewToolingStore() 132 if err != nil { 133 return err 134 } 135 136 fmt.Fprintf(Stdout, i18n.G("Fetching snap %q\n"), snapName) 137 dlOpts := image.DownloadOptions{ 138 TargetDir: x.TargetDir, 139 Basename: x.Basename, 140 Channel: x.Channel, 141 CohortKey: x.CohortKey, 142 Revision: revision, 143 // if something goes wrong, don't force it to start over again 144 LeavePartialOnError: true, 145 } 146 snapPath, snapInfo, err := tsto.DownloadSnap(snapName, dlOpts) 147 if err != nil { 148 return err 149 } 150 151 fmt.Fprintf(Stdout, i18n.G("Fetching assertions for %q\n"), snapName) 152 assertPath, err := fetchSnapAssertions(tsto, snapPath, snapInfo) 153 if err != nil { 154 return err 155 } 156 157 // simplify paths 158 wd, _ := os.Getwd() 159 if p, err := filepath.Rel(wd, assertPath); err == nil { 160 assertPath = p 161 } 162 if p, err := filepath.Rel(wd, snapPath); err == nil { 163 snapPath = p 164 } 165 // add a hint what to do with the downloaded snap (LP:1676707) 166 fmt.Fprintf(Stdout, i18n.G(`Install the snap with: 167 snap ack %s 168 snap install %s 169 `), assertPath, snapPath) 170 171 return nil 172 }