Beispiel #1
0
func TestBuild_LookupImage_ExistLocally(t *testing.T) {
	var (
		nilImage *docker.Image

		b, c        = makeBuild(t, "", Config{})
		resultImage = &docker.Image{ID: "789"}
		name        = "ubuntu:latest"

		localImages = []*imagename.ImageName{
			imagename.NewFromString("debian:7.7"),
			imagename.NewFromString("debian:latest"),
			imagename.NewFromString("ubuntu:12.04"),
			imagename.NewFromString("ubuntu:14.04"),
			imagename.NewFromString("ubuntu:latest"),
		}
	)

	c.On("InspectImage", name).Return(nilImage, nil).Once()
	c.On("ListImages").Return(localImages, nil).Once()
	c.On("InspectImage", name).Return(resultImage, nil).Once()

	result, err := b.lookupImage(name)
	if err != nil {
		t.Fatal(err)
	}

	assert.Equal(t, resultImage, result)
	c.AssertExpectations(t)
}
Beispiel #2
0
// PullImage pulls docker image
func (c *DockerClient) PullImage(name string) error {

	var (
		image                  = imagename.NewFromString(name)
		pipeReader, pipeWriter = io.Pipe()
		fdOut, isTerminalOut   = term.GetFdInfo(c.log.Out)
		out                    = c.log.Out
		errch                  = make(chan error, 1)
	)

	if !isTerminalOut {
		out = c.log.Writer()
	}

	opts := docker.PullImageOptions{
		Repository:    image.NameWithRegistry(),
		Registry:      image.Registry,
		Tag:           image.GetTag(),
		OutputStream:  pipeWriter,
		RawJSONStream: true,
	}

	c.log.Infof("| Pull image %s", image)
	c.log.Debugf("Pull image %s with options: %# v", image, opts)

	go func() {
		errch <- jsonmessage.DisplayJSONMessagesStream(pipeReader, out, fdOut, isTerminalOut)
	}()

	if err := c.client.PullImage(opts, c.auth); err != nil {
		return err
	}

	return <-errch
}
Beispiel #3
0
// Execute runs the command
func (c *CommandPush) Execute(b *Build) (State, error) {
	if len(c.cfg.args) != 1 {
		return b.state, fmt.Errorf("PUSH requires exactly one argument")
	}

	if b.state.ImageID == "" {
		return b.state, fmt.Errorf("Cannot PUSH empty image")
	}

	if err := b.client.TagImage(b.state.ImageID, c.cfg.args[0]); err != nil {
		return b.state, err
	}

	image := imagename.NewFromString(c.cfg.args[0])
	artifact := imagename.Artifact{
		Name:      image,
		Pushed:    b.cfg.Push,
		Tag:       image.GetTag(),
		ImageID:   b.state.ImageID,
		BuildTime: time.Now(),
	}

	// push image and add some lines to artifacts
	if b.cfg.Push {
		digest, err := b.client.PushImage(image.String())
		if err != nil {
			return b.state, err
		}
		artifact.Digest = digest
		artifact.Addressable = fmt.Sprintf("%s@%s", image.NameWithRegistry(), digest)
	} else {
		log.Infof("| Don't push. Pass --push flag to actually push to the registry")
	}

	// Publish artifact files
	if b.cfg.ArtifactsPath != "" {
		if err := os.MkdirAll(b.cfg.ArtifactsPath, 0755); err != nil {
			return b.state, fmt.Errorf("Failed to create directory %s for the artifacts, error: %s", b.cfg.ArtifactsPath, err)
		}

		filePath := filepath.Join(b.cfg.ArtifactsPath, artifact.GetFileName())

		artifacts := imagename.Artifacts{
			[]imagename.Artifact{artifact},
		}
		content, err := yaml.Marshal(artifacts)
		if err != nil {
			return b.state, err
		}

		if err := ioutil.WriteFile(filePath, content, 0644); err != nil {
			return b.state, fmt.Errorf("Failed to write artifact file %s, error: %s", filePath, err)
		}

		log.Infof("| Saved artifact file %s", filePath)
		log.Debugf("Artifact properties: %# v", pretty.Formatter(artifact))
	}

	return b.state, nil
}
Beispiel #4
0
func TestBuild_LookupImage_PullAndNotExist(t *testing.T) {
	var (
		b, c = makeBuild(t, "", Config{Pull: true})
		name = "ubuntu:latest"

		remoteImages = []*imagename.ImageName{
			imagename.NewFromString("debian:7.7"),
			imagename.NewFromString("debian:latest"),
			imagename.NewFromString("ubuntu:12.04"),
			imagename.NewFromString("ubuntu:14.04"),
		}
	)

	c.On("ListImageTags", name).Return(remoteImages, nil).Once()

	_, err := b.lookupImage(name)
	assert.EqualError(t, err, "Image not found: ubuntu:latest (also checked in the remote registry)")
	c.AssertExpectations(t)
}
Beispiel #5
0
// PushImage pushes the image
func (c *DockerClient) PushImage(imageName string) (digest string, err error) {
	var (
		img = imagename.NewFromString(imageName)

		buf                    bytes.Buffer
		pipeReader, pipeWriter = io.Pipe()
		outStream              = io.MultiWriter(pipeWriter, &buf)
		fdOut, isTerminalOut   = term.GetFdInfo(c.log.Out)
		out                    = c.log.Out

		opts = docker.PushImageOptions{
			Name:          img.NameWithRegistry(),
			Tag:           img.GetTag(),
			Registry:      img.Registry,
			OutputStream:  outStream,
			RawJSONStream: true,
		}
		errch = make(chan error, 1)
	)

	if !isTerminalOut {
		out = c.log.Writer()
	}

	c.log.Infof("| Push %s", img)

	c.log.Debugf("Push with options: %# v", opts)

	// TODO: DisplayJSONMessagesStream may fail by client.PushImage run without errors
	go func() {
		errch <- jsonmessage.DisplayJSONMessagesStream(pipeReader, out, fdOut, isTerminalOut)
	}()

	if err := c.client.PushImage(opts, c.auth); err != nil {
		return "", err
	}
	pipeWriter.Close()

	if err := <-errch; err != nil {
		return "", fmt.Errorf("Failed to process json stream, error %s", err)
	}

	// It is the best way to have pushed image digest so far
	matches := captureDigest.FindStringSubmatch(buf.String())
	if len(matches) > 0 {
		digest = matches[1]
	}

	return digest, nil
}
Beispiel #6
0
// TagImage adds tag to the image
func (c *DockerClient) TagImage(imageID, imageName string) error {
	img := imagename.NewFromString(imageName)

	c.log.Infof("| Tag %.12s -> %s", imageID, img)

	opts := docker.TagImageOptions{
		Repo:  img.NameWithRegistry(),
		Tag:   img.GetTag(),
		Force: true,
	}

	c.log.Debugf("Tag image %s with options: %# v", imageID, opts)

	return c.client.TagImage(imageID, opts)
}
Beispiel #7
0
// ListImages lists all pulled images in the local docker registry
func (c *DockerClient) ListImages() (images []*imagename.ImageName, err error) {

	var dockerImages []docker.APIImages
	if dockerImages, err = c.client.ListImages(docker.ListImagesOptions{}); err != nil {
		return
	}

	images = []*imagename.ImageName{}
	for _, image := range dockerImages {
		for _, repoTag := range image.RepoTags {
			images = append(images, imagename.NewFromString(repoTag))
		}
	}

	return
}
Beispiel #8
0
func (builder *Builder) ensureImage(imageName string, purpose string) error {
	_, err := builder.Docker.InspectImage(imageName)
	if err != nil && err.Error() == "no such image" {
		fmt.Fprintf(builder.OutStream, "[Rocker] Pulling image: %s for %s\n", imageName, purpose)

		image := imagename.NewFromString(imageName)

		pipeReader, pipeWriter := io.Pipe()

		pullOpts := docker.PullImageOptions{
			Repository:    image.NameWithRegistry(),
			Registry:      image.Registry,
			Tag:           image.GetTag(),
			OutputStream:  pipeWriter,
			RawJSONStream: true,
		}

		errch := make(chan error)

		go func() {
			err := builder.Docker.PullImage(pullOpts, *builder.Auth)

			if err := pipeWriter.Close(); err != nil {
				fmt.Fprintf(builder.OutStream, "pipeWriter.Close() err: %s\n", err)
			}

			errch <- err
		}()

		if err := jsonmessage.DisplayJSONMessagesStream(pipeReader, builder.OutStream, builder.fdOut, builder.isTerminalOut); err != nil {
			return fmt.Errorf("Failed to process json stream for image: %s, error: %s", image, err)
		}

		if err := <-errch; err != nil {
			return fmt.Errorf("Failed to pull image: %s, error: %s", image, err)
		}
	} else if err != nil {
		return err
	}
	return nil
}
Beispiel #9
0
func showCommand(c *cli.Context) {
	dockerClient, err := dockerclient.NewFromCli(c)
	if err != nil {
		log.Fatal(err)
	}

	// Initialize context dir
	args := c.Args()
	if len(args) == 0 {
		log.Fatal("Missing image argument")
	}
	//parse parameter to name
	imageName := imagename.NewFromString(args[0])
	infos := []*build.RockerImageData{}

	if imageName.IsStrict() {
		image, err := dockerClient.InspectImage(args[0])
		if err != nil && err.Error() == "no such image" {
			image, err = imagename.RegistryGet(imageName)
			if err != nil {
				log.Fatal(err)
			}
		} else if err != nil {
			log.Fatal(err)
		}
		info, err := toInfo(imageName, image)
		if err != nil {
			log.Fatal(err)
		}
		infos = append(infos, info)
	} else {
		images, err := imagename.RegistryListTags(imageName)
		if err != nil {
			log.Fatal(err)
		}

		type resp struct {
			name  *imagename.ImageName
			image *docker.Image
			err   error
		}
		chResp := make(chan resp, len(images))

		for _, img := range images {
			go func(img *imagename.ImageName) {
				r := resp{name: img}
				r.image, r.err = imagename.RegistryGet(img)
				chResp <- r
			}(img)
		}

		for _ = range images {
			r := <-chResp
			if r.err != nil {
				log.Println(r.err)
			} else if info, err := toInfo(r.name, r.image); err == nil {
				infos = append(infos, info)
			}
		}
	}

	if c.Bool("json") {
		res, err := json.Marshal(infos)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(string(res))
	} else {
		for _, res := range infos {
			fmt.Println(res.PrettyString())
		}
	}
}
Beispiel #10
0
// lookupImage looks up for the image by name and returns *docker.Image object (result of the inspect)
// `Pull` config option defines whether we want to update the latest version of the image from the remote registry
// See build.Config struct for more details about other build config options.
//
// If `Pull` is false, it tries to lookup locally by exact matching, e.g. if the image is already
// pulled with that exact name given (no fuzzy semver matching)
//
// Then the function fetches the list of all pulled images and tries to match one of them by the given name.
//
// If `Pull` is set to true or if it cannot find the image locally, it then fetches all image
// tags from the remote registry and finds the best match for the given image name.
//
// If it cannot find the image either locally or in the remote registry, it returns `nil`
//
// In case the given image has sha256 tag, it looks for it locally and pulls if it's not found.
// No semver matching is done for sha256 tagged images.
//
// See also TestBuild_LookupImage_* test cases in build_test.go
func (b *Build) lookupImage(name string) (img *docker.Image, err error) {
	var (
		candidate, remoteCandidate *imagename.ImageName

		imgName = imagename.NewFromString(name)
		pull    = false
		hub     = b.cfg.Pull
		isSha   = imgName.TagIsSha()
	)

	// If hub is true, then there is no sense to inspect the local image
	if !hub || isSha {
		// Try to inspect image as is, without version resolution
		if img, err := b.client.InspectImage(name); err != nil || img != nil {
			return img, err
		}
	}

	if isSha {
		// If we are still here and image not found locally, we want to pull it
		candidate = imgName
		hub = false
		pull = true
	}

	if !isSha && !hub {
		// List local images
		var localImages = []*imagename.ImageName{}
		if localImages, err = b.client.ListImages(); err != nil {
			return nil, err
		}
		// Resolve local candidate
		candidate = imgName.ResolveVersion(localImages)
	}

	// In case we want to include external images as well, pulling list of available
	// images from the remote registry
	if hub || candidate == nil {
		log.Debugf("Getting list of tags for %s from the registry", imgName)

		var remoteImages []*imagename.ImageName

		if remoteImages, err = b.client.ListImageTags(imgName.String()); err != nil {
			err = fmt.Errorf("Failed to list tags of image %s from the remote registry, error: %s", imgName, err)
		}

		// Since we found the remote image, we want to pull it
		if remoteCandidate = imgName.ResolveVersion(remoteImages); remoteCandidate != nil {
			pull = true
			candidate = remoteCandidate
		}
	}

	// If not candidate found, it's an error
	if candidate == nil {
		err = fmt.Errorf("Image not found: %s (also checked in the remote registry)", imgName)
		return
	}

	if !isSha && imgName.GetTag() != candidate.GetTag() {
		if remoteCandidate != nil {
			log.Infof("Resolve %s --> %s (found remotely)", imgName, candidate.GetTag())
		} else {
			log.Infof("Resolve %s --> %s", imgName, candidate.GetTag())
		}
	}

	if pull {
		if err = b.client.PullImage(candidate.String()); err != nil {
			return
		}
	}

	return b.client.InspectImage(candidate.String())
}
Beispiel #11
0
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
)

