gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/daemon/api_snap_conf.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-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 daemon 21 22 import ( 23 "fmt" 24 "net/http" 25 26 "github.com/snapcore/snapd/client" 27 "github.com/snapcore/snapd/jsonutil" 28 "github.com/snapcore/snapd/overlord/auth" 29 "github.com/snapcore/snapd/overlord/configstate" 30 "github.com/snapcore/snapd/overlord/configstate/config" 31 "github.com/snapcore/snapd/overlord/state" 32 "github.com/snapcore/snapd/snap" 33 "github.com/snapcore/snapd/strutil" 34 ) 35 36 var ( 37 snapConfCmd = &Command{ 38 Path: "/v2/snaps/{name}/conf", 39 GET: getSnapConf, 40 PUT: setSnapConf, 41 ReadAccess: authenticatedAccess{}, 42 WriteAccess: authenticatedAccess{}, 43 } 44 ) 45 46 func getSnapConf(c *Command, r *http.Request, user *auth.UserState) Response { 47 vars := muxVars(r) 48 snapName := configstate.RemapSnapFromRequest(vars["name"]) 49 50 keys := strutil.CommaSeparatedList(r.URL.Query().Get("keys")) 51 52 s := c.d.overlord.State() 53 s.Lock() 54 tr := config.NewTransaction(s) 55 s.Unlock() 56 57 currentConfValues := make(map[string]interface{}) 58 // Special case - return root document 59 if len(keys) == 0 { 60 keys = []string{""} 61 } 62 for _, key := range keys { 63 var value interface{} 64 if err := tr.Get(snapName, key, &value); err != nil { 65 if config.IsNoOption(err) { 66 if key == "" { 67 // no configuration - return empty document 68 currentConfValues = make(map[string]interface{}) 69 break 70 } 71 return &apiError{ 72 Status: 400, 73 Message: err.Error(), 74 Kind: client.ErrorKindConfigNoSuchOption, 75 Value: err, 76 } 77 } else { 78 return InternalError("%v", err) 79 } 80 } 81 if key == "" { 82 if len(keys) > 1 { 83 return BadRequest("keys contains zero-length string") 84 } 85 return SyncResponse(value) 86 } 87 88 currentConfValues[key] = value 89 } 90 91 return SyncResponse(currentConfValues) 92 } 93 94 func setSnapConf(c *Command, r *http.Request, user *auth.UserState) Response { 95 vars := muxVars(r) 96 snapName := configstate.RemapSnapFromRequest(vars["name"]) 97 98 var patchValues map[string]interface{} 99 if err := jsonutil.DecodeWithNumber(r.Body, &patchValues); err != nil { 100 return BadRequest("cannot decode request body into patch values: %v", err) 101 } 102 103 st := c.d.overlord.State() 104 st.Lock() 105 defer st.Unlock() 106 107 taskset, err := configstate.ConfigureInstalled(st, snapName, patchValues, 0) 108 if err != nil { 109 // TODO: just return snap-not-installed instead ? 110 if _, ok := err.(*snap.NotInstalledError); ok { 111 return SnapNotFound(snapName, err) 112 } 113 return errToResponse(err, []string{snapName}, InternalError, "%v") 114 } 115 116 summary := fmt.Sprintf("Change configuration of %q snap", snapName) 117 change := newChange(st, "configure-snap", summary, []*state.TaskSet{taskset}, []string{snapName}) 118 119 st.EnsureBefore(0) 120 121 return AsyncResponse(nil, change.ID()) 122 }