github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/change.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 "net/url" 27 "time" 28 ) 29 30 // A Change is a modification to the system state. 31 type Change struct { 32 ID string `json:"id"` 33 Kind string `json:"kind"` 34 Summary string `json:"summary"` 35 Status string `json:"status"` 36 Tasks []*Task `json:"tasks,omitempty"` 37 Ready bool `json:"ready"` 38 Err string `json:"err,omitempty"` 39 40 SpawnTime time.Time `json:"spawn-time,omitempty"` 41 ReadyTime time.Time `json:"ready-time,omitempty"` 42 43 data map[string]*json.RawMessage 44 } 45 46 var ErrNoData = fmt.Errorf("data entry not found") 47 48 // Get unmarshals into value the kind-specific data with the provided key. 49 func (c *Change) Get(key string, value interface{}) error { 50 raw := c.data[key] 51 if raw == nil { 52 return ErrNoData 53 } 54 return json.Unmarshal([]byte(*raw), value) 55 } 56 57 // A Task is an operation done to change the system's state. 58 type Task struct { 59 ID string `json:"id"` 60 Kind string `json:"kind"` 61 Summary string `json:"summary"` 62 Status string `json:"status"` 63 Log []string `json:"log,omitempty"` 64 Progress TaskProgress `json:"progress"` 65 66 SpawnTime time.Time `json:"spawn-time,omitempty"` 67 ReadyTime time.Time `json:"ready-time,omitempty"` 68 } 69 70 type TaskProgress struct { 71 Label string `json:"label"` 72 Done int `json:"done"` 73 Total int `json:"total"` 74 } 75 76 type changeAndData struct { 77 Change 78 Data map[string]*json.RawMessage `json:"data"` 79 } 80 81 // Change fetches information about a Change given its ID. 82 func (client *Client) Change(id string) (*Change, error) { 83 var chgd changeAndData 84 _, err := client.doSync("GET", "/v2/changes/"+id, nil, nil, nil, &chgd) 85 if err != nil { 86 return nil, err 87 } 88 89 chgd.Change.data = chgd.Data 90 return &chgd.Change, nil 91 } 92 93 // Abort attempts to abort a change that is in not yet ready. 94 func (client *Client) Abort(id string) (*Change, error) { 95 var postData struct { 96 Action string `json:"action"` 97 } 98 postData.Action = "abort" 99 100 var body bytes.Buffer 101 if err := json.NewEncoder(&body).Encode(postData); err != nil { 102 return nil, err 103 } 104 105 var chg Change 106 if _, err := client.doSync("POST", "/v2/changes/"+id, nil, nil, &body, &chg); err != nil { 107 return nil, err 108 } 109 110 return &chg, nil 111 } 112 113 type ChangeSelector uint8 114 115 func (c ChangeSelector) String() string { 116 switch c { 117 case ChangesInProgress: 118 return "in-progress" 119 case ChangesReady: 120 return "ready" 121 case ChangesAll: 122 return "all" 123 } 124 125 panic(fmt.Sprintf("unknown ChangeSelector %d", c)) 126 } 127 128 const ( 129 ChangesInProgress ChangeSelector = 1 << iota 130 ChangesReady 131 ChangesAll = ChangesReady | ChangesInProgress 132 ) 133 134 type ChangesOptions struct { 135 SnapName string // if empty, no filtering by name is done 136 Selector ChangeSelector 137 } 138 139 func (client *Client) Changes(opts *ChangesOptions) ([]*Change, error) { 140 query := url.Values{} 141 if opts != nil { 142 if opts.Selector != 0 { 143 query.Set("select", opts.Selector.String()) 144 } 145 if opts.SnapName != "" { 146 query.Set("for", opts.SnapName) 147 } 148 } 149 150 var chgds []changeAndData 151 _, err := client.doSync("GET", "/v2/changes", query, nil, nil, &chgds) 152 if err != nil { 153 return nil, err 154 } 155 156 var chgs []*Change 157 for i := range chgds { 158 chgd := &chgds[i] 159 chgd.Change.data = chgd.Data 160 chgs = append(chgs, &chgd.Change) 161 } 162 163 return chgs, err 164 }