Skip to content

Commit

Permalink
Merge pull request kubernetes#6100 from pmorie/entrypoint
Browse files Browse the repository at this point in the history
Add control over container entrypoint
  • Loading branch information
vmarmol committed Mar 31, 2015
2 parents d6d370d + 7628b37 commit 9bbf0b1
Show file tree
Hide file tree
Showing 23 changed files with 815 additions and 226 deletions.
20 changes: 20 additions & 0 deletions contrib/for-tests/entrypoint-tester/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

FROM scratch
ADD ep ep
ADD ep ep-2
EXPOSE 8080
ENTRYPOINT ["/ep"]
CMD ["default", "arguments"]
15 changes: 15 additions & 0 deletions contrib/for-tests/entrypoint-tester/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
all: push

TAG = 0.1

ep: ep.go
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' ./ep.go

image: ep
sudo docker build -t kubernetes/eptest:$(TAG) .

push: image
sudo docker push kubernetes/eptest:$(TAG)

clean:
rm -f ep
29 changes: 29 additions & 0 deletions contrib/for-tests/entrypoint-tester/ep.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright 2015 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"fmt"
"os"
)

// This program prints the arguments it's passed and exits.
func main() {
args := os.Args
fmt.Printf("%v\n", args)
os.Exit(0)
}
44 changes: 43 additions & 1 deletion docs/containers.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,46 @@
# Container with Kubernetes
# Containers with Kubernetes

## Containers and commands

So far the Pods we've seen have all used the `image` field to indicate what process Kubernetes
should run in a container. In this case, Kubernetes runs the image's default command. If we want
to run a particular command or override the image's defaults, there are two additional fields that
we can use:

1. `Command`: Controls the actual command run by the image
2. `Args`: Controls the arguments passed to the command

### How docker handles command and arguments

