forked from hashicorp/packer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
windows_container_communicator.go
191 lines (159 loc) · 4.66 KB
/
windows_container_communicator.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package docker
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
packersdk "github.com/Bourne-ID/packer/packer-plugin-sdk/packer"
)
// Windows containers are a special beast in Docker; you can't use docker cp
// to move files between the container and host.
// This communicator works around that limitation by reusing all possible
// methods and fields of the normal Docker Communicator, but we overwrite the
// Upload, Download, and UploadDir methods to utilize a mounted directory and
// native powershell commands rather than relying on docker cp.
type WindowsContainerCommunicator struct {
Communicator
}
// Upload uses docker exec to copy the file from the host to the container
func (c *WindowsContainerCommunicator) Upload(dst string, src io.Reader, fi *os.FileInfo) error {
// Create a temporary file to store the upload
tempfile, err := ioutil.TempFile(c.HostDir, "upload")
if err != nil {
return err
}
defer os.Remove(tempfile.Name())
// Copy the contents to the temporary file
_, err = io.Copy(tempfile, src)
if err != nil {
return err
}
if fi != nil {
tempfile.Chmod((*fi).Mode())
}
tempfile.Close()
// Copy the file into place by copying the temporary file we put
// into the shared folder into the proper location in the container
cmd := &packersdk.RemoteCmd{
Command: fmt.Sprintf("Copy-Item -Path %s/%s -Destination %s", c.ContainerDir,
filepath.Base(tempfile.Name()), dst),
}
ctx := context.TODO()
if err := c.Start(ctx, cmd); err != nil {
return err
}
// Wait for the copy to complete
cmd.Wait()
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Upload failed with non-zero exit status: %d", cmd.ExitStatus())
}
return nil
}
func (c *WindowsContainerCommunicator) UploadDir(dst string, src string, exclude []string) error {
// Create the temporary directory that will store the contents of "src"
// for copying into the container.
td, err := ioutil.TempDir(c.HostDir, "dirupload")
if err != nil {
return err
}
defer os.RemoveAll(td)
walkFn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
relpath, err := filepath.Rel(src, path)
if err != nil {
return err
}
hostpath := filepath.Join(td, relpath)
// If it is a directory, just create it
if info.IsDir() {
return os.MkdirAll(hostpath, info.Mode())
}
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
dest, err := os.Readlink(path)
if err != nil {
return err
}
return os.Symlink(dest, hostpath)
}
// It is a file, copy it over, including mode.
src, err := os.Open(path)
if err != nil {
return err
}
defer src.Close()
dst, err := os.Create(hostpath)
if err != nil {
return err
}
defer dst.Close()
if _, err := io.Copy(dst, src); err != nil {
return err
}
return nil
}
// Copy the entire directory tree to the temporary directory
if err := filepath.Walk(src, walkFn); err != nil {
return err
}
// Determine the destination directory
containerSrc := filepath.Join(c.ContainerDir, filepath.Base(td))
containerDst := dst
if src[len(src)-1] != '/' {
containerDst = filepath.Join(dst, filepath.Base(src))
}
// Make the directory, then copy into it
cmd := &packersdk.RemoteCmd{
Command: fmt.Sprintf("Copy-Item %s -Destination %s -Recurse",
containerSrc, containerDst),
}
ctx := context.TODO()
if err := c.Start(ctx, cmd); err != nil {
return err
}
// Wait for the copy to complete
cmd.Wait()
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Upload failed with non-zero exit status: %d", cmd.ExitStatus())
}
return nil
}
// Download pulls a file out of a container using `docker cp`. We have a source
// path and want to write to an io.Writer
func (c *WindowsContainerCommunicator) Download(src string, dst io.Writer) error {
log.Printf("Downloading file from container: %s:%s", c.ContainerID, src)
// Copy file onto temp file on mounted volume inside container
var stdout, stderr bytes.Buffer
cmd := &packersdk.RemoteCmd{
Command: fmt.Sprintf("Copy-Item -Path %s -Destination %s/%s", src, c.ContainerDir,
filepath.Base(src)),
Stdout: &stdout,
Stderr: &stderr,
}
ctx := context.TODO()
if err := c.Start(ctx, cmd); err != nil {
return err
}
// Wait for the copy to complete
cmd.Wait()
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Failed to copy file to shared drive: %s, %s, %d", stderr.String(), stdout.String(), cmd.ExitStatus())
}
// Read that copied file into a new file opened on host machine
fsrc, err := os.Open(filepath.Join(c.HostDir, filepath.Base(src)))
if err != nil {
return err
}
defer fsrc.Close()
defer os.Remove(fsrc.Name())
_, err = io.Copy(dst, fsrc)
if err != nil {
return err
}
return nil
}