github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/cmd/snap/cmd_prepare_image.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2021 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 "encoding/json" 24 "fmt" 25 "os" 26 "strings" 27 28 "github.com/jessevdk/go-flags" 29 30 "github.com/snapcore/snapd/i18n" 31 "github.com/snapcore/snapd/image" 32 ) 33 34 type cmdPrepareImage struct { 35 Classic bool `long:"classic"` 36 Architecture string `long:"arch"` 37 38 Positional struct { 39 ModelAssertionFn string 40 TargetDir string 41 } `positional-args:"yes" required:"yes"` 42 43 Channel string `long:"channel"` 44 45 Customize string `long:"customize" hidden:"yes"` 46 47 // TODO: introduce SnapWithChannel? 48 Snaps []string `long:"snap" value-name:"<snap>[=<channel>]"` 49 ExtraSnaps []string `long:"extra-snaps" hidden:"yes"` // DEPRECATED 50 } 51 52 func init() { 53 addCommand("prepare-image", 54 i18n.G("Prepare a device image"), 55 i18n.G(` 56 The prepare-image command performs some of the steps necessary for 57 creating device images. 58 59 For core images it is not invoked directly but usually via 60 ubuntu-image. 61 62 For preparing classic images it supports a --classic mode`), 63 func() flags.Commander { return &cmdPrepareImage{} }, 64 map[string]string{ 65 // TRANSLATORS: This should not start with a lowercase letter. 66 "classic": i18n.G("Enable classic mode to prepare a classic model image"), 67 // TRANSLATORS: This should not start with a lowercase letter. 68 "arch": i18n.G("Specify an architecture for snaps for --classic when the model does not"), 69 // TRANSLATORS: This should not start with a lowercase letter. 70 "snap": i18n.G("Include the given snap from the store or a local file and/or specify the channel to track for the given snap"), 71 // TRANSLATORS: This should not start with a lowercase letter. 72 "extra-snaps": i18n.G("Extra snaps to be installed (DEPRECATED)"), 73 // TRANSLATORS: This should not start with a lowercase letter. 74 "channel": i18n.G("The channel to use"), 75 // TRANSLATORS: This should not start with a lowercase letter. 76 "customize": i18n.G("Image customizations specified as JSON file."), 77 }, []argDesc{ 78 { 79 // TRANSLATORS: This needs to begin with < and end with > 80 name: i18n.G("<model-assertion>"), 81 // TRANSLATORS: This should not start with a lowercase letter. 82 desc: i18n.G("The model assertion name"), 83 }, { 84 // TRANSLATORS: This needs to begin with < and end with > 85 name: i18n.G("<target-dir>"), 86 // TRANSLATORS: This should not start with a lowercase letter. 87 desc: i18n.G("The target directory"), 88 }, 89 }) 90 } 91 92 var imagePrepare = image.Prepare 93 94 func (x *cmdPrepareImage) Execute(args []string) error { 95 opts := &image.Options{ 96 Snaps: x.ExtraSnaps, 97 ModelFile: x.Positional.ModelAssertionFn, 98 Channel: x.Channel, 99 Architecture: x.Architecture, 100 } 101 102 if x.Customize != "" { 103 custo, err := readImageCustomizations(x.Customize) 104 if err != nil { 105 return err 106 } 107 opts.Customizations = *custo 108 } 109 110 snaps := make([]string, 0, len(x.Snaps)+len(x.ExtraSnaps)) 111 snapChannels := make(map[string]string) 112 for _, snapWChannel := range x.Snaps { 113 snapAndChannel := strings.SplitN(snapWChannel, "=", 2) 114 snaps = append(snaps, snapAndChannel[0]) 115 if len(snapAndChannel) == 2 { 116 snapChannels[snapAndChannel[0]] = snapAndChannel[1] 117 } 118 } 119 120 snaps = append(snaps, x.ExtraSnaps...) 121 122 if len(snaps) != 0 { 123 opts.Snaps = snaps 124 } 125 if len(snapChannels) != 0 { 126 opts.SnapChannels = snapChannels 127 } 128 129 // store-wide cohort key via env, see image/options.go 130 opts.WideCohortKey = os.Getenv("UBUNTU_STORE_COHORT_KEY") 131 132 opts.PrepareDir = x.Positional.TargetDir 133 opts.Classic = x.Classic 134 135 return imagePrepare(opts) 136 } 137 138 func readImageCustomizations(customizationsFile string) (*image.Customizations, error) { 139 f, err := os.Open(customizationsFile) 140 if err != nil { 141 return nil, fmt.Errorf("cannot read image customizations: %v", err) 142 } 143 defer f.Close() 144 dec := json.NewDecoder(f) 145 var custo image.Customizations 146 if err := dec.Decode(&custo); err != nil { 147 return nil, fmt.Errorf("cannot parse customizations %q: %v", customizationsFile, err) 148 } 149 return &custo, nil 150 }