Merge pull request #1458 from brendandburns/dontpull

Add the ability to turn off image pulling.
This commit is contained in:
Tim Hockin 2014-10-01 12:40:47 -07:00
commit db49dc0012
7 changed files with 118 additions and 3 deletions

View File

@ -17,6 +17,8 @@ limitations under the License.
package api
import (
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/fsouza/go-dockerclient"
)
@ -181,6 +183,38 @@ type LivenessProbe struct {
InitialDelaySeconds int64 `yaml:"initialDelaySeconds,omitempty" json:"initialDelaySeconds,omitempty"`
}
// PullPolicy describes a policy for if/when to pull a container image
type PullPolicy string
const (
// Always attempt to pull the latest image. Container will fail If the pull fails.
PullAlways PullPolicy = "PullAlways"
// Never pull an image, only use a local image. Container will fail if the image isn't present
PullNever PullPolicy = "PullNever"
// Pull if the image isn't present on disk. Container will fail if the image isn't present and the pull fails.
PullIfNotPresent PullPolicy = "PullIfNotPresent"
)
func IsPullAlways(p PullPolicy) bool {
// Default to pull always
if len(p) == 0 {
return true
}
return pullPoliciesEqual(p, PullAlways)
}
func IsPullNever(p PullPolicy) bool {
return pullPoliciesEqual(p, PullNever)
}
func IsPullIfNotPresent(p PullPolicy) bool {
return pullPoliciesEqual(p, PullIfNotPresent)
}
func pullPoliciesEqual(p1, p2 PullPolicy) bool {
return strings.ToLower(string(p1)) == strings.ToLower(string(p2))
}
// Container represents a single container that is expected to be run on the host.
type Container struct {
// Required: This must be a DNS_LABEL. Each container in a pod must
@ -203,6 +237,8 @@ type Container struct {
Lifecycle *Lifecycle `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
// Optional: Default to false.
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"`
// Optional: Policy for pulling images for this container
ImagePullPolicy PullPolicy `json:"imagePullPolicy" yaml:"imagePullPolicy"`
}
// Handler defines a specific action that should be taken

View File

@ -191,6 +191,18 @@ type LivenessProbe struct {
InitialDelaySeconds int64 `yaml:"initialDelaySeconds,omitempty" json:"initialDelaySeconds,omitempty"`
}
// PullPolicy describes a policy for if/when to pull a container image
type PullPolicy string
const (
// Always attempt to pull the latest image. Container will fail If the pull fails.
PullAlways PullPolicy = "PullAlways"
// Never pull an image, only use a local image. Container will fail if the image isn't present
PullNever PullPolicy = "PullNever"
// Pull if the image isn't present on disk. Container will fail if the image isn't present and the pull fails.
PullIfNotPresent PullPolicy = "PullIfNotPresent"
)
// Container represents a single container that is expected to be run on the host.
type Container struct {
// Required: This must be a DNS_LABEL. Each container in a pod must
@ -213,6 +225,8 @@ type Container struct {
Lifecycle *Lifecycle `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
// Optional: Default to false.
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"`
// Optional: Policy for pulling images for this container
ImagePullPolicy PullPolicy `json:"imagePullPolicy" yaml:"imagePullPolicy"`
}
// Handler defines a specific action that should be taken

View File

@ -190,6 +190,18 @@ type LivenessProbe struct {
InitialDelaySeconds int64 `yaml:"initialDelaySeconds,omitempty" json:"initialDelaySeconds,omitempty"`
}
// PullPolicy describes a policy for if/when to pull a container image
type PullPolicy string
const (
// Always attempt to pull the latest image. Container will fail If the pull fails.
PullAlways PullPolicy = "PullAlways"
// Never pull an image, only use a local image. Container will fail if the image isn't present
PullNever PullPolicy = "PullNever"
// Pull if the image isn't present on disk. Container will fail if the image isn't present and the pull fails.
PullIfNotPresent PullPolicy = "PullIfNotPresent"
)
// Container represents a single container that is expected to be run on the host.
type Container struct {
// Required: This must be a DNS_LABEL. Each container in a pod must
@ -212,6 +224,8 @@ type Container struct {
Lifecycle *Lifecycle `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
// Optional: Default to false.
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"`
// Optional: Policy for pulling images for this container
ImagePullPolicy PullPolicy `json:"imagePullPolicy" yaml:"imagePullPolicy"`
}
// Handler defines a specific action that should be taken

View File

@ -226,6 +226,18 @@ type LivenessProbe struct {
InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty" yaml:"initialDelaySeconds,omitempty"`
}
// PullPolicy describes a policy for if/when to pull a container image
type PullPolicy string
const (
// Always attempt to pull the latest image. Container will fail If the pull fails.
PullAlways PullPolicy = "PullAlways"
// Never pull an image, only use a local image. Container will fail if the image isn't present
PullNever PullPolicy = "PullNever"
// Pull if the image isn't present on disk. Container will fail if the image isn't present and the pull fails.
PullIfNotPresent PullPolicy = "PullIfNotPresent"
)
// Container represents a single container that is expected to be run on the host.
type Container struct {
// Required: This must be a DNS_LABEL. Each container in a pod must
@ -248,6 +260,8 @@ type Container struct {
Lifecycle *Lifecycle `json:"lifecycle,omitempty" yaml:"lifecycle,omitempty"`
// Optional: Default to false.
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"`
// Optional: Policy for pulling images for this container
ImagePullPolicy PullPolicy `json:"imagePullPolicy" yaml:"imagePullPolicy"`
}
// Handler defines a specific action that should be taken

View File

@ -47,6 +47,7 @@ type DockerInterface interface {
CreateContainer(docker.CreateContainerOptions) (*docker.Container, error)
StartContainer(id string, hostConfig *docker.HostConfig) error
StopContainer(id string, timeout uint) error
InspectImage(image string) (*docker.Image, error)
PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error
Logs(opts docker.LogsOptions) error
}
@ -57,6 +58,7 @@ type DockerID string
// DockerPuller is an abstract interface for testability. It abstracts image pull operations.
type DockerPuller interface {
Pull(image string) error
IsImagePresent(image string) (bool, error)
}
// dockerPuller is the default implementation of DockerPuller.
@ -148,6 +150,24 @@ func (p throttledDockerPuller) Pull(image string) error {
return fmt.Errorf("pull QPS exceeded.")
}
func (p dockerPuller) IsImagePresent(name string) (bool, error) {
image, _ := parseImageName(name)
_, err := p.client.InspectImage(image)
if err == nil {
return true, nil
}
// This is super brittle, but its the best we got.
// TODO: Land code in the docker client to use docker.Error here instead.
if err.Error() == "no such image" {
return false, nil
}
return false, err
}
func (p throttledDockerPuller) IsImagePresent(name string) (bool, error) {
return p.puller.IsImagePresent(name)
}
// DockerContainers is a map of containers
type DockerContainers map[DockerID]*docker.APIContainers

View File

@ -130,6 +130,10 @@ func (f *FakeDockerClient) PullImage(opts docker.PullImageOptions, auth docker.A
return f.Err
}
func (f *FakeDockerClient) InspectImage(name string) (*docker.Image, error) {
return nil, f.Err
}
// FakeDockerPuller is a stub implementation of DockerPuller.
type FakeDockerPuller struct {
sync.Mutex
@ -153,3 +157,7 @@ func (f *FakeDockerPuller) Pull(image string) (err error) {
}
return err
}
func (f *FakeDockerPuller) IsImagePresent(name string) (bool, error) {
return true, nil
}

View File

@ -528,9 +528,18 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers dockertools.DockerContaine
}
glog.V(3).Infof("Container with name %s--%s--%s doesn't exist, creating %#v", podFullName, uuid, container.Name, container)
if err := kl.dockerPuller.Pull(container.Image); err != nil {
glog.Errorf("Failed to pull image %s: %v skipping pod %s container %s.", container.Image, err, podFullName, container.Name)
continue
if !api.IsPullNever(container.ImagePullPolicy) {
present, err := kl.dockerPuller.IsImagePresent(container.Image)
if err != nil {
glog.Errorf("Failed to inspect image: %s: %#v skipping pod %s container %s", container.Image, err, podFullName, container.Name)
continue
}
if api.IsPullAlways(container.ImagePullPolicy) || !present {
if err := kl.dockerPuller.Pull(container.Image); err != nil {
glog.Errorf("Failed to pull image %s: %v skipping pod %s container %s.", container.Image, err, podFullName, container.Name)
continue
}
}
}
// TODO(dawnchen): Check RestartPolicy.DelaySeconds before restart a container
containerID, err := kl.runContainer(pod, &container, podVolumes, "container:"+string(netID))