github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/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 } 42 ) 43 44 func getSnapConf(c *Command, r *http.Request, user *auth.UserState) Response { 45 vars := muxVars(r) 46 snapName := configstate.RemapSnapFromRequest(vars["name"]) 47 48 keys := strutil.CommaSeparatedList(r.URL.Query().Get("keys")) 49 50 s := c.d.overlord.State() 51 s.Lock() 52 tr := config.NewTransaction(s) 53 s.Unlock() 54 55 currentConfValues := make(map[string]interface{}) 56 // Special case - return root document 57 if len(keys) == 0 { 58 keys = []string{""} 59 } 60 for _, key := range keys { 61 var value interface{} 62 if err := tr.Get(snapName, key, &value); err != nil { 63 if config.IsNoOption(err) { 64 if key == "" { 65 // no configuration - return empty document 66 currentConfValues = make(map[string]interface{}) 67 break 68 } 69 return SyncResponse(&resp{ 70 Type: ResponseTypeError, 71 Result: &errorResult{ 72 Message: err.Error(), 73 Kind: client.ErrorKindConfigNoSuchOption, 74 Value: err, 75 }, 76 Status: 400, 77 }, nil) 78 } else { 79 return InternalError("%v", err) 80 } 81 } 82 if key == "" { 83 if len(keys) > 1 { 84 return BadRequest("keys contains zero-length string") 85 } 86 return SyncResponse(value, nil) 87 } 88 89 currentConfValues[key] = value 90 } 91 92 return SyncResponse(currentConfValues, nil) 93 } 94 95 func setSnapConf(c *Command, r *http.Request, user *auth.UserState) Response { 96 vars := muxVars(r) 97 snapName := configstate.RemapSnapFromRequest(vars["name"]) 98 99 var patchValues map[string]interface{} 100 if err := jsonutil.DecodeWithNumber(r.Body, &patchValues); err != nil { 101 return BadRequest("cannot decode request body into patch values: %v", err) 102 } 103 104 st := c.d.overlord.State() 105 st.Lock() 106 defer st.Unlock() 107 108 taskset, err := configstate.ConfigureInstalled(st, snapName, patchValues, 0) 109 if err != nil { 110 // TODO: just return snap-not-installed instead ? 111 if _, ok := err.(*snap.NotInstalledError); ok { 112 return SnapNotFound(snapName, err) 113 } 114 return errToResponse(err, []string{snapName}, InternalError, "%v") 115 } 116 117 summary := fmt.Sprintf("Change configuration of %q snap", snapName) 118 change := newChange(st, "configure-snap", summary, []*state.TaskSet{taskset}, []string{snapName}) 119 120 st.EnsureBefore(0) 121 122 return AsyncResponse(nil, &Meta{Change: change.ID()}) 123 }