Fix image pull bug (wait until download finishes)

Signed-off-by: Nishant Totla <nishanttotla@gmail.com>
This commit is contained in:
Nishant Totla 2016-04-01 15:27:18 -07:00
parent 2363ea8077
commit ff0c79e08d
No known key found for this signature in database
GPG Key ID: 7EA5781C9B3D0C19
3 changed files with 36 additions and 19 deletions

View File

@ -2,6 +2,7 @@ package cluster
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -952,40 +953,54 @@ func (e *Engine) CreateVolume(request *types.VolumeCreateRequest) (*Volume, erro
} }
// FIXME: This will become unnecessary after docker/engine-api#162 is merged // FIXME: This will become unnecessary after docker/engine-api#162 is merged
func getTagFromNamedRef(dref reference.Named) string { func buildImagePullOptions(image string) (types.ImagePullOptions, error) {
var tag string distributionRef, err := reference.ParseNamed(image)
switch x := dref.(type) { if err != nil {
case reference.Digested: return types.ImagePullOptions{}, err
}
name := distributionRef.Name()
tag := "latest"
switch x := distributionRef.(type) {
case reference.Canonical:
tag = x.Digest().String() tag = x.Digest().String()
case reference.NamedTagged: case reference.NamedTagged:
tag = x.Tag() tag = x.Tag()
} }
return tag
return types.ImagePullOptions{
ImageID: name,
Tag: tag,
}, nil
} }
// Pull an image on the engine // Pull an image on the engine
func (e *Engine) Pull(image string, authConfig *types.AuthConfig) error { func (e *Engine) Pull(image string, authConfig *types.AuthConfig) error {
distributionRef, err := reference.ParseNamed(image) pullOpts, err := buildImagePullOptions(image)
if err != nil { if err != nil {
return err return err
} }
pullResponse, err := e.apiClient.ImagePull(context.TODO(), pullOpts, nil)
repository := distributionRef.Name()
tag := getTagFromNamedRef(distributionRef)
pullOpts := types.ImagePullOptions{
ImageID: repository,
Tag: tag,
}
_, err = e.apiClient.ImagePull(context.TODO(), pullOpts, nil)
e.CheckConnectionErr(err) e.CheckConnectionErr(err)
if err != nil { if err != nil {
return err return err
} }
// wait until the image download is finished
dec := json.NewDecoder(pullResponse)
for {
m := map[string]interface{}{}
if err := dec.Decode(&m); err != nil {
if err == io.EOF {
break
}
return err
}
}
// force refresh images // force refresh images
e.RefreshImages() e.RefreshImages()
return nil return nil
} }

View File

@ -293,7 +293,7 @@ func TestCreateContainer(t *testing.T) {
engine = NewEngine("test", 0, engOpts) engine = NewEngine("test", 0, engOpts)
client = mockclient.NewMockClient() client = mockclient.NewMockClient()
apiClient = engineapimock.NewMockClient() apiClient = engineapimock.NewMockClient()
readCloser = nopCloser{bytes.NewBufferString("ok")} readCloser = nopCloser{bytes.NewBufferString("")}
) )
engine.setState(stateUnhealthy) engine.setState(stateUnhealthy)
@ -346,7 +346,7 @@ func TestCreateContainer(t *testing.T) {
id = "id3" id = "id3"
apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil).Once() apiClient.On("ImageList", mock.Anything, mock.AnythingOfType("ImageListOptions")).Return([]types.Image{}, nil).Once()
mockConfig.CPUShares = int64(math.Ceil(float64(config.CPUShares*1024) / float64(mockInfo.NCPU))) mockConfig.CPUShares = int64(math.Ceil(float64(config.CPUShares*1024) / float64(mockInfo.NCPU)))
apiClient.On("ImagePull", mock.Anything, types.ImagePullOptions{ImageID: config.Image}, mock.Anything).Return(readCloser, nil).Once() apiClient.On("ImagePull", mock.Anything, types.ImagePullOptions{ImageID: config.Image, Tag: "latest"}, mock.Anything).Return(readCloser, nil).Once()
// FIXMEENGINEAPI : below should return an engine-api error, or something custom // FIXMEENGINEAPI : below should return an engine-api error, or something custom
apiClient.On("ContainerCreate", mock.Anything, &mockConfig.Config, &mockConfig.HostConfig, &mockConfig.NetworkingConfig, name).Return(types.ContainerCreateResponse{}, dockerclient.ErrImageNotFound).Once() apiClient.On("ContainerCreate", mock.Anything, &mockConfig.Config, &mockConfig.HostConfig, &mockConfig.NetworkingConfig, name).Return(types.ContainerCreateResponse{}, dockerclient.ErrImageNotFound).Once()
// FIXMEENGINEAPI : below should return an engine-api error, or something custom // FIXMEENGINEAPI : below should return an engine-api error, or something custom

View File

@ -14,6 +14,7 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/discovery" "github.com/docker/docker/pkg/discovery"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/docker/engine-api/client"
"github.com/docker/engine-api/types" "github.com/docker/engine-api/types"
containertypes "github.com/docker/engine-api/types/container" containertypes "github.com/docker/engine-api/types/container"
networktypes "github.com/docker/engine-api/types/network" networktypes "github.com/docker/engine-api/types/network"
@ -148,8 +149,9 @@ func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string,
if err != nil { if err != nil {
var retries int64 var retries int64
// fails with image not found, then try to reschedule with image affinity // fails with image not found, then try to reschedule with image affinity
// ENGINEAPIFIXME: The first error can be removed once dockerclient is removed
bImageNotFoundError, _ := regexp.MatchString(`image \S* not found`, err.Error()) bImageNotFoundError, _ := regexp.MatchString(`image \S* not found`, err.Error())
if bImageNotFoundError && !config.HaveNodeConstraint() { if (bImageNotFoundError || client.IsErrImageNotFound(err)) && !config.HaveNodeConstraint() {
// Check if the image exists in the cluster // Check if the image exists in the cluster
// If exists, retry with a image affinity // If exists, retry with a image affinity
if c.Image(config.Image) != nil { if c.Image(config.Image) != nil {