gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/sysconfig/sysconfig.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 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 sysconfig
    21  
    22  import (
    23  	"fmt"
    24  	"path/filepath"
    25  
    26  	"github.com/snapcore/snapd/asserts"
    27  	"github.com/snapcore/snapd/gadget"
    28  	"github.com/snapcore/snapd/snap"
    29  )
    30  
    31  // See https://github.com/snapcore/core20/pull/46
    32  const writableDefaultsDir = "_writable_defaults"
    33  
    34  // Options is the set of options used to configure the run system
    35  type Options struct {
    36  	// CloudInitSrcDir is where to find the cloud-init data when installing it,
    37  	// i.e. in early boot install mode it could be something like
    38  	// filepath.Join(boot.InitramfsUbuntuSeedDir,"data")
    39  	CloudInitSrcDir string
    40  
    41  	// TargetRootDir is the root directory where to install configure
    42  	// data, i.e. for cloud-init during the initramfs it will be something like
    43  	// boot.InstallHostWritableDir
    44  	TargetRootDir string
    45  
    46  	// AllowCloudInit is whether to allow cloud-init to run or not in the
    47  	// TargetRootDir.
    48  	AllowCloudInit bool
    49  
    50  	// GadgetDir is the path of the mounted gadget snap.
    51  	GadgetDir string
    52  
    53  	// GadgetSnap is a snap.Container of the gadget snap. This is used in
    54  	// priority over GadgetDir if set.
    55  	GadgetSnap snap.Container
    56  }
    57  
    58  // Device carries information about the device model and mode that is
    59  // relevant to sysconfig.
    60  type Device interface {
    61  	RunMode() bool
    62  	Classic() bool
    63  
    64  	Kernel() string
    65  	//Base() string
    66  
    67  	HasModeenv() bool
    68  
    69  	//Model() *asserts.Model
    70  }
    71  
    72  type configedDevice struct {
    73  	model *asserts.Model
    74  }
    75  
    76  func (di *configedDevice) RunMode() bool {
    77  	// the functions in sysconfig are used to configure not yet
    78  	// running systems.
    79  	return false
    80  }
    81  
    82  func (d *configedDevice) Classic() bool {
    83  	return d.model.Classic()
    84  }
    85  
    86  func (d *configedDevice) Kernel() string {
    87  	return d.model.Kernel()
    88  }
    89  
    90  func (d *configedDevice) HasModeenv() bool {
    91  	return d.model.Grade() != asserts.ModelGradeUnset
    92  }
    93  
    94  // ApplyFilesystemOnlyDefaultsImpl is initialized by init() of configcore.
    95  var ApplyFilesystemOnlyDefaultsImpl = func(dev Device, rootDir string, defaults map[string]interface{}) error {
    96  	panic("ApplyFilesystemOnlyDefaultsImpl is unset, import overlord/configstate/configcore")
    97  }
    98  
    99  // ApplyFilesystemOnlyDefaults applies (via configcore.filesystemOnlyApply())
   100  // filesystem modifications under rootDir, according to the defaults.
   101  // This is a subset of core config options that is important
   102  // early during boot, before all the configuration is applied as part of
   103  // normal execution of configure hook.
   104  func ApplyFilesystemOnlyDefaults(model *asserts.Model, rootDir string, defaults map[string]interface{}) error {
   105  	dev := &configedDevice{model: model}
   106  	return ApplyFilesystemOnlyDefaultsImpl(dev, rootDir, defaults)
   107  }
   108  
   109  // ConfigureTargetSystem configures the ubuntu-data partition with
   110  // any configuration needed from e.g. the gadget or for cloud-init (and also for
   111  // cloud-init from the gadget).
   112  // It is okay to use both from install mode for run mode, as well as from the
   113  // initramfs for recover mode.
   114  // It is only meant to be used with models that have a grade (i.e. UC20+).
   115  func ConfigureTargetSystem(model *asserts.Model, opts *Options) error {
   116  	// check that we have a uc20 model
   117  	if model.Grade() == asserts.ModelGradeUnset {
   118  		return fmt.Errorf("internal error: ConfigureTargetSystem can only be used with a model with a grade")
   119  	}
   120  
   121  	if err := configureCloudInit(model, opts); err != nil {
   122  		return err
   123  	}
   124  
   125  	var gadgetInfo *gadget.Info
   126  	var err error
   127  	switch {
   128  	case opts.GadgetSnap != nil:
   129  		// we do not perform consistency validation here because
   130  		// such unlikely problems are better surfaced in different
   131  		// and less surprising contexts like the seeding itself
   132  		gadgetInfo, err = gadget.ReadInfoFromSnapFileNoValidate(opts.GadgetSnap, nil)
   133  	case opts.GadgetDir != "":
   134  		gadgetInfo, err = gadget.ReadInfo(opts.GadgetDir, nil)
   135  	}
   136  
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	if gadgetInfo != nil {
   142  		defaults := gadget.SystemDefaults(gadgetInfo.Defaults)
   143  		if len(defaults) > 0 {
   144  			if err := ApplyFilesystemOnlyDefaults(model, WritableDefaultsDir(opts.TargetRootDir), defaults); err != nil {
   145  				return err
   146  			}
   147  		}
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  // WritableDefaultsDir returns the full path of the joined subdir under the
   154  // subtree for default content for system data living at rootdir,
   155  // i.e. rootdir/_writable_defaults/subdir...
   156  func WritableDefaultsDir(rootdir string, subdir ...string) string {
   157  	return filepath.Join(rootdir, writableDefaultsDir, filepath.Join(subdir...))
   158  }