Skip to content

Commit c594bf4

Browse files
author
OpenShift Bot
committed
Merge pull request #461 from mfojtik/volumes
Merged by openshift-bot
2 parents 95d932a + 1c8b079 commit c594bf4

File tree

11 files changed

+77
-53
lines changed

11 files changed

+77
-53
lines changed

cmd/s2i/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ $ s2i build . centos/ruby-22-centos7 hello-world-app
175175
buildCmd.Flags().StringVarP(&(cfg.DisplayName), "application-name", "n", "", "Specify the display name for the application (default: output image name)")
176176
buildCmd.Flags().StringVarP(&(cfg.Description), "description", "", "", "Specify the description of the application")
177177
buildCmd.Flags().VarP(&(cfg.AllowedUIDs), "allowed-uids", "u", "Specify a range of allowed user ids for the builder image")
178-
buildCmd.Flags().VarP(&(cfg.Injections), "inject", "i", "Specify a list of directories to inject into the assemble container")
178+
buildCmd.Flags().VarP(&(cfg.Injections), "inject", "i", "Specify a directory to inject into the assemble container")
179+
buildCmd.Flags().VarP(&(cfg.BuildVolumes), "volume", "v", "Specify a volume to mount into the assemble container")
179180
buildCmd.Flags().StringSliceVar(&(cfg.DropCapabilities), "cap-drop", []string{}, "Specify a comma-separated list of capabilities to drop when running Docker containers")
180181
buildCmd.Flags().StringVarP(&(oldDestination), "location", "l", "",
181182
"DEPRECATED: Specify a destination location for untar operation")

contrib/bash/s2i

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ _s2i_build()
234234
flags+=("--scripts-url=")
235235
two_word_flags+=("-s")
236236
flags+=("--use-config")
237+
flags+=("--volume=")
238+
two_word_flags+=("-v")
237239

238240
must_have_one_flag=()
239241
must_have_one_noun=()

