github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/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 client 21 22 import ( 23 "bytes" 24 "context" 25 "encoding/json" 26 "fmt" 27 "net/url" 28 29 "golang.org/x/xerrors" 30 31 "github.com/snapcore/snapd/asserts" 32 ) 33 34 type remodelData struct { 35 NewModel string `json:"new-model"` 36 } 37 38 // Remodel tries to remodel the system with the given assertion data 39 func (client *Client) Remodel(b []byte) (changeID string, err error) { 40 data, err := json.Marshal(&remodelData{ 41 NewModel: string(b), 42 }) 43 if err != nil { 44 return "", fmt.Errorf("cannot marshal remodel data: %v", err) 45 } 46 headers := map[string]string{ 47 "Content-Type": "application/json", 48 } 49 50 return client.doAsync("POST", "/v2/model", nil, headers, bytes.NewReader(data)) 51 } 52 53 // CurrentModelAssertion returns the current model assertion 54 func (client *Client) CurrentModelAssertion() (*asserts.Model, error) { 55 assert, err := currentAssertion(client, "/v2/model") 56 if err != nil { 57 return nil, err 58 } 59 modelAssert, ok := assert.(*asserts.Model) 60 if !ok { 61 return nil, fmt.Errorf("unexpected assertion type (%s) returned", assert.Type().Name) 62 } 63 return modelAssert, nil 64 } 65 66 // CurrentSerialAssertion returns the current serial assertion 67 func (client *Client) CurrentSerialAssertion() (*asserts.Serial, error) { 68 assert, err := currentAssertion(client, "/v2/model/serial") 69 if err != nil { 70 return nil, err 71 } 72 serialAssert, ok := assert.(*asserts.Serial) 73 if !ok { 74 return nil, fmt.Errorf("unexpected assertion type (%s) returned", assert.Type().Name) 75 } 76 return serialAssert, nil 77 } 78 79 // helper function for getting assertions from the daemon via a REST path 80 func currentAssertion(client *Client, path string) (asserts.Assertion, error) { 81 q := url.Values{} 82 83 response, cancel, err := client.rawWithTimeout(context.Background(), "GET", path, q, nil, nil, nil) 84 if err != nil { 85 fmt := "failed to query current assertion: %w" 86 return nil, xerrors.Errorf(fmt, err) 87 } 88 defer cancel() 89 defer response.Body.Close() 90 if response.StatusCode != 200 { 91 return nil, parseError(response) 92 } 93 94 dec := asserts.NewDecoder(response.Body) 95 96 // only decode a single assertion - we can't ever get more than a single 97 // assertion through these endpoints by design 98 assert, err := dec.Decode() 99 if err != nil { 100 return nil, fmt.Errorf("failed to decode assertions: %v", err) 101 } 102 103 return assert, nil 104 }