Skip to content
This repository was archived by the owner on Jan 21, 2020. It is now read-only.

Commit a5967a1

Browse files
author
David Chung
authored
Standardize use of templates (#353)
Signed-off-by: David Chung <[email protected]>
1 parent a144077 commit a5967a1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+6266
-48
lines changed

cmd/cli/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ func main() {
3939
}
4040

4141
cmd.AddCommand(cli.VersionCommand(), cli.InfoCommand(f))
42+
43+
cmd.AddCommand(templateCommand(f))
4244
cmd.AddCommand(pluginCommand(f), instancePluginCommand(f), groupPluginCommand(f), flavorPluginCommand(f))
4345

4446
err := cmd.Execute()

cmd/cli/template.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
log "github.com/Sirupsen/logrus"
7+
"github.com/docker/infrakit/pkg/discovery"
8+
"github.com/docker/infrakit/pkg/template"
9+
"github.com/spf13/cobra"
10+
)
11+
12+
func templateCommand(plugins func() discovery.Plugins) *cobra.Command {
13+
14+
templateURL := ""
15+
cmd := &cobra.Command{
16+
Use: "template",
17+
Short: "Render an infrakit template",
18+
RunE: func(cmd *cobra.Command, args []string) error {
19+
20+
log.Infof("Using %v for reading template\n", templateURL)
21+
engine, err := template.NewTemplate(templateURL, template.Options{
22+
SocketDir: discovery.Dir(),
23+
})
24+
if err != nil {
25+
return err
26+
}
27+
view, err := engine.Render(nil)
28+
if err != nil {
29+
return err
30+
}
31+
32+
fmt.Print(view)
33+
return nil
34+
},
35+
}
36+
cmd.Flags().StringVar(&templateURL, "url", "", "URL for the template")
37+
38+
return cmd
39+
}

pkg/example/flavor/swarm/flavor.go

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
89
log "github.com/Sirupsen/logrus"
910
docker_types "github.com/docker/docker/api/types"
1011
"github.com/docker/docker/api/types/filters"
@@ -13,8 +14,8 @@ import (
1314
"github.com/docker/infrakit/pkg/plugin/group/util"
1415
"github.com/docker/infrakit/pkg/spi/flavor"
1516
"github.com/docker/infrakit/pkg/spi/instance"
17+
"github.com/docker/infrakit/pkg/template"
1618
"golang.org/x/net/context"
17-
"text/template"
1819
)
1920

2021
type nodeType string
@@ -26,12 +27,13 @@ const (
2627
)
2728

