Skip to content

Commit c2a4867

Browse files
authored
Merge pull request #496 from metrue/master
v0.9.33
2 parents ebdd086 + b569820 commit c2a4867

File tree

13 files changed

+253
-55
lines changed

13 files changed

+253
-55
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ clean:
2727
rm -rf ${DIST_DIR}
2828

2929
unit-test:
30-
CI=true ./scripts/coverage.sh
30+
./scripts/coverage.sh
3131

3232
cli-test-ci:
3333
./scripts/test_cli.sh 'js'

fx.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,26 @@ func main() {
231231
Value: defaultSSHKeyFile,
232232
},
233233
cli.StringFlag{
234-
Name: "tag, t",
235-
Usage: "image tag",
234+
Name: "host, H",
235+
Usage: "target host, <user>@<host>",
236+
Value: defaultHost,
237+
},
238+
cli.StringFlag{
239+
Name: "kubeconf, C",
240+
Usage: "kubeconf of kubernetes cluster",
241+
},
242+
cli.StringFlag{
243+
Name: "name, n",
244+
Usage: "image name",
236245
Value: uuid.New().String(),
237246
},
238247
},
239248
Action: handle(
240249
middlewares.Parse("image_build"),
241250
middlewares.Language(),
251+
middlewares.SSH,
252+
middlewares.Driver,
253+
middlewares.Build,
242254
handlers.BuildImage,
243255
),
244256
},

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/gobuffalo/envy v1.8.1 // indirect
1717
github.com/gobuffalo/packd v1.0.0
1818
github.com/gobuffalo/packr/v2 v2.8.0
19-
github.com/golang/mock v1.4.2
19+
github.com/golang/mock v1.4.3
2020
github.com/golang/snappy v0.0.1 // indirect
2121
github.com/google/go-querystring v1.0.0
2222
github.com/google/uuid v1.1.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ github.com/golang/mock v1.4.1 h1:ocYkMQY5RrXTYgXl7ICpV0IXwlEQGwKIsery4gyXa1U=
121121
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
122122
github.com/golang/mock v1.4.2 h1:fXIkPzOBCwDUPvYmOPzETABgbqpYlYNigQ2o64eMr5c=
123123
github.com/golang/mock v1.4.2/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
124+
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
125+
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
124126
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
125127
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
126128
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=

handlers/image.go

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,17 @@
11
package handlers
22

33
import (
4-
"fmt"
5-
"os"
6-
"time"
7-
84
"github.com/apex/log"
95
"github.com/metrue/fx/bundle"
106
"github.com/metrue/fx/constants"
11-
containerruntimes "github.com/metrue/fx/container_runtimes"
127
"github.com/metrue/fx/context"
138
"github.com/metrue/fx/hook"
14-
"github.com/metrue/fx/pkg/spinner"
159
)
1610

1711
// BuildImage build image
1812
func BuildImage(ctx context.Contexter) (err error) {
19-
spinner.Start("building")
20-
defer func() {
21-
spinner.Stop("building", err)
22-
}()
23-
workdir := fmt.Sprintf("/tmp/fx-%d", time.Now().Unix())
24-
defer os.RemoveAll(workdir)
25-
26-
fn := ctx.Get("fn").(string)
27-
deps := ctx.Get("deps").([]string)
28-
language := ctx.Get("language").(string)
29-
30-
if err := bundle.Bundle(workdir, language, fn, deps...); err != nil {
31-
return err
32-
}
33-
34-
if err := hook.RunBeforeBuildHook(workdir); err != nil {
35-
return err
36-
}
37-
38-
docker := ctx.Get("docker").(containerruntimes.ContainerRuntime)
39-
nameWithTag := ctx.Get("tag").(string) + ":latest"
40-
if err := docker.BuildImage(ctx.GetContext(), workdir, nameWithTag); err != nil {
41-
return err
42-
}
43-
log.Infof("image built: %s %v", nameWithTag, constants.CheckedSymbol)
13+
image := ctx.Get("image").(string)
14+
log.Infof("image built: %s %v", image, constants.CheckedSymbol)
4415
return nil
4516
}
4617

