github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/systems.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 client 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "fmt" 26 27 "golang.org/x/xerrors" 28 29 "github.com/snapcore/snapd/snap" 30 ) 31 32 // SystemModelData contains information about the model 33 type SystemModelData struct { 34 // Model as the model assertion 35 Model string `json:"model,omitempty"` 36 // BrandID corresponds to brand-id in the model assertion 37 BrandID string `json:"brand-id,omitempty"` 38 // DisplayName is human friendly name, corresponds to display-name in 39 // the model assertion 40 DisplayName string `json:"display-name,omitempty"` 41 } 42 43 type System struct { 44 // Current is true when the system running now was installed from that 45 // recovery seed 46 Current bool `json:"current,omitempty"` 47 // Label of the recovery system 48 Label string `json:"label,omitempty"` 49 // Model information 50 Model SystemModelData `json:"model,omitempty"` 51 // Brand information 52 Brand snap.StoreAccount `json:"brand,omitempty"` 53 // Actions available for this system 54 Actions []SystemAction `json:"actions,omitempty"` 55 } 56 57 type SystemAction struct { 58 // Title is a user presentable action description 59 Title string `json:"title,omitempty"` 60 // Mode given action can be executed in 61 Mode string `json:"mode,omitempty"` 62 } 63 64 // ListSystems list all systems available for seeding or recovery. 65 func (client *Client) ListSystems() ([]System, error) { 66 type systemsResponse struct { 67 Systems []System `json:"systems,omitempty"` 68 } 69 70 var rsp systemsResponse 71 72 if _, err := client.doSync("GET", "/v2/systems", nil, nil, nil, &rsp); err != nil { 73 return nil, xerrors.Errorf("cannot list recovery systems: %v", err) 74 } 75 return rsp.Systems, nil 76 } 77 78 // DoSystemAction issues a request to perform an action using the given seed 79 // system and its mode. 80 func (client *Client) DoSystemAction(systemLabel string, action *SystemAction) error { 81 if systemLabel == "" { 82 return fmt.Errorf("cannot request an action without the system") 83 } 84 if action == nil { 85 return fmt.Errorf("cannot request an action without one") 86 } 87 // deeper verification is done by the backend 88 89 req := struct { 90 Action string `json:"action"` 91 *SystemAction 92 }{ 93 Action: "do", 94 SystemAction: action, 95 } 96 97 var body bytes.Buffer 98 if err := json.NewEncoder(&body).Encode(&req); err != nil { 99 return err 100 } 101 if _, err := client.doSync("POST", "/v2/systems/"+systemLabel, nil, nil, &body, nil); err != nil { 102 return xerrors.Errorf("cannot request system action: %v", err) 103 } 104 return nil 105 } 106 107 // RebootToSystem issues a request to reboot into system with the 108 // given label and the given mode. 109 // 110 // When called without a systemLabel and without a mode it will just 111 // trigger a regular reboot. 112 // 113 // When called without a systemLabel but with a mode it will use 114 // the current system to enter the given mode. 115 // 116 // Note that "recover" and "run" modes are only available for the 117 // current system. 118 func (client *Client) RebootToSystem(systemLabel, mode string) error { 119 // verification is done by the backend 120 121 req := struct { 122 Action string `json:"action"` 123 Mode string `json:"mode"` 124 }{ 125 Action: "reboot", 126 Mode: mode, 127 } 128 129 var body bytes.Buffer 130 if err := json.NewEncoder(&body).Encode(&req); err != nil { 131 return err 132 } 133 if _, err := client.doSync("POST", "/v2/systems/"+systemLabel, nil, nil, &body, nil); err != nil { 134 if systemLabel != "" { 135 return xerrors.Errorf("cannot request system reboot into %q: %v", systemLabel, err) 136 } 137 return xerrors.Errorf("cannot request system reboot: %v", err) 138 } 139 return nil 140 }