Skip to content

Commit 9153323

Browse files
committed
Separated api/command functions
1 parent c2a323c commit 9153323

File tree

13 files changed

+403
-388
lines changed

13 files changed

+403
-388
lines changed

api.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/url"
99
"os"
1010
"strings"
11+
"text/template"
1112

1213
log "github.com/Sirupsen/logrus"
1314
)
@@ -438,6 +439,13 @@ type ScalewayImageDefinition struct {
438439
Arch string `json:"arch"`
439440
}
440441

442+
var funcMap = template.FuncMap{
443+
"json": func(v interface{}) string {
444+
a, _ := json.Marshal(v)
445+
return string(a)
446+
},
447+
}
448+
441449
// NewScalewayAPI creates a ready-to-use ScalewayAPI client
442450
func NewScalewayAPI(endpoint, organization, token string) (*ScalewayAPI, error) {
443451
cache, err := NewScalewayCache()

api_helpers.go

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"sync"
7+
"time"
8+
9+
log "github.com/Sirupsen/logrus"
10+
"github.com/docker/docker/pkg/namesgenerator"
11+
"github.com/dustin/go-humanize"
12+
)
13+
14+
// CreateVolumeFromHumanSize creates a volume on the API with a human readable size
15+
func CreateVolumeFromHumanSize(api *ScalewayAPI, size string) (*string, error) {
16+
bytes, err := humanize.ParseBytes(size)
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
var newVolume ScalewayVolumeDefinition
22+
newVolume.Name = size
23+
newVolume.Size = bytes
24+
newVolume.Type = "l_ssd"
25+
26+
volumeID, err := api.PostVolume(newVolume)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
return &volumeID, nil
32+
}
33+
34+
// ScalewayResolvedIdentifier represents a list of matching identifier for a specifier pattern
35+
type ScalewayResolvedIdentifier struct {
36+
// Identifiers holds matching identifiers
37+
Identifiers []ScalewayIdentifier
38+
39+
// Needle is the criteria used to lookup identifiers
40+
Needle string
41+
}
42+
43+
// resolveIdentifiers resolves needles provided by the user
44+
func resolveIdentifiers(api *ScalewayAPI, needles []string, out chan ScalewayResolvedIdentifier) {
45+
// first attempt, only lookup from the cache
46+
var unresolved []string
47+
for _, needle := range needles {
48+
idents := api.Cache.LookUpIdentifiers(needle)
49+
if len(idents) == 0 {
50+
unresolved = append(unresolved, needle)
51+
} else {
52+
out <- ScalewayResolvedIdentifier{
53+
Identifiers: idents,
54+
Needle: needle,
55+
}
56+
}
57+
}
58+
// fill the cache by fetching from the API and resolve missing identifiers
59+
if len(unresolved) > 0 {
60+
var wg sync.WaitGroup
61+
wg.Add(5)
62+
go func() {
63+
api.GetServers(true, 0)
64+
wg.Done()
65+
}()
66+
go func() {
67+
api.GetImages()
68+
wg.Done()
69+
}()
70+
go func() {
71+
api.GetSnapshots()
72+
wg.Done()
73+
}()
74+
go func() {
75+
api.GetVolumes()
76+
wg.Done()
77+
}()
78+
go func() {
79+
api.GetBootscripts()
80+
wg.Done()
81+
}()
82+
wg.Wait()
83+
for _, needle := range unresolved {
84+
idents := api.Cache.LookUpIdentifiers(needle)
85+
out <- ScalewayResolvedIdentifier{
86+
Identifiers: idents,
87+
Needle: needle,
88+
}
89+
}
90+
}
91+
close(out)
92+
}
93+
94+
// inspectIdentifiers inspects identifiers concurrently
95+
func inspectIdentifiers(api *ScalewayAPI, ci chan ScalewayResolvedIdentifier, cj chan interface{}) {
96+
var wg sync.WaitGroup
97+
for {
98+
idents, ok := <-ci
99+
if !ok {
100+
break
101+
}
102+
if len(idents.Identifiers) != 1 {
103+
if len(idents.Identifiers) == 0 {
104+
log.Errorf("Unable to resolve identifier %s", idents.Needle)
105+
} else {
106+
log.Errorf("Too many candidates for %s (%d)", idents.Needle, len(idents.Identifiers))
107+
for _, identifier := range idents.Identifiers {
108+
// FIXME: also print the name
109+
log.Infof("- %s", identifier.Identifier)
110+
}
111+
}
112+
} else {
113+
ident := idents.Identifiers[0]
114+
wg.Add(1)
115+
go func() {
116+
if ident.Type == IdentifierServer {
117+
server, err := api.GetServer(ident.Identifier)
118+
if err == nil {
119+
cj <- server
120+
}
121+
} else if ident.Type == IdentifierImage {
122+
image, err := api.GetImage(ident.Identifier)
123+
if err == nil {
124+
cj <- image
125+
}
126+
} else if ident.Type == IdentifierSnapshot {
127+
snap, err := api.GetSnapshot(ident.Identifier)
128+
if err == nil {
129+
cj <- snap
130+
}
131+
} else if ident.Type == IdentifierVolume {
132+
snap, err := api.GetVolume(ident.Identifier)
133+
if err == nil {
134+
cj <- snap
135+
}
136+
} else if ident.Type == IdentifierBootscript {
137+
bootscript, err := api.GetBootscript(ident.Identifier)
138+
if err == nil {
139+
cj <- bootscript
140+
}
141+
}
142+
wg.Done()
143+
}()
144+
}
145+
}
146+
wg.Wait()
147+
close(cj)
148+
}
149+
150+
func createServer(api *ScalewayAPI, imageName string, name string, bootscript string, env string, additionalVolumes string) (string, error) {
151+
if name == "" {
152+
name = strings.Replace(namesgenerator.GetRandomName(0), "_", "-", -1)
153+
}
154+
155+
var server ScalewayServerDefinition
156+
server.Volumes = make(map[string]string)
157+
158+
server.Tags = []string{}
159+
if env != "" {
160+
server.Tags = strings.Split(env, " ")
161+
}
162+
if additionalVolumes != "" {
163+
volumes := strings.Split(additionalVolumes, " ")
164+
for i := range volumes {
165+
volumeID, err := CreateVolumeFromHumanSize(api, volumes[i])
166+
if err != nil {
167+
return "", err
168+
}
169+
170+
volumeIDx := fmt.Sprintf("%d", i+1)
171+
server.Volumes[volumeIDx] = *volumeID
172+
}
173+
}
174+
server.Name = name
175+
if bootscript != "" {
176+
bootscript := api.GetBootscriptID(bootscript)
177+
server.Bootscript = &bootscript
178+
}
179+
180+
_, err := humanize.ParseBytes(imageName)
181+
if err == nil {
182+
// Create a new root volume
183+
volumeID, err := CreateVolumeFromHumanSize(api, imageName)
184+
if err != nil {
185+
return "", err
186+
}
187+
server.Volumes["0"] = *volumeID
188+
} else {
189+
// Use an existing image
190+
// FIXME: handle snapshots
191+
image := api.GetImageID(imageName)
192+
server.Image = &image
193+
}
194+
195+
serverID, err := api.PostServer(server)
196+
if err != nil {
197+
return "", nil
198+
}
199+
200+
return serverID, nil
201+
}
202+
203+
// WaitForServerState asks API in a loop until a server matches a wanted state
204+
func WaitForServerState(api *ScalewayAPI, serverID string, targetState string) (*ScalewayServer, error) {
205+
var server *ScalewayServer
206+
var err error
207+
208+
for {
209+
server, err = api.GetServer(serverID)
210+
if err != nil {
211+
return nil, err
212+
}
213+
if server.State == targetState {
214+
break
215+
}
216+
time.Sleep(1 * time.Second)
217+
}
218+
219+
return server, nil
220+
}
221+
222+
// WaitForServerReady wait for a server state to be running, then wait for the SSH port to be available
223+
func WaitForServerReady(api *ScalewayAPI, serverID string) (*ScalewayServer, error) {
224+
server, err := WaitForServerState(api, serverID, "running")
225+
if err != nil {
226+
return nil, err
227+
}
228+
229+
dest := fmt.Sprintf("%s:22", server.PublicAddress.IP)
230+
231+
err = WaitForTCPPortOpen(dest)
232+
if err != nil {
233+
return nil, err
234+
}
235+
236+
return server, nil
237+
}
238+
239+
// ScalewayImageInterface is an interface to multiple Scaleway items
240+
type ScalewayImageInterface struct {
241+
CreationDate time.Time
242+
Identifier string
243+
Name string
244+
Tag string
245+
VirtualSize float64
246+
Public bool
247+
Type string
248+
}
249+
250+
// ByCreationDate sorts images by CreationDate field
251+
type ByCreationDate []ScalewayImageInterface
252+
253+
func (a ByCreationDate) Len() int { return len(a) }
254+
func (a ByCreationDate) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
255+
func (a ByCreationDate) Less(i, j int) bool { return a[j].CreationDate.Before(a[i].CreationDate) }
256+
257+
func startServer(api *ScalewayAPI, needle string, wait bool) error {
258+
server := api.GetServerID(needle)
259+
260+
err := api.PostServerAction(server, "poweron")
261+
if err != nil {
262+
if err.Error() != "server should be stopped" {
263+
return fmt.Errorf("Server %s is already started: %v", server, err)
264+
}
265+
}
266+
267+
if wait {
268+
_, err = WaitForServerReady(api, server)
269+
if err != nil {
270+
return fmt.Errorf("Failed to wait for server %s to be ready, %v", needle, err)
271+
}
272+
}
273+
return nil
274+
}
275+
276+
func startServerOnce(api *ScalewayAPI, needle string, wait bool, successChan chan bool, errChan chan error) {
277+
err := startServer(api, needle, wait)
278+
279+
if err != nil {
280+
errChan <- err
281+
return
282+
}
283+
284+
fmt.Println(needle)
285+
successChan <- true
286+
}

cache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ func RemoveDuplicates(elements []string) []string {
333333

334334
// Place all keys from the map into a slice.
335335
result := []string{}
336-
for key, _ := range encountered {
336+
for key := range encountered {
337337
result = append(result, key)
338338
}
339339
return result

0 commit comments

Comments
 (0)