middlewares/driver.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package middlewares
22

33
import (
44
"fmt"
5+
"runtime"
56
"time"
67

78
"github.com/apex/log"
@@ -10,7 +11,9 @@ import (
1011
"github.com/metrue/fx/context"
1112
dockerDriver "github.com/metrue/fx/driver/docker"
1213
k8sInfra "github.com/metrue/fx/driver/k8s"
13-
"github.com/metrue/fx/provisioners"
14+
"github.com/metrue/fx/provisioner"
15+
darwin "github.com/metrue/fx/provisioner/darwin"
16+
linux "github.com/metrue/fx/provisioner/linux"
1417
"github.com/metrue/go-ssh-client"
1518
)
1619

@@ -29,7 +32,14 @@ func Driver(ctx context.Contexter) (err error) {
2932
if err := driver.Ping(ctx.GetContext()); err != nil {
3033
log.Infof("provisioning %s ...", host)
3134

32-
provisioner := provisioners.New(sshClient)
35+
var provisioner provisioner.Provisioner
36+
// TODO actually we should get os of target host, not the host of fx
37+
// running on
38+
if runtime.GOOS == "darwin" {
39+
provisioner = darwin.New(sshClient)
40+
} else {
41+
provisioner = linux.New(sshClient)
42+
}
3343
isRemote := (host != "127.0.0.1" && host != "localhost")
3444
if err := provisioner.Provision(ctx.GetContext(), isRemote); err != nil {
3545
return err

middlewares/parse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func Parse(action string) func(ctx context.Contexter) (err error) {
132132
}
133133
ctx.Set("deps", deps)
134134
if err := set(ctx, cli, []argsField{
135-
argsField{Name: "tag", Type: "string"},
135+
argsField{Name: "name", Type: "string"},
136136
argsField{Name: "ssh_port", Type: "string", Env: "SSH_PORT"},
137137
argsField{Name: "ssh_key", Type: "string", Env: "SSH_KEY_FILE"},
138138
argsField{Name: "host", Type: "string", Env: "FX_HOST"},

provisioner/darwin/darwin.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package darwin
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os/exec"
8+
"strings"
9+
"time"
10+
11+
"github.com/metrue/fx/provisioner"
12+
"github.com/metrue/go-ssh-client"
13+
)
14+
15+
const sshConnectionTimeout = 10 * time.Second
16+
17+
var scripts = map[string]string{
18+
"docker_version": "docker version",
19+
"has_docker": "type docker",
20+
"check_fx_agent": "docker inspect fx-agent",
21+
"start_fx_agent": "docker run -d --name=fx-agent --rm -v /var/run/docker.sock:/var/run/docker.sock -p 0.0.0.0:8866:1234 bobrik/socat TCP-LISTEN:1234,fork UNIX-CONNECT:/var/run/docker.sock",
22+
}
23+
24+
// Docker define a fx docker host
25+
type Docker struct {
26+
sshClient ssh.Clienter
27+
}
28+
29+
// New a docker provioner
30+
func New(sshClient ssh.Clienter) *Docker {
31+
return &Docker{
32+
sshClient: sshClient,
33+
}
34+
}
35+
36+
// Provision a host
37+
func (d *Docker) Provision(ctx context.Context, isRemote bool) error {
38+
if err := d.runCmd(scripts["docker_version"], isRemote); err != nil {
39+
if err := d.runCmd(scripts["has_docker"], isRemote); err != nil {
40+
return errors.New("could not find docker on the $PATH")
41+
}
42+
return errors.New("cannot connect to the Docker daemon, is the docker daemon running?")
43+
}
44+
45+
if err := d.runCmd(scripts["check_fx_agent"], isRemote); err != nil {
46+
if err := d.runCmd(scripts["start_fx_agent"], isRemote); err != nil {
47+
return err
48+
}
49+
}
50+
return nil
51+
}
52+
53+
func (d *Docker) runCmd(script string, isRemote bool, options ...ssh.CommandOptions) error {
54+
option := ssh.CommandOptions{
55+
Timeout: sshConnectionTimeout,
56+
}
57+
if len(options) >= 1 {
58+
option = options[0]
59+
}
60+
if !isRemote {
61+
params := strings.Split(script, " ")
62+
if len(params) == 0 {
63+
return fmt.Errorf("invalid script: %s", script)
64+
}
65+
// nolint
66+
cmd := exec.Command(params[0], params[1:]...)
67+
cmd.Stdout = option.Stdout
68+
cmd.Stderr = option.Stderr
69+
err := cmd.Run()
70+
if err != nil {
71+
return err
72+
}
73+
return nil
74+
}
75+
ok, err := d.sshClient.Connectable(sshConnectionTimeout)
76+
if err != nil {
77+
return fmt.Errorf("could not connect via SSH: '%s'", err)
78+
}
79+
if !ok {
80+
return fmt.Errorf("could not connect via SSH")
81+
}
82+
83+
return d.sshClient.RunCommand(script, option)
84+
}
85+
86+
var (
87+
_ provisioner.Provisioner = &Docker{}
88+
)

provisioner/darwin/darwin_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package darwin
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
"github.com/golang/mock/gomock"
9+
"github.com/metrue/go-ssh-client"
10+
sshMocks "github.com/metrue/go-ssh-client/mocks"
11+
)
12+
13+
func TestDriverProvision(t *testing.T) {
14+
t.Run("SSHConnectError", func(t *testing.T) {
15+
ctrl := gomock.NewController(t)
16+
defer ctrl.Finish()
17+
18+
sshClient := sshMocks.NewMockClienter(ctrl)
19+
n := &Docker{sshClient: sshClient}
20+
err := errors.New("could not connect to host")
21+
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(false, err).AnyTimes()
22+
if err := n.Provision(context.Background(), true); err == nil {
23+
t.Fatalf("should get error when SSH connection not ok")
24+
}
25+
})
26+
27+
t.Run("SSHConnectionNotOK", func(t *testing.T) {
28+
ctrl := gomock.NewController(t)
29+
defer ctrl.Finish()
30+
31+
sshClient := sshMocks.NewMockClienter(ctrl)
32+
n := New(sshClient)
33+
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(false, nil).AnyTimes()
34+
if err := n.Provision(context.Background(), true); err == nil {
35+
t.Fatalf("should get error when SSH connection not ok")
36+
}
37+
})
38+
39+
t.Run("DockerAndFxAgentOK", func(t *testing.T) {
40+
ctrl := gomock.NewController(t)
41+
defer ctrl.Finish()
42+
43+
sshClient := sshMocks.NewMockClienter(ctrl)
44+
n := New(sshClient)
45+
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil).AnyTimes()
46+
sshClient.EXPECT().RunCommand(scripts["docker_version"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
47+
sshClient.EXPECT().RunCommand(scripts["check_fx_agent"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
48+
if err := n.Provision(context.Background(), true); err != nil {
49+
t.Fatal(err)
50+
}
51+
})
52+
53+
t.Run("DockerNotReady", func(t *testing.T) {
54+
ctrl := gomock.NewController(t)
55+
defer ctrl.Finish()
56+
57+
sshClient := sshMocks.NewMockClienter(ctrl)
58+
n := New(sshClient)
59+
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil).AnyTimes()
60+
err := errors.New("docker command not found")
61+
sshClient.EXPECT().RunCommand(scripts["docker_version"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(err)
62+
sshClient.EXPECT().RunCommand(scripts["has_docker"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(err)
63+
if err := n.Provision(context.Background(), true); err == nil {
64+
t.Fatal("should tell user to install docker first")
65+
}
66+
})
67+
68+
t.Run("FxAgentNotReady", func(t *testing.T) {
69+
ctrl := gomock.NewController(t)
70+
defer ctrl.Finish()
71+
72+
sshClient := sshMocks.NewMockClienter(ctrl)
73+
n := New(sshClient)
74+
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil).AnyTimes()
75+
err := errors.New("fx agent not found")
76+
sshClient.EXPECT().RunCommand(scripts["docker_version"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
77+
sshClient.EXPECT().RunCommand(scripts["check_fx_agent"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(err)
78+
sshClient.EXPECT().RunCommand(scripts["start_fx_agent"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
79+
if err := n.Provision(context.Background(), true); err != nil {
80+
t.Fatal(err)
81+
}
82+
})
83+
84+
t.Run("DockerAndFxAgentReady", func(t *testing.T) {
85+
ctrl := gomock.NewController(t)
86+
defer ctrl.Finish()
87+
88+
sshClient := sshMocks.NewMockClienter(ctrl)
89+
n := New(sshClient)
90+
91+
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil).AnyTimes()
92+
sshClient.EXPECT().RunCommand(scripts["docker_version"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
93+
sshClient.EXPECT().RunCommand(scripts["check_fx_agent"], ssh.CommandOptions{Timeout: sshConnectionTimeout}).Return(nil)
94+
if err := n.Provision(context.Background(), true); err != nil {
95+
t.Fatal(err)
96+
}
97+
})
98+
}
99+
100+
func TestRunCommand(t *testing.T) {
101+
ctrl := gomock.NewController(t)
102+
defer ctrl.Finish()
103+
104+
sshClient := sshMocks.NewMockClienter(ctrl)
105+
n := &Docker{
106+
sshClient: sshClient,
107+
}
108+
script := "script"
109+
option := ssh.CommandOptions{
110+
Timeout: sshConnectionTimeout,
111+
}
112+
sshClient.EXPECT().Connectable(sshConnectionTimeout).Return(true, nil)
113+
sshClient.EXPECT().RunCommand(script, option).Return(nil)
114+
if err := n.runCmd(script, true, option); err != nil {
115+
t.Fatal(err)
116+
}
117+
}

provisioners/docker.go renamed to provisioner/linux/linux.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package provisioners
1+
package linux
22

33
import (
44
"context"
@@ -7,13 +7,15 @@ import (
77
"strings"
88
"time"
99

10+
"github.com/metrue/fx/provisioner"
1011
"github.com/metrue/go-ssh-client"
1112
)
1213

1314
const sshConnectionTimeout = 10 * time.Second
1415

1516
var scripts = map[string]interface{}{
1617
"docker_version": "docker version",
18+
"has_docker": "type docker",
1719
"install_docker": "curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-18.06.3-ce.tgz -o docker.tgz && tar zxvf docker.tgz && mv docker/* /usr/bin && rm -rf docker docker.tgz",
1820
"start_dockerd": "dockerd >/dev/null 2>&1 & sleep 2",
1921
"check_fx_agent": "docker inspect fx-agent",
@@ -35,8 +37,10 @@ func New(sshClient ssh.Clienter) *Docker {
3537
// Provision a host
3638
func (d *Docker) Provision(ctx context.Context, isRemote bool) error {
3739
if err := d.runCmd(scripts["docker_version"].(string), isRemote); err != nil {
38-
if err := d.runCmd(scripts["install_docker"].(string), isRemote); err != nil {
39-
return err
40+
if err := d.runCmd(scripts["has_docker"].(string), isRemote); err != nil {
41+
if err := d.runCmd(scripts["install_docker"].(string), isRemote); err != nil {
42+
return err
43+
}
4044
}
4145

4246
if err := d.runCmd(scripts["start_dockerd"].(string), isRemote); err != nil {
@@ -86,5 +90,5 @@ func (d *Docker) runCmd(script string, isRemote bool, options ...ssh.CommandOpti
8690
}
8791

8892
var (
89-
_ Provisioner = &Docker{}
93+
_ provisioner.Provisioner = &Docker{}
9094
)

0 commit comments

Comments
 (0)