Skip to content

Commit

Permalink
feat(instance): import file for cloud-init
Browse files Browse the repository at this point in the history
  • Loading branch information
jtherin committed Nov 13, 2020
1 parent 3e0a8fb commit 53c1749
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ EXAMPLES:
Create an instance with 2 local volumes (10GB and 10GB)
scw instance server create image=ubuntu_focal root-volume=local:10GB additional-volumes.0=local:10GB

Create an instance using cloud-init config file
scw instance server create image=ubuntu_focal [email protected]

Use an existing IP
ip=$(scw instance ip create | grep id | awk '{ print $2 }')
scw instance server create image=ubuntu_focal ip=$ip
Expand All @@ -35,7 +38,7 @@ ARGS:
[security-group-id] The security group ID it use for this server
[placement-group-id] The placement group ID in witch the server has to be created
[bootscript-id] The bootscript ID to use, if empty the local boot will be used
[cloud-init] The cloud-init script to use
[cloud-init] The cloud-init script to use. Use @filename to import a file
[boot-type=local] The boot type to use, if empty the local boot will be used. Will be overwritten to bootscript if bootscript-id is set. (local | bootscript | rescue)
[project-id] Project ID to use. If none is passed the default project ID will be used
[zone=fr-par-1] Zone to target. If none is passed will use default zone from the config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ EXAMPLES:
Put a given server in the given placement group. Server must be off
scw instance server update

Update cloud-init script on a given server
scw instance server update 11111111-1111-1111-1111-111111111111 cloud-init=@filename

ARGS:
server-id UUID of the server
[name] Name of the server
[ip] IP that should be attached to the server (use ip=none to detach)
[cloud-init] The cloud-init script to use
[cloud-init] The cloud-init script to use. Use @filename to import a file
[boot-type] (local | bootscript | rescue)
[tags.{index}] Tags of the server
[bootscript]
Expand Down
21 changes: 21 additions & 0 deletions internal/args/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package args

import (
"fmt"
"io/ioutil"
"strings"
)

// ValueOrFileContent returns file content or value
func ValueOrFileContent(value string) (string, error) {
if !strings.HasPrefix(value, "@") {
return value, nil
}

content, err := ioutil.ReadFile(value[1:])
if err != nil {
return "", fmt.Errorf("could not open requested file: %s", err)
}

return string(content), nil
}
62 changes: 62 additions & 0 deletions internal/args/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package args

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

const (
testFileContent = "test-content"
testFileName = "local-test-file.txt"
)

func TestValueOrFileContent(t *testing.T) {
type TestCase struct {
arg string
error bool
expected string
}

run := func(tc TestCase) func(t *testing.T) {
return func(t *testing.T) {
reader, err := ValueOrFileContent(tc.arg)
assert.Equal(t, tc.error, err != nil)
assert.Equal(t, tc.expected, reader)
}
}

t.Run("empty", run(TestCase{
arg: "",
expected: "",
}))

t.Run("plain-value", run(TestCase{
arg: "value",
expected: "value",
}))

t.Run("@missing-file", run(TestCase{
arg: "@missing-file",
expected: "",
error: true,
}))

// Write a test file to read content
f, err := os.Create(testFileName)
assert.NoError(t, err)
_, err = f.WriteString(testFileContent)
assert.NoError(t, err)
err = f.Close()
assert.NoError(t, err)

t.Run("@"+testFileName, run(TestCase{
arg: "@" + testFileName,
expected: testFileContent,
}))

// Remove tested file
err = os.Remove(testFileName)
assert.NoError(t, err)
}
12 changes: 9 additions & 3 deletions internal/namespaces/instance/v1/custom_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/fatih/color"
"github.com/scaleway/scaleway-cli/internal/args"
"github.com/scaleway/scaleway-cli/internal/core"
"github.com/scaleway/scaleway-cli/internal/human"
"github.com/scaleway/scaleway-cli/internal/interactive"
Expand Down Expand Up @@ -202,7 +203,7 @@ func serverUpdateBuilder(c *core.Command) *core.Command {
})
c.ArgSpecs.AddBefore("boot-type", &core.ArgSpec{
Name: "cloud-init",
Short: "The cloud-init script to use",
Short: "The cloud-init script to use. Use @filename to import a file",
})