var (
	configTemplateVars = Vars{
		"mykey": "myval",
		"n":     "5",
		"data": map[string]string{
			"foo": "bar",
		},
		"RockerArtifacts": []imagename.Artifact{
			imagename.Artifact{
				Name: imagename.NewFromString("alpine:3.2"),
				Tag:  "3.2",
			},
			imagename.Artifact{
				Name:   imagename.NewFromString("golang:1.5"),
				Tag:    "1.5",
				Digest: "sha256:ead434",
			},
			imagename.Artifact{
				Name:   imagename.NewFromString("data:master"),
				Tag:    "master",
				Digest: "sha256:fafe14",
			},
			imagename.Artifact{
				Name:   imagename.NewFromString("ssh:latest"),
				Tag:    "latest",
Beispiel #12
0
// cmdTag implements TAG command
// TODO: document behavior of cmdTag
func (builder *Builder) cmdTag(args []string, attributes map[string]bool, flags map[string]string, original string) (err error) {
	builder.recentTags = []*imagename.ImageName{}
	if len(args) == 0 {
		return fmt.Errorf("Command is missing value: %s", original)
	}
	image := imagename.NewFromString(args[0])

	// Save rockerfile to label, sot it can be inspected later
	if builder.AddMeta && !builder.metaAdded {
		data := &RockerImageData{
			ImageName:  image,
			Rockerfile: builder.RockerfileContent,
			Vars:       builder.CliVars,
			Properties: template.Vars{},
		}

		if hostname, _ := os.Hostname(); hostname != "" {
			data.Properties["hostname"] = hostname
		}
		if user, _ := user.Current(); user != nil {
			data.Properties["system_login"] = user.Username
			data.Properties["system_user"] = user.Name
		}

		json, err := json.Marshal(data)
		if err != nil {
			return fmt.Errorf("Failed to marshal rocker data, error: %s", err)
		}

		builder.addLabels(map[string]string{
			"rocker-data": string(json),
		})

		fmt.Fprintf(builder.OutStream, "[Rocker]  add rocker-data label\n")

		if err := builder.commitContainer("", builder.Config.Cmd, "LABEL rocker-data"); err != nil {
			return err
		}

		builder.metaAdded = true
	}

	doTag := func(tag string) error {
		img := &imagename.ImageName{
			Registry: image.Registry,
			Name:     image.Name,
			Tag:      tag,
		}
		builder.recentTags = append(builder.recentTags, img)

		fmt.Fprintf(builder.OutStream, "[Rocker]  Tag %.12s -> %s\n", builder.imageID, img)

		err := builder.Docker.TagImage(builder.imageID, docker.TagImageOptions{
			Repo:  img.NameWithRegistry(),
			Tag:   img.GetTag(),
			Force: true,
		})
		if err != nil {
			return fmt.Errorf("Failed to set tag %s to image %s", img, builder.imageID)
		}
		return nil
	}

	// By default, tag with current branch name if tag is not specified
	// do not use :latest unless it was set explicitly
	if !image.HasTag() {
		if builder.Vars.IsSet("branch") && builder.Vars["branch"].(string) != "" {
			image.Tag = builder.Vars["branch"].(string)
		}
		// Additionally, tag image with current git sha
		if builder.Vars.IsSet("commit") && builder.Vars["commit"] != "" {
			if err := doTag(fmt.Sprintf("%.7s", builder.Vars["commit"])); err != nil {
				return err
			}
		}
	}

	// Do the asked tag
	if err := doTag(image.GetTag()); err != nil {
		return err
	}

	// Optionally make a semver aliases
	if _, ok := flags["semver"]; ok && image.HasTag() {
		ver, err := NewSemver(image.GetTag())
		if err != nil {
			return fmt.Errorf("--semver flag expects tag to be in semver format, error: %s", err)
		}
		// If the version is like 1.2.3-build512 we also want to alias 1.2.3
		if ver.HasSuffix() {
			if err := doTag(fmt.Sprintf("%d.%d.%d", ver.Major, ver.Minor, ver.Patch)); err != nil {
				return err
			}
		}
		if err := doTag(fmt.Sprintf("%d.%d.x", ver.Major, ver.Minor)); err != nil {
			return err
		}
		if err := doTag(fmt.Sprintf("%d.x", ver.Major)); err != nil {
			return err
		}
	}

	return nil
}
Beispiel #13
0
// ListImageTags returns the list of images instances obtained from all tags existing in the registry
func (c *DockerClient) ListImageTags(name string) (images []*imagename.ImageName, err error) {
	return imagename.RegistryListTags(imagename.NewFromString(name))
}
Beispiel #14
0
func makeImageHelper(vars Vars) func(string, ...string) (string, error) {
	// Sort artifacts so we match semver on latest item
	var (
		artifacts = &imagename.Artifacts{}
		ok        bool
	)

	if artifacts.RockerArtifacts, ok = vars["RockerArtifacts"].([]imagename.Artifact); !ok {
		artifacts.RockerArtifacts = []imagename.Artifact{}
	}

	sort.Sort(artifacts)

	log.Debugf("`image` helper got artifacts: %# v", pretty.Formatter(artifacts))

	return func(img string, args ...string) (string, error) {
		var (
			matched     bool
			ok          bool
			shouldMatch bool
			image       = imagename.NewFromString(img)
		)

		if len(args) > 0 {
			image = imagename.New(img, args[0])
		}

		for _, a := range artifacts.RockerArtifacts {
			if !image.IsSameKind(*a.Name) {
				continue
			}

			if image.HasVersionRange() {
				if !image.Contains(a.Name) {
					log.Debugf("Skipping artifact %s because it is not suitable for %s", a.Name, image)
					continue
				}
			} else if image.GetTag() != a.Name.GetTag() {
				log.Debugf("Skipping artifact %s because it is not suitable for %s", a.Name, image)
				continue
			}

			if a.Digest != "" {
				log.Infof("Apply artifact digest %s for image %s", a.Digest, image)
				image.SetTag(a.Digest)
				matched = true
				break
			}
			if a.Name.HasTag() {
				log.Infof("Apply artifact tag %s for image %s", a.Name.GetTag(), image)
				image.SetTag(a.Name.GetTag())
				matched = true
				break
			}
		}

		if shouldMatch, ok = vars["DemandArtifacts"].(bool); ok && shouldMatch && !matched {
			return "", fmt.Errorf("Cannot find suitable artifact for image %s", image)
		}

		return image.String(), nil
	}
}