forked from josharian/packer-provisioner-tunnel
/
main.go
145 lines (118 loc) · 2.88 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Packer-provisioner-tunnel is a packer provisioner plugin.
//
package main
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"strconv"
"strings"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/packer/plugin"
"github.com/mitchellh/packer/template/interpolate"
)
type tunnel struct {
common.PackerConfig `mapstructure:",squash"`
Exec string `mapstructure:"exec"`
Args []string `mapstructure:"args"`
server *sshServer
}
func (t *tunnel) Prepare(raw ...interface{}) error {
var errs *packer.MultiError
err := config.Decode(t, nil, raw...)
if err != nil {
return err
}
if t.Exec == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("missing tunnel provisioner parameter exec"))
}
t.Exec, err = interpolate.Render(t.Exec, nil)
if err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("error processing exec template: %s", err))
}
for i, arg := range t.Args {
t.Args[i], err = interpolate.Render(arg, nil)
if err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("error processing arg %d (%q): %s", i, arg, err))
}
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
var texec string
texec, err = exec.LookPath(t.Exec)
if err != nil {
return fmt.Errorf("executable %q not found: %v", t.Exec, err)
}
t.Exec = texec
t.server, err = newSSHServer()
if err != nil {
return fmt.Errorf("could not initialize ssh server: %v", err)
}
return nil
}
type lineWriter struct {
output func(string)
buffer []byte
}
func (w *lineWriter) Write(b []byte) (int, error) {
w.buffer = append(w.buffer, b...)
for {
i := bytes.IndexByte(w.buffer, '\n')
if i == -1 {
break
}
w.output(string(w.buffer[:i]))
w.buffer = w.buffer[i+1:]
}
return len(b), nil
}
func (w *lineWriter) Flush() {
if len(w.buffer) > 0 {
w.output(string(w.buffer))
}
}
func (t *tunnel) Provision(ui packer.Ui, comm packer.Communicator) error {
ui.Say("Starting tunnel")
t.server.comm = comm
errc := make(chan error, 1)
go func() {
errc <- t.server.serveOne()
}()
stdout := &lineWriter{output: ui.Say}
stderr := &lineWriter{output: ui.Error}
cmd := exec.Command(t.Exec, t.Args...)
cmd.Env = append(os.Environ(),
"PACKER_TUNNEL_USERNAME="+t.server.username,
"PACKER_TUNNEL_PASSWORD="+t.server.password,
"PACKER_TUNNEL_PORT="+strconv.Itoa(t.server.port),
)
cmd.Stdout = stdout
cmd.Stderr = stderr
log.Println("Command", cmd.Args, "env", cmd.Env)
ui.Say("Running command " + strings.Join(cmd.Args, " "))
err := cmd.Run()
stdout.Flush()
stderr.Flush()
if err != nil {
ui.Error(fmt.Sprintf("Error running command %s", err))
return err
}
return <-errc
}
func (t *tunnel) Cancel() {
log.Println("Cancelled")
os.Exit(0)
}
func main() {
server, err := plugin.Server()
if err != nil {
panic(err)
}
server.RegisterProvisioner(new(tunnel))
server.Serve()
}