Docker images have metadata associated with them that is used to store information about the image.
The image author may use this to define defaults for the command and arguments to run a container
when the user does not supply values. Docker calls the fields for commands and arguments
`Entrypoint` and `Cmd` respectively. The full details for this feature are too complicated to
describe here, mostly due to the fact that the docker API allows users to specify both of these
fields as either a string array or a string and there are subtle differences in how those cases are
handled. We encourage the curious to check out [docker's documentation]() for this feature.

Kubernetes allows you to override both the image's default command (docker `Entrypoint`) and args
(docker `Cmd`) with the `Command` and `Args` fields of `Container`. The rules are:

1. If you do not supply a `Command` or `Args` for a container, the defaults defined by the image
will be used
2. If you supply a `Command` but no `Args` for a container, only the supplied `Command` will be
used; the image's default arguments are ignored
3. If you supply only `Args`, the image's default command will be used with the arguments you
supply
4. If you supply a `Command` **and** `Args`, the image's defaults will be ignored and the values
you supply will be used

Here are examples for these rules in table format

| Image `Entrypoint` | Image `Cmd` | Container `Command` | Container `Args` | Command Run |
|--------------------|------------------|---------------------|--------------------|------------------|
| `[/ep-1]` | `[foo bar]` | <not set> | <not set> | `[ep-1 foo bar]` |
| `[/ep-1]` | `[foo bar]` | `[/ep-2]` | <not set> | `[ep-2]` |
| `[/ep-1]` | `[foo bar]` | <not set> | `[zoo boo]` | `[ep-1 zoo boo]` |
| `[/ep-1]` | `[foo bar]` | `[/ep-2]` | `[zoo boo]` | `[ep-2 zoo boo]` |


## Capabilities

Expand Down
4 changes: 3 additions & 1 deletion pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,8 +505,10 @@ type Container struct {
Name string `json:"name"`
// Required.
Image string `json:"image"`
// Optional: Defaults to whatever is defined in the image.
// Optional: The docker image's entrypoint is used if this is not provided; cannot be updated.
Command []string `json:"command,omitempty"`
// Optional: The docker image's cmd is used if this is not provided; cannot be updated.
Args []string `json:"args,omitempty"`
// Optional: Defaults to Docker's default.
WorkingDir string `json:"workingDir,omitempty"`
Ports []ContainerPort `json:"ports,omitempty"`
Expand Down
10 changes: 8 additions & 2 deletions pkg/api/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,10 @@ func init() {
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
return err
}
if err := s.Convert(&in.Command, &out.Command, 0); err != nil {
if err := s.Convert(&in.Command, &out.Entrypoint, 0); err != nil {
return err
}
if err := s.Convert(&in.Args, &out.Command, 0); err != nil {
return err
}
if err := s.Convert(&in.WorkingDir, &out.WorkingDir, 0); err != nil {
Expand Down Expand Up @@ -615,7 +618,10 @@ func init() {
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
return err
}
if err := s.Convert(&in.Command, &out.Command, 0); err != nil {
if err := s.Convert(&in.Command, &out.Args, 0); err != nil {
return err
}
if err := s.Convert(&in.Entrypoint, &out.Command, 0); err != nil {
return err
}
if err := s.Convert(&in.WorkingDir, &out.WorkingDir, 0); err != nil {
Expand Down
8 changes: 5 additions & 3 deletions pkg/api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,11 @@ type Container struct {
Name string `json:"name" description:"name of the container; must be a DNS_LABEL and unique within the pod; cannot be updated"`
// Required.
Image string `json:"image" description:"Docker image name"`
// Optional: Defaults to whatever is defined in the image.
Command []string `json:"command,omitempty" description:"command argv array; not executed within a shell; defaults to entrypoint or command in the image; cannot be updated"`
// Optional: Defaults to Docker's default.
// Optional: The image's entrypoint is used if this is not provided; cannot be updated.
Entrypoint []string `json:"entrypoint:omitempty" description:"entrypoint array; not executed within a shell; the image's entrypoint is used if this is not provided; cannot be updated"`
// Optional: The image's cmd is used if this is not provided; cannot be updated.
Command []string `json:"command,omitempty" description:"command argv array; not executed within a shell; the image's cmd is used if this is not provided; cannot be updated"`
// Optional: Docker's default is used if this is not provided.
WorkingDir string `json:"workingDir,omitempty" description:"container's working directory; defaults to image's default; cannot be updated"`
Ports []ContainerPort `json:"ports,omitempty" description:"list of ports to expose from the container; cannot be updated"`
Env []EnvVar `json:"env,omitempty" description:"list of environment variables to set in the container; cannot be updated"`
Expand Down
11 changes: 8 additions & 3 deletions pkg/api/v1beta2/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,15 @@ func init() {
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
return err
}
if err := s.Convert(&in.Command, &out.Command, 0); err != nil {
if err := s.Convert(&in.Command, &out.Entrypoint, 0); err != nil {
return err
}
if err := s.Convert(&in.Args, &out.Command, 0); err != nil {
return err
}
if err := s.Convert(&in.WorkingDir, &out.WorkingDir, 0); err != nil {
return err
}

if err := s.Convert(&in.Ports, &out.Ports, 0); err != nil {
return err
}
Expand Down Expand Up @@ -403,7 +405,10 @@ func init() {
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
return err
}
if err := s.Convert(&in.Command, &out.Command, 0); err != nil {
if err := s.Convert(&in.Command, &out.Args, 0); err != nil {
return err
}
if err := s.Convert(&in.Entrypoint, &out.Command, 0); err != nil {
return err
}
if err := s.Convert(&in.WorkingDir, &out.WorkingDir, 0); err != nil {
Expand Down
8 changes: 5 additions & 3 deletions pkg/api/v1beta2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,9 +393,11 @@ type Container struct {
Name string `json:"name" description:"name of the container; must be a DNS_LABEL and unique within the pod; cannot be updated"`
// Required.
Image string `json:"image" description:"Docker image name"`
// Optional: Defaults to whatever is defined in the image.
Command []string `json:"command,omitempty" description:"command argv array; not executed within a shell; defaults to entrypoint or command in the image; cannot be updated"`
// Optional: Defaults to Docker's default.
// Optional: The image's entrypoint is used if this is not provided; cannot be updated.
Entrypoint []string `json:"entrypoint:omitempty" description:"entrypoint array; not executed within a shell; the image's entrypoint is used if this is not provided; cannot be updated"`
// Optional: The image's cmd is used if this is not provided; cannot be updated.
Command []string `json:"command,omitempty" description:"command argv array; not executed within a shell; the image's cmd is used if this is not provided; cannot be updated"`
// Optional: Docker's default is used if this is not provided.
WorkingDir string `json:"workingDir,omitempty" description:"container's working directory; defaults to image's default; cannot be updated"`
Ports []ContainerPort `json:"ports,omitempty" description:"list of ports to expose from the container; cannot be updated"`
Env []EnvVar `json:"env,omitempty" description:"list of environment variables to set in the container; cannot be updated"`
Expand Down
6 changes: 4 additions & 2 deletions pkg/api/v1beta3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,10 @@ type Container struct {
Name string `json:"name" description:"name of the container; must be a DNS_LABEL and unique within the pod; cannot be updated"`
// Required.
Image string `json:"image" description:"Docker image name"`
// Optional: Defaults to whatever is defined in the image.
Command []string `json:"command,omitempty" description:"command argv array; not executed within a shell; defaults to entrypoint or command in the image; cannot be updated"`
// Optional: The docker image's entrypoint is used if this is not provided; cannot be updated.
Command []string `json:"command,omitempty" description:"entrypoint array; not executed within a shell; the docker image's entrypoint is used if this is not provided; cannot be updated"`
// Optional: The docker image's cmd is used if this is not provided; cannot be updated.
Args []string `json:"args,omitempty" description:"command array; the docker image's cmd is used if this is not provided; arguments to the entrypoint; cannot be updated"`
// Optional: Defaults to Docker's default.
WorkingDir string `json:"workingDir,omitempty" description:"container's working directory; defaults to image's default; cannot be updated"`
Ports []ContainerPort `json:"ports,omitempty" description:"list of ports to expose from the container; cannot be updated"`
Expand Down
39 changes: 0 additions & 39 deletions pkg/kubelet/container/container_reference_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package container

import (
"fmt"
"sync"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
Expand Down Expand Up @@ -62,41 +61,3 @@ func (c *RefManager) GetRef(id string) (ref *api.ObjectReference, ok bool) {
ref, ok = c.containerIDToRef[id]
return ref, ok
}

// fieldPath returns a fieldPath locating container within pod.
// Returns an error if the container isn't part of the pod.
func fieldPath(pod *api.Pod, container *api.Container) (string, error) {
for i := range pod.Spec.Containers {
here := &pod.Spec.Containers[i]
if here.Name == container.Name {
if here.Name == "" {
return fmt.Sprintf("spec.containers[%d]", i), nil
} else {
return fmt.Sprintf("spec.containers{%s}", here.Name), nil
}
}
}
return "", fmt.Errorf("container %#v not found in pod %#v", container, pod)
}

// GenerateContainerRef returns an *api.ObjectReference which references the given container within the
// given pod. Returns an error if the reference can't be constructed or the container doesn't
// actually belong to the pod.
// TODO: Pods that came to us by static config or over HTTP have no selfLink set, which makes
// this fail and log an error. Figure out how we want to identify these pods to the rest of the
// system.
// TODO(yifan): Revisit this function later, for current case it does not need to use RefManager
// as a receiver.
func (c *RefManager) GenerateContainerRef(pod *api.Pod, container *api.Container) (*api.ObjectReference, error) {
fieldPath, err := fieldPath(pod, container)
if err != nil {
// TODO: figure out intelligent way to refer to containers that we implicitly
// start (like the pod infra container). This is not a good way, ugh.
fieldPath = "implicitly required container " + container.Name
}
ref, err := api.GetPartialReference(pod, fieldPath)
if err != nil {
return nil, err
}
return ref, nil
}
61 changes: 0 additions & 61 deletions pkg/kubelet/container/container_reference_manager_test.go

This file was deleted.

59 changes: 59 additions & 0 deletions pkg/kubelet/container/ref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
Copyright 2015 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package container

import (
"fmt"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
)

// GenerateContainerRef returns an *api.ObjectReference which references the given container
// within the given pod. Returns an error if the reference can't be constructed or the
// container doesn't actually belong to the pod.
//
// This function will return an error if the provided Pod does not have a selfLink,
// but we expect selfLink to be populated at all call sites for the function.
func GenerateContainerRef(pod *api.Pod, container *api.Container) (*api.ObjectReference, error) {
fieldPath, err := fieldPath(pod, container)
if err != nil {
// TODO: figure out intelligent way to refer to containers that we implicitly
// start (like the pod infra container). This is not a good way, ugh.
fieldPath = "implicitly required container " + container.Name
}
ref, err := api.GetPartialReference(pod, fieldPath)
if err != nil {
return nil, err
}
return ref, nil
}

// fieldPath returns a fieldPath locating container within pod.
// Returns an error if the container isn't part of the pod.
func fieldPath(pod *api.Pod, container *api.Container) (string, error) {
for i := range pod.Spec.Containers {
here := &pod.Spec.Containers[i]
if here.Name == container.Name {
if here.Name == "" {
return fmt.Sprintf("spec.containers[%d]", i), nil
} else {
return fmt.Sprintf("spec.containers{%s}", here.Name), nil
}
}
}
return "", fmt.Errorf("container %#v not found in pod %#v", container, pod)
}
Loading

0 comments on commit 9bbf0b1

Please sign in to comment.