docs/cli.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ that image and add them to the tar streamed to the container into `/artifacts`.
8888
| `-s (--scripts-url)` | URL of S2I scripts (see [S2I Scripts](https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md#s2i-scripts)) |
8989
| `--recursive` | Perform recursive git clone when getting sources using git|
9090
| `-q (--quiet)` | Operate quietly, suppressing all non-error output |
91-
| `--inject` | Inject the content of the specified directory into the path in the container that runs the assemble script |
91+
| `-i (--inject)` | Inject the content of the specified directory into the path in the container that runs the assemble script |
92+
| `-v (--volume)` | Bind mounts a local directory into the container that runs the assemble script|
9293

9394
#### Context directory
9495

pkg/api/describe/describer.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,17 @@ func DescribeConfig(config *api.Config) string {
6868
if len(config.Injections) > 0 {
6969
result := []string{}
7070
for _, i := range config.Injections {
71-
result = append(result, fmt.Sprintf("%s->%s", i.SourcePath, i.DestinationDir))
71+
result = append(result, fmt.Sprintf("%s->%s", i.Source, i.Destination))
7272
}
7373
fmt.Fprintf(out, "Injections:\t%s\n", strings.Join(result, ","))
7474
}
75+
if len(config.BuildVolumes) > 0 {
76+
result := []string{}
77+
for _, i := range config.BuildVolumes {
78+
result = append(result, fmt.Sprintf("%s->%s", i.Source, i.Destination))
79+
}
80+
fmt.Fprintf(out, "Bind mounts:\t%s\n", strings.Join(result, ","))
81+
}
7582
return nil
7683
})
7784
if err != nil {

pkg/api/types.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ type Config struct {
177177
// Injections specifies a list source/destination folders that are injected to
178178
// the container that runs assemble.
179179
// All files we inject will be truncated after the assemble script finishes.
180-
Injections InjectionList
180+
Injections VolumeList
181181

182182
// CGroupLimits describes the cgroups limits that will be applied to any containers
183183
// run by s2i.
@@ -202,6 +202,10 @@ type Config struct {
202202

203203
// HasOnBuild will be set to true if the builder image contains ONBUILD instructions
204204
HasOnBuild bool
205+
206+
// BuildVolumes specifies a list of volumes to mount to container running the
207+
// build.
208+
BuildVolumes VolumeList
205209
}
206210

207211
// EnvironmentSpec specifies a single environment variable.
@@ -226,14 +230,14 @@ type CGroupLimits struct {
226230
MemorySwap int64
227231
}
228232

229-
// InjectPath contains definition of source directory and the injection path.
230-
type InjectPath struct {
231-
SourcePath string
232-
DestinationDir string
233+
// Volume represents a single volume mount point
234+
type VolumeSpec struct {
235+
Source string
236+
Destination string
233237
}
234238

235-
// InjectionList contains list of InjectPath.
236-
type InjectionList []InjectPath
239+
// VolumeList contains list of VolumeSpec.
240+
type VolumeList []VolumeSpec
237241

238242
// DockerConfig contains the configuration for a Docker connection.
239243
type DockerConfig struct {
@@ -403,7 +407,7 @@ func IsInvalidFilename(name string) bool {
403407
// This function parses the string that contains source:destination pair.
404408
// When the destination is not specified, the source get copied into current
405409
// working directory in container.
406-
func (il *InjectionList) Set(value string) error {
410+
func (l *VolumeList) Set(value string) error {
407411
mount := strings.Split(value, ":")
408412
switch len(mount) {
409413
case 0:
@@ -417,25 +421,25 @@ func (il *InjectionList) Set(value string) error {
417421
default:
418422
return fmt.Errorf("invalid source:path definition")
419423
}
420-
s := InjectPath{SourcePath: filepath.Clean(mount[0]), DestinationDir: filepath.Clean(mount[1])}
421-
if IsInvalidFilename(s.SourcePath) || IsInvalidFilename(s.DestinationDir) {
424+
s := VolumeSpec{Source: filepath.Clean(mount[0]), Destination: filepath.Clean(mount[1])}
425+
if IsInvalidFilename(s.Source) || IsInvalidFilename(s.Destination) {
422426
return fmt.Errorf("invalid characters in filename: %q", value)
423427
}
424-
*il = append(*il, s)
428+
*l = append(*l, s)
425429
return nil
426430
}
427431

428432
// String implements the String() function of pflags.Value interface.
429-
func (il *InjectionList) String() string {
433+
func (l *VolumeList) String() string {
430434
result := []string{}
431-
for _, i := range *il {
432-
result = append(result, strings.Join([]string{i.SourcePath, i.DestinationDir}, ":"))
435+
for _, i := range *l {
436+
result = append(result, strings.Join([]string{i.Source, i.Destination}, ":"))
433437
}
434438
return strings.Join(result, ",")
435439
}
436440

437441
// Type implements the Type() function of pflags.Value interface.
438-
func (il *InjectionList) Type() string {
442+
func (l *VolumeList) Type() string {
439443
return "string"
440444
}
441445

@@ -468,3 +472,13 @@ func (e *EnvironmentList) String() string {
468472
func (e *EnvironmentList) Type() string {
469473
return "string"
470474
}
475+
476+
// AsBinds converts the list of volume definitions to go-dockerclient compatible
477+
// list of bind mounts.
478+
func (l *VolumeList) AsBinds() []string {
479+
result := make([]string, len(*l))
480+
for index, v := range *l {
481+
result[index] = strings.Join([]string{v.Source, v.Destination}, ":")
482+
}
483+
return result
484+
}

pkg/api/types_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@ package api
22

33
import "testing"
44

5-
func TestInjectionListSet(t *testing.T) {
6-
table := map[string][]InjectPath{
7-
"/test:": {{SourcePath: "/test", DestinationDir: "."}},
8-
"/test:/test": {{SourcePath: "/test", DestinationDir: "/test"}},
9-
"/test/foo:/etc/ssl": {{SourcePath: "/test/foo", DestinationDir: "/etc/ssl"}},
10-
":/foo": {{SourcePath: ".", DestinationDir: "/foo"}},
11-
"/foo": {{SourcePath: "/foo", DestinationDir: "."}},
12-
":": {{SourcePath: ".", DestinationDir: "."}},
13-
"/t est/foo:": {{SourcePath: "/t est/foo", DestinationDir: "."}},
14-
`"/test":"/foo"`: {{SourcePath: "/test", DestinationDir: "/foo"}},
15-
`'/test':"/foo"`: {{SourcePath: "/test", DestinationDir: "/foo"}},
5+
func TestVolumeListSet(t *testing.T) {
6+
table := map[string][]VolumeSpec{
7+
"/test:": {{Source: "/test", Destination: "."}},
8+
"/test:/test": {{Source: "/test", Destination: "/test"}},
9+
"/test/foo:/etc/ssl": {{Source: "/test/foo", Destination: "/etc/ssl"}},
10+
":/foo": {{Source: ".", Destination: "/foo"}},
11+
"/foo": {{Source: "/foo", Destination: "."}},
12+
":": {{Source: ".", Destination: "."}},
13+
"/t est/foo:": {{Source: "/t est/foo", Destination: "."}},
14+
`"/test":"/foo"`: {{Source: "/test", Destination: "/foo"}},
15+
`'/test':"/foo"`: {{Source: "/test", Destination: "/foo"}},
1616
`"/te"st":"/foo"`: {},
1717
"/test/foo:/ss;ss": {},
1818
"/test;foo:/ssss": {},
1919
}
2020
for v, expected := range table {
21-
got := InjectionList{}
21+
got := VolumeList{}
2222
err := got.Set(v)
2323
if len(expected) == 0 {
2424
if err == nil {
@@ -33,7 +33,7 @@ func TestInjectionListSet(t *testing.T) {
3333
for _, exp := range expected {
3434
found := false
3535
for _, g := range got {
36-
if g.SourcePath == exp.SourcePath && g.DestinationDir == exp.DestinationDir {
36+
if g.Source == exp.Source && g.Destination == exp.Destination {
3737
found = true
3838
}
3939
}

pkg/build/strategies/sti/sti.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ func (builder *STI) Execute(command string, user string, config *api.Config) err
479479
NetworkMode: string(config.DockerNetworkMode),
480480
CGroupLimits: config.CGroupLimits,
481481
CapDrop: config.DropCapabilities,
482+
Binds: config.BuildVolumes.AsBinds(),
482483
}
483484

484485
// If there are injections specified, override the original assemble script
@@ -514,13 +515,13 @@ func (builder *STI) Execute(command string, user string, config *api.Config) err
514515
}
515516
glog.V(2).Info("starting the injections uploading ...")
516517
for _, s := range config.Injections {
517-
if err := builder.docker.UploadToContainer(s.SourcePath, s.DestinationDir, containerID); err != nil {
518+
if err := builder.docker.UploadToContainer(s.Source, s.Destination, containerID); err != nil {
518519
injectionError = util.HandleInjectionError(s, err)
519520
return err
520521
}
521522
}
522523
if err := builder.docker.UploadToContainer(rmScript, "/tmp/rm-injections", containerID); err != nil {
523-
injectionError = util.HandleInjectionError(api.InjectPath{SourcePath: rmScript, DestinationDir: "/tmp/rm-injections"}, err)
524+
injectionError = util.HandleInjectionError(api.VolumeSpec{Source: rmScript, Destination: "/tmp/rm-injections"}, err)
524525
return err
525526
}
526527
if originalOnStart != nil {

pkg/docker/docker.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ type RunContainerOptions struct {
129129
User string
130130
CGroupLimits *api.CGroupLimits
131131
CapDrop []string
132+
Binds []string
132133
}
133134

134135
// asDockerConfig converts a RunContainerOptions into a Config understood by the
@@ -151,6 +152,7 @@ func (rco RunContainerOptions) asDockerHostConfig() docker.HostConfig {
151152
CapDrop: rco.CapDrop,
152153
PublishAllPorts: rco.TargetImage,
153154
NetworkMode: rco.NetworkMode,
155+
Binds: rco.Binds,
154156
}
155157
if rco.CGroupLimits != nil {
156158
hostConfig.Memory = rco.CGroupLimits.MemoryLimitBytes

pkg/util/injection.go

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,23 @@ import (
1414
// FixInjectionsWithRelativePath fixes the injections that does not specify the
1515
// destination directory or the directory is relative to use the provided
1616
// working directory.
17-
func FixInjectionsWithRelativePath(workdir string, injections *api.InjectionList) {
17+
func FixInjectionsWithRelativePath(workdir string, injections *api.VolumeList) {
1818
if len(*injections) == 0 {
1919
return
2020
}
21-
newList := api.InjectionList{}
21+
newList := api.VolumeList{}
2222
for _, injection := range *injections {
2323
changed := false
24-
if filepath.Clean(injection.DestinationDir) == "." {
25-
injection.DestinationDir = workdir
24+
if filepath.Clean(injection.Destination) == "." {
25+
injection.Destination = workdir
2626
changed = true
2727
}
28-
if !filepath.IsAbs(injection.DestinationDir) {
29-
injection.DestinationDir = filepath.Join(workdir, injection.DestinationDir)
28+
if !filepath.IsAbs(injection.Destination) {
29+
injection.Destination = filepath.Join(workdir, injection.Destination)
3030
changed = true
3131
}
3232
if changed {
33-
glog.V(5).Infof("Using %q as a destination for injecting %q", injection.DestinationDir, injection.SourcePath)
33+
glog.V(5).Infof("Using %q as a destination for injecting %q", injection.Destination, injection.Source)
3434
}
3535
newList = append(newList, injection)
3636
}
@@ -39,24 +39,20 @@ func FixInjectionsWithRelativePath(workdir string, injections *api.InjectionList
3939

4040
// ExpandInjectedFiles returns a flat list of all files that are injected into a
4141
// container. All files from nested directories are returned in the list.
42-
func ExpandInjectedFiles(injections api.InjectionList) ([]string, error) {
42+
func ExpandInjectedFiles(injections api.VolumeList) ([]string, error) {
4343
result := []string{}
4444
for _, s := range injections {
45-
info, err := os.Stat(s.SourcePath)
46-
if err != nil {
45+
if _, err := os.Stat(s.Source); err != nil {
4746
return nil, err
4847
}
49-
if !info.IsDir() {
50-
return nil, fmt.Errorf("the %q must be a valid directory", s.SourcePath)
51-
}
52-
err = filepath.Walk(s.SourcePath, func(path string, f os.FileInfo, err error) error {
48+
err := filepath.Walk(s.Source, func(path string, f os.FileInfo, err error) error {
5349
if err != nil {
5450
return err
5551
}
5652
if f.IsDir() {
5753
return nil
5854
}
59-
newPath := filepath.Join(s.DestinationDir, strings.TrimPrefix(path, s.SourcePath))
55+
newPath := filepath.Join(s.Destination, strings.TrimPrefix(path, s.Source))
6056
result = append(result, newPath)
6157
return nil
6258
})
@@ -91,14 +87,14 @@ func CreateInjectedFilesRemovalScript(files []string, scriptName string) (string
9187

9288
// HandleInjectionError handles the error caused by injection and provide
9389
// reasonable suggestion to users.
94-
func HandleInjectionError(p api.InjectPath, err error) error {
90+
func HandleInjectionError(p api.VolumeSpec, err error) error {
9591
if err == nil {
9692
return nil
9793
}
9894
if strings.Contains(err.Error(), "no such file or directory") {
99-
glog.Errorf("The destination directory for %q injection must exist in container (%q)", p.SourcePath, p.DestinationDir)
95+
glog.Errorf("The destination directory for %q injection must exist in container (%q)", p.Source, p.Destination)
10096
return err
10197
}
102-
glog.Errorf("Error occured during injecting %q to %q: %v", p.SourcePath, p.DestinationDir, err)
98+
glog.Errorf("Error occured during injecting %q to %q: %v", p.Source, p.Destination, err)
10399
return err
104100
}

pkg/util/injection_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func TestExpandInjectedFiles(t *testing.T) {
4444
t.Errorf("Unable to create temp directory: %v", err)
4545
}
4646
defer os.RemoveAll(tmp)
47-
list := api.InjectionList{{SourcePath: tmp, DestinationDir: "/foo"}}
47+
list := api.VolumeList{{Source: tmp, Destination: "/foo"}}
4848
f1, _ := ioutil.TempFile(tmp, "foo")
4949
f2, _ := ioutil.TempFile(tmpNested, "bar")
5050
files, err := ExpandInjectedFiles(list)

0 commit comments

Comments
 (0)