2829
// NewSwarmFlavor creates a flavor.Plugin that creates manager and worker nodes connected in a swarm.
29-
func NewSwarmFlavor(dockerClient client.APIClient) flavor.Plugin {
30-
return &swarmFlavor{client: dockerClient}
30+
func NewSwarmFlavor(dockerClient client.APIClient, templ *template.Template) flavor.Plugin {
31+
return &swarmFlavor{client: dockerClient, initScript: templ}
3132
}
3233

3334
type swarmFlavor struct {
34-
client client.APIClient
35+
client client.APIClient
36+
initScript *template.Template
3537
}
3638

3739
type schema struct {
@@ -135,39 +137,20 @@ func (s swarmFlavor) Validate(flavorProperties json.RawMessage, allocation types
135137
const (
136138
// associationTag is a machine tag added to associate machines with Swarm nodes.
137139
associationTag = "swarm-association-id"
138-
139-
// bootScript is used to generate node boot scripts.
140-
bootScript = `#!/bin/sh
141-
set -o errexit
142-
set -o nounset
143-
set -o xtrace
144-
145-
mkdir -p /etc/docker
146-
cat << EOF > /etc/docker/daemon.json
147-
{
148-
"labels": ["swarm-association-id={{.ASSOCIATION_ID}}"]
149-
}
150-
EOF
151-
152-
{{.RESTART_DOCKER}}
153-
154-
docker swarm join {{.MY_IP}} --token {{.JOIN_TOKEN}}
155-
`
156140
)
157141

158-
func generateInitScript(joinIP, joinToken, associationID, restartCommand string) string {
159-
buffer := bytes.Buffer{}
160-
templ := template.Must(template.New("").Parse(bootScript))
142+
func generateInitScript(templ *template.Template, joinIP, joinToken, associationID, restartCommand string) (string, error) {
143+
var buffer bytes.Buffer
161144
err := templ.Execute(&buffer, map[string]string{
162145
"MY_IP": joinIP,
163146
"JOIN_TOKEN": joinToken,
164147
"ASSOCIATION_ID": associationID,
165148
"RESTART_DOCKER": restartCommand,
166149
})
167150
if err != nil {
168-
panic(err)
151+
return "", err
169152
}
170-
return buffer.String()
153+
return buffer.String(), nil
171154
}
172155

173156
// Healthy determines whether an instance is healthy. This is determined by whether it has successfully joined the
@@ -246,7 +229,7 @@ func (s swarmFlavor) Drain(flavorProperties json.RawMessage, inst instance.Descr
246229
}
247230
}
248231

249-
func (s swarmFlavor) Prepare(
232+
func (s *swarmFlavor) Prepare(
250233
flavorProperties json.RawMessage,
251234
spec instance.Spec,
252235
allocation types.AllocationMethod) (instance.Spec, error) {
@@ -281,24 +264,34 @@ func (s swarmFlavor) Prepare(
281264

282265
switch properties.Type {
283266
case worker:
284-
spec.Init = generateInitScript(
285-
self.ManagerStatus.Addr,
286-
swarmStatus.JoinTokens.Worker,
287-
associationID,
288-
properties.DockerRestartCommand)
267+
initScript, err :=
268+
generateInitScript(
269+
s.initScript,
270+
self.ManagerStatus.Addr,
271+
swarmStatus.JoinTokens.Worker,
272+
associationID,
273+
properties.DockerRestartCommand)
274+
if err != nil {
275+
return spec, err
276+
}
277+
spec.Init = initScript
289278

290279
case manager:
291280
if spec.LogicalID == nil {
292281
return spec, errors.New("Manager nodes require a LogicalID, " +
293282
"which will be used as an assigned private IP address")
294283
}
295284

296-
spec.Init = generateInitScript(
285+
initScript, err := generateInitScript(
286+
s.initScript,
297287
self.ManagerStatus.Addr,
298288
swarmStatus.JoinTokens.Manager,
299289
associationID,
300290
properties.DockerRestartCommand)
301-
291+
if err != nil {
292+
return spec, err
293+
}
294+
spec.Init = initScript
302295
default:
303296
return spec, errors.New("Unsupported node type")
304297
}

pkg/example/flavor/swarm/flavor_test.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,33 @@ package main
33
import (
44
"encoding/json"
55
"fmt"
6+
"testing"
7+
68
docker_types "github.com/docker/docker/api/types"
79
"github.com/docker/docker/api/types/filters"
810
"github.com/docker/docker/api/types/swarm"
911
mock_client "github.com/docker/infrakit/pkg/mock/docker/docker/client"
1012
"github.com/docker/infrakit/pkg/plugin/group/types"
1113
"github.com/docker/infrakit/pkg/spi/flavor"
1214
"github.com/docker/infrakit/pkg/spi/instance"
15+
"github.com/docker/infrakit/pkg/template"
1316
"github.com/golang/mock/gomock"
1417
"github.com/stretchr/testify/require"
15-
"testing"
1618
)
1719

20+
func templ() *template.Template {
21+
t, err := template.NewTemplate("str://"+DefaultInitScriptTemplate, template.Options{})
22+
if err != nil {
23+
panic(err)
24+
}
25+
return t
26+
}
27+
1828
func TestValidate(t *testing.T) {
1929
ctrl := gomock.NewController(t)
2030
defer ctrl.Finish()
2131

22-
swarmFlavor := NewSwarmFlavor(mock_client.NewMockAPIClient(ctrl))
32+
swarmFlavor := NewSwarmFlavor(mock_client.NewMockAPIClient(ctrl), templ())
2333

2434
require.NoError(t, swarmFlavor.Validate(
2535
json.RawMessage(`{"Type": "worker", "DockerRestartCommand": "systemctl restart docker"}`),
@@ -72,7 +82,7 @@ func TestWorker(t *testing.T) {
7282

7383
client := mock_client.NewMockAPIClient(ctrl)
7484

75-
flavorImpl := NewSwarmFlavor(client)
85+
flavorImpl := NewSwarmFlavor(client, templ())
7686

7787
swarmInfo := swarm.Swarm{
7888
ClusterInfo: swarm.ClusterInfo{ID: "ClusterUUID"},
@@ -134,7 +144,7 @@ func TestManager(t *testing.T) {
134144

135145
client := mock_client.NewMockAPIClient(ctrl)
136146

137-
flavorImpl := NewSwarmFlavor(client)
147+
flavorImpl := NewSwarmFlavor(client, templ())
138148

139149
swarmInfo := swarm.Swarm{
140150
ClusterInfo: swarm.ClusterInfo{ID: "ClusterUUID"},

pkg/example/flavor/swarm/main.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66
log "github.com/Sirupsen/logrus"
77
"github.com/docker/go-connections/tlsconfig"
88
"github.com/docker/infrakit/pkg/cli"
9+
"github.com/docker/infrakit/pkg/discovery"
910
flavor_plugin "github.com/docker/infrakit/pkg/rpc/flavor"
11+
"github.com/docker/infrakit/pkg/template"
1012
"github.com/docker/infrakit/pkg/util/docker"
1113
"github.com/spf13/cobra"
1214
)
@@ -31,6 +33,8 @@ func main() {
3133
certFile := cmd.Flags().String("tlscert", "", "TLS cert file path")
3234
tlsKey := cmd.Flags().String("tlskey", "", "TLS key file path")
3335
insecureSkipVerify := cmd.Flags().Bool("tlsverify", true, "True to skip TLS")
36+
initScriptTemplURL := cmd.Flags().String("init-template", "", "Init script template file, in URL form")
37+
3438
cmd.RunE = func(c *cobra.Command, args []string) error {
3539

3640
cli.SetLogLevel(*logLevel)
@@ -46,7 +50,27 @@ func main() {
4650
return err
4751
}
4852

49-
cli.RunPlugin(*name, flavor_plugin.PluginServer(NewSwarmFlavor(dockerClient)))
53+
opts := template.Options{
54+
SocketDir: discovery.Dir(),
55+
}
56+
57+
var templ *template.Template
58+
if *initScriptTemplURL == "" {
59+
t, err := template.NewTemplate("str://"+DefaultInitScriptTemplate, opts)
60+
if err != nil {
61+
return err
62+
}
63+
templ = t
64+
} else {
65+
66+
t, err := template.NewTemplate(*initScriptTemplURL, opts)
67+
if err != nil {
68+
return err
69+
}
70+
templ = t
71+
}
72+
73+
cli.RunPlugin(*name, flavor_plugin.PluginServer(NewSwarmFlavor(dockerClient, templ)))
5074
return nil
5175
}
5276

@@ -58,3 +82,25 @@ func main() {
5882
os.Exit(1)
5983
}
6084
}
85+
86+
const (
87+
// DefaultInitScriptTemplate is the default template for the init script which
88+
// the flavor injects into the user data of the instance to configure Docker Swarm.
89+
DefaultInitScriptTemplate = `
90+
#!/bin/sh
91+
set -o errexit
92+
set -o nounset
93+
set -o xtrace
94+
95+
mkdir -p /etc/docker
96+
cat << EOF > /etc/docker/daemon.json
97+
{
98+
"labels": ["swarm-association-id={{.ASSOCIATION_ID}}"]
99+
}
100+
EOF
101+
102+
{{.RESTART_DOCKER}}
103+
104+
docker swarm join {{.MY_IP}} --token {{.JOIN_TOKEN}}
105+
`
106+
)

pkg/example/flavor/swarm/swarm-vagrant-manager.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"ID": "swarm-managers",
33
"Properties": {
44
"Allocation": {
5-
"LogicalIDs": ["192.168.2.200"]
5+
"LogicalIDs": ["192.168.2.200"]
66
},
77
"Instance": {
88
"Plugin": "instance-vagrant",

pkg/example/flavor/swarm/swarm-vagrant-workers.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"ID": "swarm-workers",
33
"Properties": {
44
"Allocation": {
5-
"Size": 1
5+
"Size": 2
66
},
77
"Instance": {
88
"Plugin": "instance-vagrant",

pkg/example/instance/vagrant/instance.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99
"os"
1010
"os/exec"
1111
"path"
12-
"text/template"
1312

1413
"github.com/docker/infrakit/pkg/spi/instance"
14+
"github.com/docker/infrakit/pkg/template"
1515
)
1616

1717
// NewVagrantPlugin creates an instance plugin for vagrant.

pkg/example/instance/vagrant/main.go

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package main
22

33
import (
44
"os"
5-
"text/template"
5+
"path/filepath"
6+
"strings"
67

78
log "github.com/Sirupsen/logrus"
89
"github.com/docker/infrakit/pkg/cli"
10+
"github.com/docker/infrakit/pkg/discovery"
911
instance_plugin "github.com/docker/infrakit/pkg/rpc/instance"
12+
"github.com/docker/infrakit/pkg/template"
1013
"github.com/spf13/cobra"
1114
)
1215

@@ -24,18 +27,38 @@ func main() {
2427
os.Exit(1)
2528
}
2629
dir := cmd.Flags().String("dir", defaultDir, "Vagrant directory")
27-
templFile := cmd.Flags().String("template", "", "Vagrant Template file")
30+
templFile := cmd.Flags().String("template", "", "Vagrant Template file, in URL form")
2831
cmd.RunE = func(c *cobra.Command, args []string) error {
2932

33+
opts := template.Options{
34+
SocketDir: discovery.Dir(),
35+
}
36+
3037
var templ *template.Template
3138
if *templFile == "" {
32-
templ = template.Must(template.New("").Parse(VagrantFile))
39+
t, err := template.NewTemplate("str://"+VagrantFile, opts)
40+
if err != nil {
41+
return err
42+
}
43+
templ = t
3344
} else {
34-
var err error
35-
templ, err = template.ParseFiles()
45+
46+
// For compatiblity with old code, append a file:// if the
47+
// value is just a path
48+
if strings.Index(*templFile, "://") == -1 {
49+
50+
p, err := filepath.Abs(*templFile)
51+
if err != nil {
52+
return err
53+
}
54+
*templFile = "file://localhost" + p
55+
}
56+
57+
t, err := template.NewTemplate(*templFile, opts)
3658
if err != nil {
3759
return err
3860
}
61+
templ = t
3962
}
4063

4164
cli.SetLogLevel(*logLevel)

0 commit comments

Comments
 (0)