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  }