github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/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 }