// 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) }
// 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) }
// 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 }
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 }
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 }
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 }
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 }
// 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 }