-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnode.go
More file actions
174 lines (146 loc) · 5.54 KB
/
node.go
File metadata and controls
174 lines (146 loc) · 5.54 KB
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
package autonomisdk
import (
"bytes"
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"github.com/intercloud/autonomi-sdk/models"
)
func checkNodeFinishedTask(ctx context.Context, c *Client, workspaceID, nodeID string, waiterOptionState models.AdministrativeState) (*models.Node, bool) {
node, err := c.GetNode(ctx, workspaceID, nodeID)
if err != nil {
// if wanted state is deleted and the attachment is in this state, api has returned 404
if waiterOptionState == models.AdministrativeStateDeleted {
if strings.Contains(err.Error(), "status: 404") {
return nil, true
}
}
log.Printf("an error occurs when getting node, err: %s" + err.Error())
return nil, false
}
return node, waiterOptionState == node.State
}
// CreateNode creates asynchronously a cloud node. The node returned will depend of the passed option.
// If none is passed the node will be returned once created in database with administrative state creation_pending.
// If the option WithWaitUntilElementDeployed() is passed, the node will be returned when its state reach deployed or creation_error.
// If the option WithWaitUntilElementUndeployed() is passed, it will not be considered hence the node returned will be in state creation_pending
func (c *Client) CreateNode(ctx context.Context, payload models.CreateNode, workspaceID string, options ...OptionElement) (*models.Node, error) {
body := new(bytes.Buffer)
err := json.NewEncoder(body).Encode(&payload)
if err != nil {
return nil, err
}
if errV := c.validate.StructCtx(ctx, payload); errV != nil {
return nil, errV
}
cloudOptions := &elementOptions{}
for _, o := range options {
o(cloudOptions)
}
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/accounts/%s/workspaces/%s/nodes", c.hostURL, c.accountID, workspaceID), body)
if err != nil {
return nil, err
}
resp, err := c.doRequest(req)
if err != nil {
return nil, err
}
node := models.NodeResponse{}
err = json.Unmarshal(resp, &node)
if err != nil {
return nil, err
}
var nodePolled = &node.Data
if cloudOptions.waitUntilElementDeployed {
var success bool
nodePolled, success = WaitUntilFinishedTask(ctx, c, workspaceID, node.Data.ID.String(), models.AdministrativeStateDeployed, checkNodeFinishedTask)
if !success {
// if node creation operation failed then we try to delete it
if nodePolled != nil {
if _, err := c.DeleteNode(ctx, workspaceID, nodePolled.ID.String()); err != nil {
return nil, fmt.Errorf("Node did not reach '%s' state in time and cannot be reverted. node_id is '%s'", models.AdministrativeStateDeployed, nodePolled.ID)
}
}
return nil, fmt.Errorf("Node did not reach '%s' state in time.", models.AdministrativeStateDeployed)
}
}
return nodePolled, nil
}
func (c *Client) GetNode(ctx context.Context, workspaceID, nodeID string) (*models.Node, error) {
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/accounts/%s/workspaces/%s/nodes/%s", c.hostURL, c.accountID, workspaceID, nodeID), nil)
if err != nil {
return nil, err
}
resp, err := c.doRequest(req)
if err != nil {
return nil, err
}
node := models.NodeResponse{}
err = json.Unmarshal(resp, &node)
if err != nil {
return nil, err
}
return &node.Data, err
}
func (c *Client) UpdateNode(ctx context.Context, payload models.UpdateElement, workspaceID, nodeID string) (*models.Node, error) {
body := new(bytes.Buffer)
err := json.NewEncoder(body).Encode(&payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest(http.MethodPatch, fmt.Sprintf("%s/accounts/%s/workspaces/%s/nodes/%s", c.hostURL, c.accountID, workspaceID, nodeID), body)
if err != nil {
return nil, err
}
resp, err := c.doRequest(req)
if err != nil {
return nil, err
}
node := models.NodeResponse{}
err = json.Unmarshal(resp, &node)
if err != nil {
return nil, err
}
return &node.Data, err
}
// DeleteNode deletes asynchronously a node. The attachment returned will depend of the option passed.
// If none is passed the node will be returned once the request accepted, its state will be delete_pending
// If the option WithWaitUntilElementUndeployed() is passed, the node won't be returned as it would have been deleted. However, if an error is triggered, an object could be returned with a delete_error state.
// If the option WithWaitUntilElementDeployed() is passed, it will not be considered hence the node returned will be in state delete_pending
func (c *Client) DeleteNode(ctx context.Context, workspaceID, nodeID string, options ...OptionElement) (*models.Node, error) {
cloudOptions := &elementOptions{}
for _, o := range options {
o(cloudOptions)
}
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/accounts/%s/workspaces/%s/nodes/%s", c.hostURL, c.accountID, workspaceID, nodeID), nil)
if err != nil {
return nil, err
}
resp, err := c.doRequest(req)
if err != nil {
return nil, err
}
node := models.NodeResponse{}
err = json.Unmarshal(resp, &node)
if err != nil {
return nil, err
}
var nodePolled = &node.Data
if cloudOptions.waitUntilElementUndeployed {
var success bool
nodePolled, success = WaitUntilFinishedTask(ctx, c, workspaceID, node.Data.ID.String(), models.AdministrativeStateDeleted, checkNodeFinishedTask)
if !success {
return nil, fmt.Errorf("Node did not reach '%s' state in time.", models.AdministrativeStateDeleted)
}
}
// If the node was deleted and we were waiting for the "deleted" state,
// nodePolled will be nil. To prevent a panic when dereferencing, we
// assign an empty structure.
if nodePolled == nil {
nodePolled = &models.Node{}
}
return nodePolled, nil
}