Skip to content

Commit 936760e

Browse files
committed
Merge branch 'stackscript' into stackscript-merge
2 parents c86a759 + 8757f14 commit 936760e

File tree

2 files changed

+122
-9
lines changed

2 files changed

+122
-9
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ Successfully removed linode
6767
| `linode-image` | `LINODE_IMAGE` | `linode/ubuntu18.04` | The Linode Instance `image` which provides the Linux distribution (see [here](https://api.linode.com/v4/images)).
6868
| `linode-kernel` | `LINODE_KERNEL` | `linode/grub2` | The Linux Instance `kernel` to boot. `linode/grub2` will defer to the distribution kernel. (see [here](https://api.linode.com/v4/linode/kernels) (`?page=N`))
6969
| `linode-ssh-port` | `LINODE_SSH_PORT` | `22` | The port that SSH is running on, needed for Docker Machine to provision the Linode.
70+
| `linode-ssh-user` | `LINODE_SSH_USER` | `root` | The user as which docker-machine should log in to the Linode instance to install Docker. This user must have passwordless sudo.
7071
| `linode-docker-port` | `LINODE_DOCKER_PORT` | `2376` | The TCP port of the Linode that Docker will be listening on
7172
| `linode-swap-size` | `LINODE_SWAP_SIZE` | `512` | The amount of swap space provisioned on the Linode Instance
72-
73+
| `linode-stackscript` | `LINODE_STACKSCRIPT` | None | Specifies the Linode StackScript to use to create the instance, either by numeric ID, or using the form *username*/*label*.
74+
| `linode-stackscript-data` | `LINODE_STACKSCRIPT_DATA` | None | A JSON string specifying data that is passed (via UDF) to the selected StackScript.
7375

7476
## Discussion / Help
7577

pkg/drivers/linode/linode.go

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"net"
99
"net/http"
1010
"strings"
11+
"strconv"
12+
"encoding/json"
1113

1214
"github.com/docker/machine/libmachine/drivers"
1315
"github.com/docker/machine/libmachine/log"
@@ -37,6 +39,11 @@ type Driver struct {
3739
InstanceImage string
3840
InstanceKernel string
3941
SwapSize int
42+
43+
StackScriptID int
44+
StackScriptUser string
45+
StackScriptLabel string
46+
StackScriptData map[string]string
4047
}
4148

4249
const (
@@ -50,6 +57,8 @@ const (
5057
defaultInstanceKernel = "linode/grub2"
5158
defaultSwapSize = 512
5259
defaultDockerPort = 2376
60+
61+
defaultContainerLinuxSSHUser = "core"
5362
)
5463

5564
// NewDriver
@@ -124,7 +133,7 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
124133
EnvVar: "LINODE_LABEL",
125134
Name: "linode-label",
126135
Usage: "Linode Instance Label",
127-
},
136+
},
128137
mcnflag.StringFlag{
129138
EnvVar: "LINODE_REGION",
130139
Name: "linode-region",
@@ -143,6 +152,12 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
143152
Usage: "Linode Instance SSH Port",
144153
Value: defaultSSHPort,
145154
},
155+
mcnflag.StringFlag{
156+
EnvVar: "LINODE_SSH_USER",
157+
Name: "linode-ssh-user",
158+
Usage: "Specifies the user as which docker-machine should log in to the Linode instance to install Docker.",
159+
Value: "",
160+
},
146161
mcnflag.StringFlag{
147162
EnvVar: "LINODE_IMAGE",
148163
Name: "linode-image",
@@ -167,13 +182,38 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
167182
Usage: "Linode Instance Swap Size (MB)",
168183
Value: defaultSwapSize,
169184
},
185+
mcnflag.StringFlag{
186+
EnvVar: "LINODE_STACKSCRIPT",
187+
Name: "linode-stackscript",
188+
Usage: "Specifies the Linode StackScript to use to create the instance",
189+
Value: "",
190+
},
191+
mcnflag.StringFlag{
192+
EnvVar: "LINODE_STACKSCRIPT_DATA",
193+
Name: "linode-stackscript-data",
194+
Usage: "A JSON string specifying data for the selected StackScript",
195+
Value: "",
196+
},
170197
}
171198
}
172199

200+
// GetSSHPort returns port for use with ssh
201+
func (d *Driver) GetSSHPort() (int, error) {
202+
if d.SSHPort == 0 {
203+
d.SSHPort = defaultSSHPort
204+
}
205+
206+
return d.SSHPort, nil
207+
}
208+
173209
// GetSSHUsername returns username for use with ssh
174210
func (d *Driver) GetSSHUsername() string {
175211
if d.SSHUser == "" {
176-
d.SSHUser = defaultSSHUser
212+
if strings.Contains(d.InstanceImage, "linode/containerlinux") {
213+
d.SSHUser = defaultContainerLinuxSSHUser
214+
} else {
215+
d.SSHUser = defaultSSHUser
216+
}
177217
}
178218

179219
return d.SSHUser
@@ -187,12 +227,15 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
187227
d.InstanceType = flags.String("linode-instance-type")
188228
d.RootPassword = flags.String("linode-root-pass")
189229
d.SSHPort = flags.Int("linode-ssh-port")
230+
d.SSHUser = flags.String("linode-ssh-user")
190231
d.InstanceImage = flags.String("linode-image")
191232
d.InstanceKernel = flags.String("linode-kernel")
192233
d.InstanceLabel = flags.String("linode-label")
193234
d.SwapSize = flags.Int("linode-swap-size")
194235
d.DockerPort = flags.Int("linode-docker-port")
195236

237+
log.Infof("Using SSH port %d", d.SSHPort)
238+
196239
d.SetSwarmConfigFromFlags(flags)
197240

198241
if d.APIToken == "" {
@@ -203,21 +246,84 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
203246
return fmt.Errorf("linode driver requires the --linode-root-pass option")
204247
}
205248

249+
stackScript := flags.String("linode-stackscript")
250+
if stackScript != "" {
251+
sid, err := strconv.Atoi(stackScript)
252+
if err == nil {
253+
d.StackScriptID = sid
254+
} else {
255+
ss := strings.SplitN(stackScript, "/", 2)
256+
if len(ss) != 2 {
257+
return fmt.Errorf("linode StackScripts must be specified using username/label syntax, or using their identifier")
258+
}
259+
260+
d.StackScriptUser = ss[0]
261+
d.StackScriptLabel = ss[1]
262+
263+
stackScriptData := flags.String("linode-stackscript-data")
264+
265+
err := json.Unmarshal([]byte(stackScriptData), &d.StackScriptData)
266+
if err != nil {
267+
return fmt.Errorf("linode StackScript data must be valid JSON: %v", err)
268+
}
269+
}
270+
}
271+
206272
if len(d.InstanceLabel) == 0 {
207273
d.InstanceLabel = d.GetMachineName()
208274
}
209-
if strings.Contains(d.InstanceImage, "linode/containerlinux") {
210-
d.SSHUser = "core"
211-
}
212275

213276
return nil
214277
}
215278

216279
func (d *Driver) PreCreateCheck() error {
217280
// TODO linode-stackscript-file should be read and uploaded (private), then used for boot.
218281
// RevNote could be sha256 of file so the file can be referenced instead of reuploaded.
219-
// linode-stackscript would let the user specify an existing id
220-
// linode-stackscript-data would need to be a json input
282+
283+
client := d.getClient()
284+
285+
if d.StackScriptUser != "" {
286+
/* N.B. username isn't on the list of filterable fields, however
287+
adding it doesn't make anything fail, so if it becomes
288+
filterable in future this will become more efficient */
289+
options := map[string]string {
290+
"username": d.StackScriptUser,
291+
"label": d.StackScriptLabel,
292+
}
293+
b, err := json.Marshal(options)
294+
if err != nil {
295+
return err
296+
}
297+
opts := linodego.NewListOptions(0, string(b))
298+
stackscripts, err := client.ListStackscripts(context.TODO(), opts)
299+
if err != nil {
300+
return err
301+
}
302+
var script *linodego.Stackscript = nil
303+
for _, s := range stackscripts {
304+
if s.Username == d.StackScriptUser {
305+
script = &s
306+
break
307+
}
308+
}
309+
if script == nil {
310+
return fmt.Errorf("StackScript not found: %s/%s", d.StackScriptUser, d.StackScriptLabel)
311+
}
312+
313+
d.StackScriptUser = script.Username
314+
d.StackScriptLabel = script.Label
315+
d.StackScriptID = script.ID
316+
} else if (d.StackScriptID != 0) {
317+
script, err := client.GetStackscript(context.TODO(), d.StackScriptID)
318+
319+
if err != nil {
320+
return fmt.Errorf("StackScript %d could not be used: %v", err)
321+
}
322+
323+
d.StackScriptUser = script.Username
324+
d.StackScriptLabel = script.Label
325+
}
326+
221327
return nil
222328
}
223329

@@ -233,7 +339,6 @@ func (d *Driver) Create() error {
233339
client := d.getClient()
234340

235341
// Create a linode
236-
log.Info("Creating linode instance")
237342
createOpts := linodego.InstanceCreateOptions{
238343
Region: d.Region,
239344
Type: d.InstanceType,
@@ -244,6 +349,12 @@ func (d *Driver) Create() error {
244349
SwapSize: &d.SwapSize,
245350
}
246351

352+
if d.StackScriptID != 0 {
353+
createOpts.StackScriptID = d.StackScriptID
354+
createOpts.StackScriptData = d.StackScriptData
355+
log.Infof("Using StackScript %d: %s/%s", d.StackScriptID, d.StackScriptUser, d.StackScriptLabel)
356+
}
357+
247358
linode, err := client.CreateInstance(context.TODO(), createOpts)
248359
if err != nil {
249360
return err

0 commit comments

Comments
 (0)