c.Run = func(ctx context.Context, argsI interface{}) (i interface{}, e error) {
Expand Down Expand Up @@ -292,11 +293,16 @@ func serverUpdateBuilder(c *core.Command) *core.Command {

// Set cloud-init
if customRequest.CloudInit != "" {
err := api.SetServerUserData(&instance.SetServerUserDataRequest{
cloudInitData, err := args.ValueOrFileContent(customRequest.CloudInit)
if err != nil {
return nil, err
}

err = api.SetServerUserData(&instance.SetServerUserDataRequest{
Zone: updateServerRequest.Zone,
ServerID: customRequest.ServerID,
Key: "cloud-init",
Content: bytes.NewBufferString(customRequest.CloudInit),
Content: bytes.NewBufferString(cloudInitData),
})
if err != nil {
return nil, err
Expand Down
20 changes: 16 additions & 4 deletions internal/namespaces/instance/v1/custom_server_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import (
"strings"

"github.com/dustin/go-humanize"
"github.com/scaleway/scaleway-cli/internal/core"
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
"github.com/scaleway/scaleway-sdk-go/api/marketplace/v1"
"github.com/scaleway/scaleway-sdk-go/logger"
"github.com/scaleway/scaleway-sdk-go/scw"
"github.com/scaleway/scaleway-sdk-go/validation"

intargs "github.com/scaleway/scaleway-cli/internal/args"
"github.com/scaleway/scaleway-cli/internal/core"
)

type instanceCreateServerRequest struct {
Expand Down Expand Up @@ -121,7 +123,7 @@ func serverCreateCommand() *core.Command {
},
{
Name: "cloud-init",
Short: "The cloud-init script to use",
Short: "The cloud-init script to use. Use @filename to import a file",
},
{
Name: "boot-type",
Expand Down Expand Up @@ -156,6 +158,10 @@ func serverCreateCommand() *core.Command {
Short: "Create an instance with 2 local volumes (10GB and 10GB)",
ArgsJSON: `{"image":"ubuntu_focal","root_volume":"local:10GB","additional_volumes":["local:10GB"]}`,
},
{
Short: "Create an instance using cloud-init config file",
ArgsJSON: `{"image":"ubuntu_focal","cloud_init":"@config.txt"}`,
},
{
Short: "Use an existing IP",
Raw: `ip=$(scw instance ip create | grep id | awk '{ print $2 }')
Expand Down Expand Up @@ -185,6 +191,12 @@ func instanceServerCreateRun(ctx context.Context, argsI interface{}) (i interfac

needIPCreation := false

// import file content if needed
cloudInitData, err := intargs.ValueOrFileContent(args.CloudInit)
if err != nil {
return nil, err
}

serverReq := &instance.CreateServerRequest{
Zone: args.Zone,
Organization: args.OrganizationID,
Expand Down Expand Up @@ -396,12 +408,12 @@ func instanceServerCreateRun(ctx context.Context, argsI interface{}) (i interfac
//
// Cloud-init
//
if args.CloudInit != "" {
if cloudInitData != "" {
err := apiInstance.SetServerUserData(&instance.SetServerUserDataRequest{
Zone: args.Zone,
ServerID: server.ID,
Key: "cloud-init",
Content: bytes.NewBufferString(args.CloudInit),
Content: bytes.NewBufferString(cloudInitData),
})
if err != nil {
logger.Warningf("error while setting up your cloud-init metadata: %s. Note that the server is successfully created.", err)
Expand Down
4 changes: 4 additions & 0 deletions internal/namespaces/instance/v1/instance_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,10 @@ func instanceServerUpdate() *core.Command {
Short: "Put a given server in the given placement group. Server must be off",
ArgsJSON: `null`,
},
{
Short: "Update cloud-init script on a given server",
ArgsJSON: `{"cloud_init":"@filename","server_id":"11111111-1111-1111-1111-111111111111"}`,
},
},
}
}
Expand Down

0 comments on commit 53c1749

Please sign in to comment.