Beispiel #1
0
// getImageByName tries to find the image info with the given image name.
// TODO(yifan): Replace with 'rkt image cat-manifest'.
func (r *runtime) getImageByName(imageName string) (image, error) {
	// TODO(yifan): Print hash in 'rkt image cat-manifest'?
	images, err := r.listImages()
	if err != nil {
		return image{}, err
	}

	var name, version string
	nameVersion := strings.Split(imageName, ":")

	name, err = appctypes.SanitizeACIdentifier(nameVersion[0])
	if err != nil {
		return image{}, err
	}

	if len(nameVersion) == 2 {
		version = nameVersion[1]
	}

	for _, img := range images {
		if img.name == name {
			if version == "" || img.version == version {
				return img, nil
			}
		}
	}
	return image{}, fmt.Errorf("cannot find the image %q", imageName)
}
Beispiel #2
0
// getImageManifest invokes 'rkt image cat-manifest' to retrive the image manifest
// for the image.
func (r *runtime) getImageManifest(image string) (*appcschema.ImageManifest, error) {
	var manifest appcschema.ImageManifest

	repoToPull, tag := parseImageName(image)
	imgName, err := appctypes.SanitizeACIdentifier(repoToPull)
	if err != nil {
		return nil, err
	}
	output, err := r.runCommand("image", "cat-manifest", fmt.Sprintf("%s:%s", imgName, tag))
	if err != nil {
		return nil, err
	}
	if len(output) != 1 {
		return nil, fmt.Errorf("invalid output: %v", output)
	}
	return &manifest, json.Unmarshal([]byte(output[0]), &manifest)
}
Beispiel #3
0
// listImages lists the images that have the given name. If detail is true,
// then image manifest is also included in the result.
// Note that there could be more than one images that have the given name, we
// will return the result reversely sorted by the import time, so that the latest
// image comes first.
func (r *Runtime) listImages(image string, detail bool) ([]*rktapi.Image, error) {
	repoToPull, tag, _, err := parsers.ParseImageName(image)
	if err != nil {
		return nil, err
	}

	imageFilters := []*rktapi.ImageFilter{
		{
			// TODO(yifan): Add a field in the ImageFilter to match the whole name,
			// not just keywords.
			// https://github.com/coreos/rkt/issues/1872#issuecomment-166456938
			Keywords: []string{repoToPull},
			Labels:   []*rktapi.KeyValue{{Key: "version", Value: tag}},
		},
	}

	// If the repo name is not a valid ACIdentifier (namely if it has a port),
	// then it will have a different name in the store. Search for both the
	// original name and this modified name in case we choose to also change the
	// api-service to do this un-conversion on its end.
	if appcRepoToPull, err := appctypes.SanitizeACIdentifier(repoToPull); err != nil {
		glog.Warningf("could not convert %v to an aci identifier: %v", err)
	} else {
		imageFilters = append(imageFilters, &rktapi.ImageFilter{
			Keywords: []string{appcRepoToPull},
			Labels:   []*rktapi.KeyValue{{Key: "version", Value: tag}},
		})
	}

	ctx, cancel := context.WithTimeout(context.Background(), r.requestTimeout)
	defer cancel()
	listResp, err := r.apisvc.ListImages(ctx, &rktapi.ListImagesRequest{
		Detail:  detail,
		Filters: imageFilters,
	})
	if err != nil {
		return nil, fmt.Errorf("couldn't list images: %v", err)
	}

	// TODO(yifan): Let the API service to sort the result:
	// See https://github.com/coreos/rkt/issues/1911.
	sort.Sort(sort.Reverse(sortByImportTime(listResp.Images)))
	return listResp.Images, nil
}
Beispiel #4
0
func GenerateACI22TopLayer(dockerURL *types.ParsedDockerURL, imageConfig *typesV2.ImageConfig, layerDigest string, outputDir string, layerFile *os.File, curPwl []string, compression common.Compression, lowerLayers []*schema.ImageManifest) (string, *schema.ImageManifest, error) {
	aciName := fmt.Sprintf("%s/%s-%s", dockerURL.IndexURL, dockerURL.ImageName, layerDigest)
	sanitizedAciName, err := appctypes.SanitizeACIdentifier(aciName)
	if err != nil {
		return "", nil, err
	}
	manifest, err := GenerateManifestV22(sanitizedAciName, dockerURL, imageConfig, layerDigest, lowerLayers)
	if err != nil {
		return "", nil, err
	}

	aciPath := generateACIPath(outputDir, aciName, layerDigest, dockerURL.Tag, runtime.GOOS, runtime.GOARCH, -1)
	manifest, err = writeACI(layerFile, *manifest, curPwl, aciPath, compression)
	if err != nil {
		return "", nil, err
	}

	err = ValidateACI(aciPath)
	if err != nil {
		return "", nil, fmt.Errorf("invalid ACI generated: %v", err)
	}
	return aciPath, manifest, nil
}
Beispiel #5
0
func createACI(dir string, fs map[string]*deb, image string, m *schema.ImageManifest) error {
	idir, err := ioutil.TempDir(dir, "image")
	if err != nil {
		return errorf(err.Error())
	}
	rootfs := filepath.Join(idir, "rootfs")
	os.MkdirAll(rootfs, 0755)

	for _, d := range fs {
		err := run(exec.Command("cp", "-a", d.Path+"/.", rootfs))
		if err != nil {
			return err
		}
		i, err := types.SanitizeACIdentifier(
			fmt.Sprintf("debian.org/deb/%v", d.Name))
		if err != nil {
			return errorf(err.Error())
		}
		a, err := types.NewACIdentifier(i)
		if err != nil {
			return errorf(err.Error())
		}
		m.Annotations.Set(
			*a, fmt.Sprintf("%v/%v", d.Arch, d.Version))
	}
	bytes, err := m.MarshalJSON()
	if err != nil {
		return errorf(err.Error())
	}
	if err := ioutil.WriteFile(filepath.Join(idir, "manifest"), bytes, 0644); err != nil {
		return errorf(err.Error())
	}
	if err := run(exec.Command("actool", "build", "-overwrite", idir, image)); err != nil {
		return err
	}
	return nil
}
Beispiel #6
0
func GenerateACI22LowerLayer(dockerURL *types.ParsedDockerURL, layerDigest string, outputDir string, layerFile *os.File, curPwl []string, compression common.Compression) (string, *schema.ImageManifest, error) {
	formattedDigest := strings.Replace(layerDigest, ":", "-", -1)
	aciName := fmt.Sprintf("%s/%s-%s", dockerURL.IndexURL, dockerURL.ImageName, formattedDigest)
	sanitizedAciName, err := appctypes.SanitizeACIdentifier(aciName)
	if err != nil {
		return "", nil, err
	}
	manifest, err := GenerateEmptyManifest(sanitizedAciName)
	if err != nil {
		return "", nil, err
	}

	aciPath := generateACIPath(outputDir, aciName, layerDigest, dockerURL.Tag, runtime.GOOS, runtime.GOARCH, -1)
	manifest, err = writeACI(layerFile, *manifest, curPwl, aciPath, compression)
	if err != nil {
		return "", nil, err
	}

	err = ValidateACI(aciPath)
	if err != nil {
		return "", nil, fmt.Errorf("invalid ACI generated: %v", err)
	}
	return aciPath, manifest, nil
}
Beispiel #7
0
func GenerateManifest(layerData types.DockerImageData, dockerURL *types.ParsedDockerURL) (*schema.ImageManifest, error) {
	dockerConfig := layerData.Config
	genManifest := &schema.ImageManifest{}

	appURL := ""
	// omit docker hub index URL in app name
	if dockerURL.IndexURL != defaultIndex {
		appURL = dockerURL.IndexURL + "/"
	}
	appURL += dockerURL.ImageName + "-" + layerData.ID
	appURL, err := appctypes.SanitizeACIdentifier(appURL)
	if err != nil {
		return nil, err
	}
	name := appctypes.MustACIdentifier(appURL)
	genManifest.Name = *name

	acVersion, err := appctypes.NewSemVer(schemaVersion)
	if err != nil {
		panic("invalid appc spec version")
	}
	genManifest.ACVersion = *acVersion

	genManifest.ACKind = appctypes.ACKind(schema.ImageManifestKind)

	var (
		labels       appctypes.Labels
		parentLabels appctypes.Labels
		annotations  appctypes.Annotations
	)

	layer := appctypes.MustACIdentifier("layer")
	labels = append(labels, appctypes.Label{Name: *layer, Value: layerData.ID})

	tag := dockerURL.Tag
	version := appctypes.MustACIdentifier("version")
	labels = append(labels, appctypes.Label{Name: *version, Value: tag})

	if layerData.OS != "" {
		os := appctypes.MustACIdentifier("os")
		labels = append(labels, appctypes.Label{Name: *os, Value: layerData.OS})
		parentLabels = append(parentLabels, appctypes.Label{Name: *os, Value: layerData.OS})

		if layerData.Architecture != "" {
			arch := appctypes.MustACIdentifier("arch")
			labels = append(labels, appctypes.Label{Name: *arch, Value: layerData.Architecture})
			parentLabels = append(parentLabels, appctypes.Label{Name: *arch, Value: layerData.Architecture})
		}
	}

	if layerData.Author != "" {
		authorsKey := appctypes.MustACIdentifier("authors")
		annotations = append(annotations, appctypes.Annotation{Name: *authorsKey, Value: layerData.Author})
	}
	epoch := time.Unix(0, 0)
	if !layerData.Created.Equal(epoch) {
		createdKey := appctypes.MustACIdentifier("created")
		annotations = append(annotations, appctypes.Annotation{Name: *createdKey, Value: layerData.Created.Format(time.RFC3339)})
	}
	if layerData.Comment != "" {
		commentKey := appctypes.MustACIdentifier("docker-comment")
		annotations = append(annotations, appctypes.Annotation{Name: *commentKey, Value: layerData.Comment})
	}

	annotations = append(annotations, appctypes.Annotation{Name: *appctypes.MustACIdentifier(appcDockerV1RegistryURL), Value: dockerURL.IndexURL})
	annotations = append(annotations, appctypes.Annotation{Name: *appctypes.MustACIdentifier(appcDockerV1Repository), Value: dockerURL.ImageName})
	annotations = append(annotations, appctypes.Annotation{Name: *appctypes.MustACIdentifier(appcDockerV1ImageID), Value: layerData.ID})
	annotations = append(annotations, appctypes.Annotation{Name: *appctypes.MustACIdentifier(appcDockerV1ParentImageID), Value: layerData.Parent})

	genManifest.Labels = labels
	genManifest.Annotations = annotations

	if dockerConfig != nil {
		exec := getExecCommand(dockerConfig.Entrypoint, dockerConfig.Cmd)
		if exec != nil {
			user, group := parseDockerUser(dockerConfig.User)
			var env appctypes.Environment
			for _, v := range dockerConfig.Env {
				parts := strings.SplitN(v, "=", 2)
				env.Set(parts[0], parts[1])
			}
			app := &appctypes.App{
				Exec:             exec,
				User:             user,
				Group:            group,
				Environment:      env,
				WorkingDirectory: dockerConfig.WorkingDir,
			}

			app.MountPoints, err = convertVolumesToMPs(dockerConfig.Volumes)
			if err != nil {
				return nil, err
			}

			app.Ports, err = convertPorts(dockerConfig.ExposedPorts, dockerConfig.PortSpecs)
			if err != nil {
				return nil, err
			}

			genManifest.App = app
		}
	}

	if layerData.Parent != "" {
		indexPrefix := ""
		// omit docker hub index URL in app name
		if dockerURL.IndexURL != defaultIndex {
			indexPrefix = dockerURL.IndexURL + "/"
		}
		parentImageNameString := indexPrefix + dockerURL.ImageName + "-" + layerData.Parent
		parentImageNameString, err := appctypes.SanitizeACIdentifier(parentImageNameString)
		if err != nil {
			return nil, err
		}
		parentImageName := appctypes.MustACIdentifier(parentImageNameString)

		genManifest.Dependencies = append(genManifest.Dependencies, appctypes.Dependency{ImageName: *parentImageName, Labels: parentLabels})

		annotations = append(annotations, appctypes.Annotation{Name: *appctypes.MustACIdentifier(appcDockerV1Tag), Value: dockerURL.Tag})
	}

	return genManifest, nil
}
Beispiel #8
0
// GenerateManifest converts the docker manifest format to an appc
// ImageManifest.
func GenerateManifest(layerData types.DockerImageData, dockerURL *types.ParsedDockerURL) (*schema.ImageManifest, error) {
	dockerConfig := layerData.Config
	genManifest := &schema.ImageManifest{}

	appURL := ""
	appURL = dockerURL.IndexURL + "/"
	appURL += dockerURL.ImageName + "-" + layerData.ID
	appURL, err := appctypes.SanitizeACIdentifier(appURL)
	if err != nil {
		return nil, err
	}
	name := appctypes.MustACIdentifier(appURL)
	genManifest.Name = *name

	acVersion, err := appctypes.NewSemVer(schema.AppContainerVersion.String())
	if err != nil {
		panic("invalid appc spec version")
	}
	genManifest.ACVersion = *acVersion

	genManifest.ACKind = appctypes.ACKind(schema.ImageManifestKind)

	var annotations appctypes.Annotations

	labels := make(map[appctypes.ACIdentifier]string)
	parentLabels := make(map[appctypes.ACIdentifier]string)

	addLabel := func(key, val string) {
		if key != "" && val != "" {
			labels[*appctypes.MustACIdentifier(key)] = val
		}
	}

	addParentLabel := func(key, val string) {
		if key != "" && val != "" {
			parentLabels[*appctypes.MustACIdentifier(key)] = val
		}
	}

	addAnno := func(key, val string) {
		if key != "" && val != "" {
			annotations.Set(*appctypes.MustACIdentifier(key), val)
		}
	}

	addLabel("layer", layerData.ID)
	addLabel("version", dockerURL.Tag)
	addLabel("os", layerData.OS)
	addParentLabel("os", layerData.OS)
	addLabel("arch", layerData.Architecture)
	addParentLabel("arch", layerData.OS)

	addAnno("authors", layerData.Author)
	epoch := time.Unix(0, 0)
	if !layerData.Created.Equal(epoch) {
		addAnno("created", layerData.Created.Format(time.RFC3339))
	}
	addAnno("docker-comment", layerData.Comment)
	addAnno(common.AppcDockerRegistryURL, dockerURL.IndexURL)
	addAnno(common.AppcDockerRepository, dockerURL.ImageName)
	addAnno(common.AppcDockerImageID, layerData.ID)
	addAnno(common.AppcDockerParentImageID, layerData.Parent)

	if dockerConfig != nil {
		exec := getExecCommand(dockerConfig.Entrypoint, dockerConfig.Cmd)
		if exec != nil {
			user, group := parseDockerUser(dockerConfig.User)
			var env appctypes.Environment
			for _, v := range dockerConfig.Env {
				parts := strings.SplitN(v, "=", 2)
				env.Set(parts[0], parts[1])
			}
			app := &appctypes.App{
				Exec:             exec,
				User:             user,
				Group:            group,
				Environment:      env,
				WorkingDirectory: dockerConfig.WorkingDir,
			}

			app.MountPoints, err = convertVolumesToMPs(dockerConfig.Volumes)
			if err != nil {
				return nil, err
			}

			app.Ports, err = convertPorts(dockerConfig.ExposedPorts, dockerConfig.PortSpecs)
			if err != nil {
				return nil, err
			}

			ep, cmd, err := generateEPCmdAnnotation(dockerConfig.Entrypoint, dockerConfig.Cmd)
			if err != nil {
				return nil, err
			}
			if len(ep) > 0 {
				addAnno(common.AppcDockerEntrypoint, ep)
			}
			if len(cmd) > 0 {
				addAnno(common.AppcDockerCmd, cmd)
			}

			genManifest.App = app
		}
	}

	if layerData.Parent != "" {
		indexPrefix := ""
		// omit docker hub index URL in app name
		indexPrefix = dockerURL.IndexURL + "/"
		parentImageNameString := indexPrefix + dockerURL.ImageName + "-" + layerData.Parent
		parentImageNameString, err := appctypes.SanitizeACIdentifier(parentImageNameString)
		if err != nil {
			return nil, err
		}
		parentImageName := appctypes.MustACIdentifier(parentImageNameString)

		plbl, err := appctypes.LabelsFromMap(labels)
		if err != nil {
			return nil, err
		}

		genManifest.Dependencies = append(genManifest.Dependencies, appctypes.Dependency{ImageName: *parentImageName, Labels: plbl})

		addAnno(common.AppcDockerTag, dockerURL.Tag)
	}

	genManifest.Labels, err = appctypes.LabelsFromMap(labels)
	if err != nil {
		return nil, err
	}
	genManifest.Annotations = annotations

	return genManifest, nil
}