-
Notifications
You must be signed in to change notification settings - Fork 45
/
action.go
192 lines (164 loc) · 4.16 KB
/
action.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package hcloud
import (
"context"
"fmt"
"time"
"github.com/hetznercloud/hcloud-go/hcloud/schema"
)
// Action represents an action in the Hetzner Cloud.
type Action struct {
ID int
Status ActionStatus
Command string
Progress int
Started time.Time
Finished time.Time
ErrorCode string
ErrorMessage string
Resources []*ActionResource
}
// ActionStatus represents an action's status.
type ActionStatus string
// List of action statuses.
const (
ActionStatusRunning ActionStatus = "running"
ActionStatusSuccess = "success"
ActionStatusError = "error"
)
// ActionResource references other resources from an action.
type ActionResource struct {
ID int
Type ActionResourceType
}
// ActionResourceType represents an action's resource reference type.
type ActionResourceType string
// List of action resource reference types.
const (
ActionResourceTypeServer ActionResourceType = "server"
ActionResourceTypeImage = "image"
ActionResourceTypeISO = "iso"
ActionResourceTypeFloatingIP = "floating_ip"
)
// ActionError is the error of an action.
type ActionError struct {
Code string
Message string
}
func (e ActionError) Error() string {
return fmt.Sprintf("%s (%s)", e.Message, e.Code)
}
func (a *Action) Error() error {
if a.ErrorCode != "" && a.ErrorMessage != "" {
return ActionError{
Code: a.ErrorCode,
Message: a.ErrorMessage,
}
}
return nil
}
// ActionClient is a client for the actions API.
type ActionClient struct {
client *Client
}
// GetByID retrieves an action by its ID.
func (c *ActionClient) GetByID(ctx context.Context, id int) (*Action, *Response, error) {
req, err := c.client.NewRequest(ctx, "GET", fmt.Sprintf("/actions/%d", id), nil)
if err != nil {
return nil, nil, err
}
var body schema.ActionGetResponse
resp, err := c.client.Do(req, &body)
if err != nil {
if IsError(err, ErrorCodeNotFound) {
return nil, resp, nil
}
return nil, nil, err
}
return ActionFromSchema(body.Action), resp, nil
}
// ActionListOpts specifies options for listing actions.
type ActionListOpts struct {
ListOpts
}
// List returns a list of actions for a specific page.
func (c *ActionClient) List(ctx context.Context, opts ActionListOpts) ([]*Action, *Response, error) {
path := "/actions?" + valuesForListOpts(opts.ListOpts).Encode()
req, err := c.client.NewRequest(ctx, "GET", path, nil)
if err != nil {
return nil, nil, err
}
var body schema.ActionListResponse
resp, err := c.client.Do(req, &body)
if err != nil {
return nil, nil, err
}
actions := make([]*Action, 0, len(body.Actions))
for _, i := range body.Actions {
actions = append(actions, ActionFromSchema(i))
}
return actions, resp, nil
}
// All returns all actions.
func (c *ActionClient) All(ctx context.Context) ([]*Action, error) {
allActions := []*Action{}
opts := ActionListOpts{}
opts.PerPage = 50
_, err := c.client.all(func(page int) (*Response, error) {
opts.Page = page
actions, resp, err := c.List(ctx, opts)
if err != nil {
return resp, err
}
allActions = append(allActions, actions...)
return resp, nil
})
if err != nil {
return nil, err
}
return allActions, nil
}
// WatchProgress watches the actions progress until it completes with success or error.
func (c *ActionClient) WatchProgress(ctx context.Context, action *Action) (<-chan int, <-chan error) {
errCh := make(chan error, 1)
progressCh := make(chan int)
go func() {
defer close(errCh)
defer close(progressCh)
ticker := time.NewTicker(500 * time.Millisecond)
sendProgress := func(p int) {
select {
case progressCh <- p:
break
default:
break
}
}
for {
select {
case <-ctx.Done():
errCh <- ctx.Err()
return
case <-ticker.C:
break
}
a, _, err := c.GetByID(ctx, action.ID)
if err != nil {
errCh <- ctx.Err()
return
}
switch a.Status {
case ActionStatusRunning:
sendProgress(a.Progress)
break
case ActionStatusSuccess:
sendProgress(100)
errCh <- nil
return
case ActionStatusError:
errCh <- a.Error()
return
}
}
}()
return progressCh, errCh
}