8
8
"io"
9
9
"os"
10
10
"path"
11
+ "path/filepath"
11
12
"runtime"
13
+ "strconv"
12
14
"strings"
13
15
14
16
"k8s.io/kubernetes/pkg/credentialprovider"
@@ -22,6 +24,12 @@ import (
22
24
"github.com/openshift/origin/pkg/util/docker/dockerfile/builder/imageprogress"
23
25
)
24
26
27
+ // Mount represents a binding between the current system and the destination client
28
+ type Mount struct {
29
+ SourcePath string
30
+ DestinationPath string
31
+ }
32
+
25
33
// ClientExecutor can run Docker builds from a Docker client.
26
34
type ClientExecutor struct {
27
35
// Client is a client to a Docker daemon.
@@ -38,6 +46,11 @@ type ClientExecutor struct {
38
46
// AllowPull when set will pull images that are not present on
39
47
// the daemon.
40
48
AllowPull bool
49
+ // TransientMounts are a set of mounts from outside the build
50
+ // to the inside that will not be part of the final image. Any
51
+ // content created inside the mount's destinationPath will be
52
+ // omitted from the final image.
53
+ TransientMounts []Mount
41
54
42
55
Out , ErrOut io.Writer
43
56
@@ -125,6 +138,8 @@ func (e *ClientExecutor) Build(r io.Reader, args map[string]string) error {
125
138
e .LogFn ("FROM %s" , from )
126
139
glog .V (4 ).Infof ("step: FROM %s" , from )
127
140
141
+ var sharedMount string
142
+
128
143
// create a container to execute in, if necessary
129
144
mustStart := b .RequiresStart (node )
130
145
if e .Container == nil {
@@ -134,6 +149,23 @@ func (e *ClientExecutor) Build(r io.Reader, args map[string]string) error {
134
149
},
135
150
}
136
151
if mustStart {
152
+ // Transient mounts only make sense on images that will be running processes
153
+ if len (e .TransientMounts ) > 0 {
154
+ volumeName , err := randSeq (imageSafeCharacters , 24 )
155
+ if err != nil {
156
+ return err
157
+ }
158
+ v , err := e .Client .CreateVolume (docker.CreateVolumeOptions {Name : volumeName })
159
+ if err != nil {
160
+ return err
161
+ }
162
+ defer e .cleanupVolume (volumeName )
163
+ sharedMount = v .Mountpoint
164
+ opts .HostConfig = & docker.HostConfig {
165
+ Binds : []string {sharedMount + ":/tmp/__temporarymount" },
166
+ }
167
+ }
168
+
137
169
// TODO: windows support
138
170
if len (e .Command ) > 0 {
139
171
opts .Config .Cmd = e .Command
@@ -157,9 +189,40 @@ func (e *ClientExecutor) Build(r io.Reader, args map[string]string) error {
157
189
defer e .Cleanup ()
158
190
}
159
191
192
+ // copy any source content into the temporary mount path
193
+ if mustStart && len (e .TransientMounts ) > 0 {
194
+ var copies []Copy
195
+ for i , mount := range e .TransientMounts {
196
+ source := mount .SourcePath
197
+ copies = append (copies , Copy {
198
+ Src : source ,
199
+ Dest : []string {path .Join ("/tmp/__temporarymount" , strconv .Itoa (i ))},
200
+ })
201
+ }
202
+ if err := e .Copy (copies ... ); err != nil {
203
+ return err
204
+ }
205
+ }
206
+
160
207
// TODO: lazy start
161
208
if mustStart && ! e .Container .State .Running {
162
- if err := e .Client .StartContainer (e .Container .ID , e .HostConfig ); err != nil {
209
+ var hostConfig docker.HostConfig
210
+ if e .HostConfig != nil {
211
+ hostConfig = * e .HostConfig
212
+ }
213
+
214
+ // mount individual items temporarily
215
+ for i , mount := range e .TransientMounts {
216
+ if len (sharedMount ) == 0 {
217
+ return fmt .Errorf ("no mount point available for temporary mounts" )
218
+ }
219
+ hostConfig .Binds = append (
220
+ hostConfig .Binds ,
221
+ fmt .Sprintf ("%s:%s:%s" , path .Join (sharedMount , strconv .Itoa (i )), mount .DestinationPath , "ro" ),
222
+ )
223
+ }
224
+
225
+ if err := e .Client .StartContainer (e .Container .ID , & hostConfig ); err != nil {
163
226
return err
164
227
}
165
228
// TODO: is this racy? may have to loop wait in the actual run step
@@ -276,6 +339,11 @@ func randSeq(source string, n int) (string, error) {
276
339
return string (random ), nil
277
340
}
278
341
342
+ // cleanupVolume attempts to remove the provided volume
343
+ func (e * ClientExecutor ) cleanupVolume (name string ) error {
344
+ return e .Client .RemoveVolume (name )
345
+ }
346
+
279
347
// CleanupImage attempts to remove the provided image.
280
348
func (e * ClientExecutor ) CleanupImage (name string ) error {
281
349
return e .Client .RemoveImage (name )
@@ -448,7 +516,15 @@ func (e *ClientExecutor) Archive(src, dst string, allowDecompression, allowDownl
448
516
closer = append (closer , func () error { return os .RemoveAll (base ) })
449
517
}
450
518
} else {
451
- base = e .Directory
519
+ if filepath .IsAbs (src ) {
520
+ base = filepath .Dir (src )
521
+ src , err = filepath .Rel (base , src )
522
+ if err != nil {
523
+ return nil , nil , err
524
+ }
525
+ } else {
526
+ base = e .Directory
527
+ }
452
528
infos , err = CalcCopyInfo (src , base , allowDecompression , true )
453
529
}
454
530
if err != nil {
0 commit comments