github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/configstate/configstate.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 configstate implements the manager and state aspects responsible for 21 // the configuration of snaps. 22 package configstate 23 24 import ( 25 "fmt" 26 "os" 27 "time" 28 29 "github.com/snapcore/snapd/i18n" 30 "github.com/snapcore/snapd/overlord/hookstate" 31 "github.com/snapcore/snapd/overlord/snapstate" 32 "github.com/snapcore/snapd/overlord/state" 33 "github.com/snapcore/snapd/snap" 34 ) 35 36 func init() { 37 snapstate.Configure = Configure 38 } 39 40 func ConfigureHookTimeout() time.Duration { 41 timeout := 5 * time.Minute 42 if s := os.Getenv("SNAPD_CONFIGURE_HOOK_TIMEOUT"); s != "" { 43 if to, err := time.ParseDuration(s); err == nil { 44 timeout = to 45 } 46 } 47 return timeout 48 } 49 50 func canConfigure(st *state.State, snapName string) error { 51 // the "core" snap/pseudonym can always be configured as it 52 // is handled internally 53 if snapName == "core" { 54 return nil 55 } 56 57 var snapst snapstate.SnapState 58 err := snapstate.Get(st, snapName, &snapst) 59 if err != nil && err != state.ErrNoState { 60 return err 61 } 62 63 if !snapst.IsInstalled() { 64 return &snap.NotInstalledError{Snap: snapName} 65 } 66 67 // the "snapd" snap cannot be configured yet 68 typ, err := snapst.Type() 69 if err != nil { 70 return err 71 } 72 if typ == snap.TypeSnapd { 73 return fmt.Errorf(`cannot configure the "snapd" snap, please use "system" instead`) 74 } 75 76 // bases cannot be configured for now 77 typ, err = snapst.Type() 78 if err != nil { 79 return err 80 } 81 if typ == snap.TypeBase { 82 return fmt.Errorf("cannot configure snap %q because it is of type 'base'", snapName) 83 } 84 85 return snapstate.CheckChangeConflict(st, snapName, nil) 86 } 87 88 // ConfigureInstalled returns a taskset to apply the given 89 // configuration patch for an installed snap. It returns 90 // snap.NotInstalledError if the snap is not installed. 91 func ConfigureInstalled(st *state.State, snapName string, patch map[string]interface{}, flags int) (*state.TaskSet, error) { 92 if err := canConfigure(st, snapName); err != nil { 93 return nil, err 94 } 95 96 taskset := Configure(st, snapName, patch, flags) 97 return taskset, nil 98 } 99 100 // Configure returns a taskset to apply the given configuration patch. 101 func Configure(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet { 102 summary := fmt.Sprintf(i18n.G("Run configure hook of %q snap"), snapName) 103 // regular configuration hook 104 hooksup := &hookstate.HookSetup{ 105 Snap: snapName, 106 Hook: "configure", 107 Optional: len(patch) == 0, 108 IgnoreError: flags&snapstate.IgnoreHookError != 0, 109 TrackError: flags&snapstate.TrackHookError != 0, 110 // all configure hooks must finish within this timeout 111 Timeout: ConfigureHookTimeout(), 112 } 113 var contextData map[string]interface{} 114 if flags&snapstate.UseConfigDefaults != 0 { 115 contextData = map[string]interface{}{"use-defaults": true} 116 } else if len(patch) > 0 { 117 contextData = map[string]interface{}{"patch": patch} 118 } 119 120 if hooksup.Optional { 121 summary = fmt.Sprintf(i18n.G("Run configure hook of %q snap if present"), snapName) 122 } 123 124 task := hookstate.HookTask(st, summary, hooksup, contextData) 125 return state.NewTaskSet(task) 126 } 127 128 // RemapSnapFromRequest renames a snap as received from an API request 129 func RemapSnapFromRequest(snapName string) string { 130 if snapName == "system" { 131 return "core" 132 } 133 return snapName 134 } 135 136 // RemapSnapToResponse renames a snap as about to be sent from an API response 137 func RemapSnapToResponse(snapName string) string { 138 if snapName == "core" { 139 return "system" 140 } 141 return snapName 142 }