Browse Source

Add Entrypoint to builder and container config

By setting an entrypoint in the Dockerfile this
allows one to run an image and only pass arguments.
Michael Crosby 12 years ago
parent
commit
b16ff9f859
8 changed files with 90 additions and 4 deletions
  1. 13 2
      builder.go
  2. 18 1
      buildfile.go
  3. 9 0
      buildfile_test.go
  4. 8 0
      container.go
  5. 26 0
      container_test.go
  6. 1 0
      docs/sources/commandline/command/run.rst
  7. 7 0
      docs/sources/use/builder.rst
  8. 8 1
      utils.go

+ 13 - 2
builder.go

@@ -50,12 +50,23 @@ func (builder *Builder) Create(config *Config) (*Container, error) {
 		config.Hostname = id[:12]
 		config.Hostname = id[:12]
 	}
 	}
 
 
+	var args []string
+	var entrypoint string
+
+	if len(config.Entrypoint) != 0 {
+		entrypoint = config.Entrypoint[0]
+		args = append(config.Entrypoint[1:], config.Cmd...)
+	} else {
+		entrypoint = config.Cmd[0]
+		args = config.Cmd[1:]
+	}
+
 	container := &Container{
 	container := &Container{
 		// FIXME: we should generate the ID here instead of receiving it as an argument
 		// FIXME: we should generate the ID here instead of receiving it as an argument
 		ID:              id,
 		ID:              id,
 		Created:         time.Now(),
 		Created:         time.Now(),
-		Path:            config.Cmd[0],
-		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
+		Path:            entrypoint,
+		Args:            args, //FIXME: de-duplicate from config
 		Config:          config,
 		Config:          config,
 		Image:           img.ID, // Always use the resolved image id
 		Image:           img.ID, // Always use the resolved image id
 		NetworkSettings: &NetworkSettings{},
 		NetworkSettings: &NetworkSettings{},

+ 18 - 1
buildfile.go

@@ -141,7 +141,7 @@ func (b *buildFile) CmdEnv(args string) error {
 func (b *buildFile) CmdCmd(args string) error {
 func (b *buildFile) CmdCmd(args string) error {
 	var cmd []string
 	var cmd []string
 	if err := json.Unmarshal([]byte(args), &cmd); err != nil {
 	if err := json.Unmarshal([]byte(args), &cmd); err != nil {
-		utils.Debugf("Error unmarshalling: %s, using /bin/sh -c", err)
+		utils.Debugf("Error unmarshalling: %s, setting cmd to /bin/sh -c", err)
 		cmd = []string{"/bin/sh", "-c", args}
 		cmd = []string{"/bin/sh", "-c", args}
 	}
 	}
 	if err := b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
 	if err := b.commit("", cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
@@ -165,6 +165,23 @@ func (b *buildFile) CmdCopy(args string) error {
 	return fmt.Errorf("COPY has been deprecated. Please use ADD instead")
 	return fmt.Errorf("COPY has been deprecated. Please use ADD instead")
 }
 }
 
 
+func (b *buildFile) CmdEntrypoint(args string) error {
+	if args == "" {
+		return fmt.Errorf("Entrypoint cannot be empty")
+	}
+
+	var entrypoint []string
+	if err := json.Unmarshal([]byte(args), &entrypoint); err != nil {
+		b.config.Entrypoint = []string{"/bin/sh", "-c", args}
+	} else {
+		b.config.Entrypoint = entrypoint
+	}
+	if err := b.commit("", b.config.Cmd, fmt.Sprintf("ENTRYPOINT %s", args)); err != nil {
+		return err
+	}
+	return nil
+}
+
 func (b *buildFile) addRemote(container *Container, orig, dest string) error {
 func (b *buildFile) addRemote(container *Container, orig, dest string) error {
 	file, err := utils.Download(orig, ioutil.Discard)
 	file, err := utils.Download(orig, ioutil.Discard)
 	if err != nil {
 	if err != nil {

+ 9 - 0
buildfile_test.go

@@ -79,6 +79,15 @@ run [ "$(cat /somewheeeere/over/the/rainbooow/ga)" = "bu" ]
 from %s
 from %s
 env    FOO BAR
 env    FOO BAR
 run    [ "$FOO" = "BAR" ]
 run    [ "$FOO" = "BAR" ]
+`,
+		nil,
+	},
+
+	{
+		`
+from docker-ut
+ENTRYPOINT /bin/echo
+CMD Hello world
 `,
 `,
 		nil,
 		nil,
 	},
 	},

+ 8 - 0
container.go

@@ -76,6 +76,7 @@ type Config struct {
 	Image        string // Name of the image as it was passed by the operator (eg. could be symbolic)
 	Image        string // Name of the image as it was passed by the operator (eg. could be symbolic)
 	Volumes      map[string]struct{}
 	Volumes      map[string]struct{}
 	VolumesFrom  string
 	VolumesFrom  string
+	Entrypoint   []string
 }
 }
 
 
 type HostConfig struct {
 type HostConfig struct {
@@ -123,6 +124,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	cmd.Var(flVolumes, "v", "Attach a data volume")
 	cmd.Var(flVolumes, "v", "Attach a data volume")
 
 
 	flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
 	flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
+	flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image")
 
 
 	var flBinds ListOpts
 	var flBinds ListOpts
 	cmd.Var(&flBinds, "b", "Bind mount a volume from the host (e.g. -b /host:/container)")
 	cmd.Var(&flBinds, "b", "Bind mount a volume from the host (e.g. -b /host:/container)")
@@ -153,6 +155,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 
 
 	parsedArgs := cmd.Args()
 	parsedArgs := cmd.Args()
 	runCmd := []string{}
 	runCmd := []string{}
+	entrypoint := []string{}
 	image := ""
 	image := ""
 	if len(parsedArgs) >= 1 {
 	if len(parsedArgs) >= 1 {
 		image = cmd.Arg(0)
 		image = cmd.Arg(0)
@@ -160,6 +163,10 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 	if len(parsedArgs) > 1 {
 	if len(parsedArgs) > 1 {
 		runCmd = parsedArgs[1:]
 		runCmd = parsedArgs[1:]
 	}
 	}
+	if *flEntrypoint != "" {
+		entrypoint = []string{*flEntrypoint}
+	}
+
 	config := &Config{
 	config := &Config{
 		Hostname:     *flHostname,
 		Hostname:     *flHostname,
 		PortSpecs:    flPorts,
 		PortSpecs:    flPorts,
@@ -177,6 +184,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
 		Image:        image,
 		Image:        image,
 		Volumes:      flVolumes,
 		Volumes:      flVolumes,
 		VolumesFrom:  *flVolumesFrom,
 		VolumesFrom:  *flVolumesFrom,
+		Entrypoint:   entrypoint,
 	}
 	}
 	hostConfig := &HostConfig{
 	hostConfig := &HostConfig{
 		Binds: flBinds,
 		Binds: flBinds,

+ 26 - 0
container_test.go

@@ -1043,6 +1043,32 @@ func TestEnv(t *testing.T) {
 	}
 	}
 }
 }
 
 
+func TestEntrypoint(t *testing.T) {
+	runtime, err := newTestRuntime()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer nuke(runtime)
+	container, err := NewBuilder(runtime).Create(
+		&Config{
+			Image:      GetTestImage(runtime).ID,
+			Entrypoint: []string{"/bin/echo"},
+			Cmd:        []string{"-n", "foobar"},
+		},
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer runtime.Destroy(container)
+	output, err := container.Output()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if string(output) != "foobar" {
+		t.Error(string(output))
+	}
+}
+
 func grepFile(t *testing.T, path string, pattern string) {
 func grepFile(t *testing.T, path string, pattern string) {
 	f, err := os.Open(path)
 	f, err := os.Open(path)
 	if err != nil {
 	if err != nil {

+ 1 - 0
docs/sources/commandline/command/run.rst

@@ -26,3 +26,4 @@
       -v=[]: Creates a new volume and mounts it at the specified path.
       -v=[]: Creates a new volume and mounts it at the specified path.
       -volumes-from="": Mount all volumes from the given container.
       -volumes-from="": Mount all volumes from the given container.
       -b=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]
       -b=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]
+      -entrypoint="": Overwrite the default entrypoint set by the image.

+ 7 - 0
docs/sources/use/builder.rst

@@ -153,6 +153,13 @@ of `<src>` will be written at `<dst>`.
 If `<dest>` doesn't exist, it is created along with all missing directories in its path. All new
 If `<dest>` doesn't exist, it is created along with all missing directories in its path. All new
 files and directories are created with mode 0700, uid and gid 0.
 files and directories are created with mode 0700, uid and gid 0.
 
 
+2.8 ENTRYPOINT
+-------------
+
+    ``ENTRYPOINT /bin/echo``
+
+The `ENTRYPOINT` instruction adds an entry command that will not be overwritten when arguments are passed to docker run, unlike the behavior of `CMD`.  This allows arguments to be passed to the entrypoint.  i.e. `docker run <image> -d` will pass the "-d" argument to the entrypoint.
+
 3. Dockerfile Examples
 3. Dockerfile Examples
 ======================
 ======================
 
 

+ 8 - 1
utils.go

@@ -44,7 +44,11 @@ func CompareConfig(a, b *Config) bool {
 			return false
 			return false
 		}
 		}
 	}
 	}
-
+	for i := 0; i < len(a.Entrypoint); i++ {
+		if a.Entrypoint[i] != b.Entrypoint[i] {
+			return false
+		}
+	}
 	return true
 	return true
 }
 }
 
 
@@ -85,4 +89,7 @@ func MergeConfig(userConf, imageConf *Config) {
 	if userConf.Dns == nil || len(userConf.Dns) == 0 {
 	if userConf.Dns == nil || len(userConf.Dns) == 0 {
 		userConf.Dns = imageConf.Dns
 		userConf.Dns = imageConf.Dns
 	}
 	}
+	if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
+		userConf.Entrypoint = imageConf.Entrypoint
+	}
 }
 }