gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/daemon/api_model.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 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 "encoding/json" 24 "net/http" 25 26 "github.com/snapcore/snapd/asserts" 27 "github.com/snapcore/snapd/client" 28 "github.com/snapcore/snapd/overlord/auth" 29 "github.com/snapcore/snapd/overlord/devicestate" 30 "github.com/snapcore/snapd/overlord/state" 31 ) 32 33 var ( 34 serialModelCmd = &Command{ 35 Path: "/v2/model/serial", 36 GET: getSerial, 37 ReadAccess: openAccess{}, 38 } 39 modelCmd = &Command{ 40 Path: "/v2/model", 41 POST: postModel, 42 GET: getModel, 43 ReadAccess: openAccess{}, 44 WriteAccess: rootAccess{}, 45 } 46 ) 47 48 var devicestateRemodel = devicestate.Remodel 49 50 type postModelData struct { 51 NewModel string `json:"new-model"` 52 } 53 54 type modelAssertJSON struct { 55 Headers map[string]interface{} `json:"headers,omitempty"` 56 Body string `json:"body,omitempty"` 57 } 58 59 func postModel(c *Command, r *http.Request, _ *auth.UserState) Response { 60 defer r.Body.Close() 61 var data postModelData 62 decoder := json.NewDecoder(r.Body) 63 if err := decoder.Decode(&data); err != nil { 64 return BadRequest("cannot decode request body into remodel operation: %v", err) 65 } 66 rawNewModel, err := asserts.Decode([]byte(data.NewModel)) 67 if err != nil { 68 return BadRequest("cannot decode new model assertion: %v", err) 69 } 70 newModel, ok := rawNewModel.(*asserts.Model) 71 if !ok { 72 return BadRequest("new model is not a model assertion: %v", newModel.Type()) 73 } 74 75 st := c.d.overlord.State() 76 st.Lock() 77 defer st.Unlock() 78 79 chg, err := devicestateRemodel(st, newModel) 80 if err != nil { 81 return BadRequest("cannot remodel device: %v", err) 82 } 83 ensureStateSoon(st) 84 85 return AsyncResponse(nil, chg.ID()) 86 87 } 88 89 // getModel gets the current model assertion using the DeviceManager 90 func getModel(c *Command, r *http.Request, _ *auth.UserState) Response { 91 opts, err := parseHeadersFormatOptionsFromURL(r.URL.Query()) 92 if err != nil { 93 return BadRequest(err.Error()) 94 } 95 96 st := c.d.overlord.State() 97 st.Lock() 98 defer st.Unlock() 99 100 devmgr := c.d.overlord.DeviceManager() 101 102 model, err := devmgr.Model() 103 if err == state.ErrNoState { 104 return &apiError{ 105 Status: 404, 106 Message: "no model assertion yet", 107 Kind: client.ErrorKindAssertionNotFound, 108 Value: "model", 109 } 110 } 111 if err != nil { 112 return InternalError("accessing model failed: %v", err) 113 } 114 115 if opts.jsonResult { 116 modelJSON := modelAssertJSON{} 117 118 modelJSON.Headers = model.Headers() 119 if !opts.headersOnly { 120 modelJSON.Body = string(model.Body()) 121 } 122 123 return SyncResponse(modelJSON) 124 } 125 126 return AssertResponse([]asserts.Assertion{model}, false) 127 } 128 129 // getSerial gets the current serial assertion using the DeviceManager 130 func getSerial(c *Command, r *http.Request, _ *auth.UserState) Response { 131 opts, err := parseHeadersFormatOptionsFromURL(r.URL.Query()) 132 if err != nil { 133 return BadRequest(err.Error()) 134 } 135 136 st := c.d.overlord.State() 137 st.Lock() 138 defer st.Unlock() 139 140 devmgr := c.d.overlord.DeviceManager() 141 142 serial, err := devmgr.Serial() 143 if err == state.ErrNoState { 144 return &apiError{ 145 Status: 404, 146 Message: "no serial assertion yet", 147 Kind: client.ErrorKindAssertionNotFound, 148 Value: "serial", 149 } 150 } 151 if err != nil { 152 return InternalError("accessing serial failed: %v", err) 153 } 154 155 if opts.jsonResult { 156 serialJSON := modelAssertJSON{} 157 158 serialJSON.Headers = serial.Headers() 159 if !opts.headersOnly { 160 serialJSON.Body = string(serial.Body()) 161 } 162 163 return SyncResponse(serialJSON) 164 } 165 166 return AssertResponse([]asserts.Assertion{serial}, false) 167 }