searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

regctl 源码分析

2023-06-26 09:33:11
160
0

regctl 没有使用第三方的库,镜像的导出和保存都是自己实现的

镜像导出

命令示例

从docker hub拉取nginx最新镜像保存到本地,然后推送到本地的harbor源

regctl image  export nginx ng.tar -v debug
regctl registry set docker.xx.cn:60001 --tls=disabled
regctl image import docker.xx.cn:60001/library/nginx:v1 ng.tar

ng.tar 导出后的文件列表

tar tvf ng1.tar
-rw-r--r-- 0/0              30 1970-01-01 08:00 oci-layout
-rw-r--r-- 0/0             323 1970-01-01 08:00 index.json
drwxr-xr-x 0/0               0 1970-01-01 08:00 blobs/sha256
-rw-r--r-- 0/0            1862 1970-01-01 08:00 blobs/sha256/b95a99feebf7797479e0c5eb5ec0bdfa5d9f504bc94da550c2f58e839ea6914f
-rw-r--r-- 0/0            1570 1970-01-01 08:00 blobs/sha256/89020cd33be2767f3f894484b8dd77bc2e5a1ccc864350b92c53262213257dfc
-rw-r--r-- 0/0            7654 1970-01-01 08:00 blobs/sha256/2b7d6430f78d432f89109b29d88d4c36c868cdbf15dc31d2132ceaa02b993763
-rw-r--r-- 0/0        31381485 1970-01-01 08:00 blobs/sha256/7a6db449b51b92eac5c81cdbd82917785343f1664b2be57b22337b0a40c5b29d
-rw-r--r-- 0/0        25348180 1970-01-01 08:00 blobs/sha256/ca1981974b581a41cc58598a6b51580d317ac61590be75a8a63fa479e53890da
-rw-r--r-- 0/0             602 1970-01-01 08:00 blobs/sha256/d4019c921e20447eea3c9658bd0780a7e3771641bf29b85f222ec3f54c11a84f
-rw-r--r-- 0/0             894 1970-01-01 08:00 blobs/sha256/7cb804d746d48520f1c0322fcda93249b96b4ed0bbd7f9912b2eb21bd8da6b43

image命令入口

func init(){
    ...
	imageCmd.AddCommand(imageCheckBaseCmd)
	imageCmd.AddCommand(imageCopyCmd)
	imageCmd.AddCommand(imageDeleteCmd)
	imageCmd.AddCommand(imageDigestCmd)
	imageCmd.AddCommand(imageExportCmd)
	imageCmd.AddCommand(imageImportCmd)
	imageCmd.AddCommand(imageInspectCmd)
	imageCmd.AddCommand(imageManifestCmd)
	imageCmd.AddCommand(imageModCmd)
	imageCmd.AddCommand(imageRateLimitCmd)
	rootCmd.AddCommand(imageCmd)
}    

---
var imageExportCmd = &cobra.Command{
	Use:   "export <image_ref> [filename]",
	Short: "export image",
	Long: `Exports an image into a tar file that can be later loaded into a docker
engine with "docker load". The tar file is output to stdout by default.
Example usage: regctl image export registry:5000/yourimg:v1 >yourimg-v1.tar`,
	Args:              cobra.RangeArgs(1, 2),
	ValidArgsFunction: completeArgTag,
    //命令调用的函数
	RunE:              runImageExport,
}


---
//这个导出函数封装的很棒
func runImageExport(cmd *cobra.Command, args []string) error {
	ctx := cmd.Context()
	r, err := ref.New(args[0])
	if err != nil {
		return err
	}
	var w io.Writer
	if len(args) == 2 {
		w, err = os.Create(args[1])
		if err != nil {
			return err
		}
	} else {
		w = os.Stdout
	}
	rc := newRegClient()
	defer rc.Close(ctx, r)
	log.WithFields(logrus.Fields{
		"ref": r.CommonName(),
	}).Debug("Image export")
	return rc.ImageExport(ctx, r, w)
}

分析下ref.New 干了啥,解析镜像ref

var (
	hostPartS = `(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)`
	// host with port allows a short name in addition to hostDomainS
	hostPortS = `(?:` + hostPartS + `(?:` + regexp.QuoteMeta(`.`) + hostPartS + `)*` + regexp.QuoteMeta(`.`) + `?` + regexp.QuoteMeta(`:`) + `[0-9]+)`
	// hostname may be ip, fqdn (example.com), or trailing dot (example.)
	hostDomainS = `(?:` + hostPartS + `(?:(?:` + regexp.QuoteMeta(`.`) + hostPartS + `)+` + regexp.QuoteMeta(`.`) + `?|` + regexp.QuoteMeta(`.`) + `))`
	hostUpperS  = `(?:[a-zA-Z0-9]*[A-Z][a-zA-Z0-9-]*[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[A-Z][a-zA-Z0-9]*)`
	registryS   = `(?:` + hostDomainS + `|` + hostPortS + `|` + hostUpperS + `|localhost(?:` + regexp.QuoteMeta(`:`) + `[0-9]+))`
	repoPartS   = `[a-z0-9]+(?:(?:[_.]|__|[-]*)[a-z0-9]+)*`
	pathS       = `[/a-zA-Z0-9_\-. ]+`
	tagS        = `[\w][\w.-]{0,127}`
	digestS     = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`
	refRE       = regexp.MustCompile(`^(?:(` + registryS + `)` + regexp.QuoteMeta(`/`) + `)?` +
		`(` + repoPartS + `(?:` + regexp.QuoteMeta(`/`) + repoPartS + `)*)` +
		`(?:` + regexp.QuoteMeta(`:`) + `(` + tagS + `))?` +
		`(?:` + regexp.QuoteMeta(`@`) + `(` + digestS + `))?$`)
	schemeRE = regexp.MustCompile(`^([a-z]+)://(.+)$`)
	pathRE   = regexp.MustCompile(`^(` + pathS + `)` +
		`(?:` + regexp.QuoteMeta(`:`) + `(` + tagS + `))?` +
		`(?:` + regexp.QuoteMeta(`@`) + `(` + digestS + `))?$`)
)



// New returns a reference based on the scheme, defaulting to a
func New(parse string) (Ref, error) {
	scheme := ""
	path := parse
    //schemeRE = regexp.MustCompile(`^([a-z]+)://(.+)$`)
    //FindStringSubmatch 返回一个字符串切片,其中包含 s 中正则表达式的最左侧匹配的
    //文本及其子表达式的匹配(如果有),
    //如包注释中的 'Submatch' 说明所定义。返回值 nil 表示不匹配。
	matchScheme := schemeRE.FindStringSubmatch(parse)
	if len(matchScheme) == 3 {
		scheme = matchScheme[1] //协议
		path = matchScheme[2] //路径
	}
	ret := Ref{
		Scheme:    scheme,
		Reference: parse,
	}
	switch scheme {
	case "":
		ret.Scheme = "reg"
		matchRef := refRE.FindStringSubmatch(path)
		if matchRef == nil || len(matchRef) < 5 {
			if refRE.FindStringSubmatch(strings.ToLower(path)) != nil {
                //repo必须是小写字符
				return Ref{}, fmt.Errorf("invalid reference \"%s\", repo must be lowercase", path)
			}
			return Ref{}, fmt.Errorf("invalid reference \"%s\"", path)
		}
		ret.Registry = matchRef[1] //仓库地址
		ret.Repository = matchRef[2]
		ret.Tag = matchRef[3]
		ret.Digest = matchRef[4] //签名

		// handle localhost use case since it matches the regex for a repo path entry
		repoPath := strings.Split(ret.Repository, "/")
		if ret.Registry == "" && repoPath[0] == "localhost" {
			ret.Registry = repoPath[0]
			ret.Repository = strings.Join(repoPath[1:], "/")
		}
		switch ret.Registry {
		case "", dockerRegistryDNS, dockerRegistryLegacy:
			ret.Registry = dockerRegistry
		}
		if ret.Registry == dockerRegistry && !strings.Contains(ret.Repository, "/") {
			ret.Repository = dockerLibrary + "/" + ret.Repository
		}
		if ret.Tag == "" && ret.Digest == "" {
			ret.Tag = "latest"
		}
		if ret.Repository == "" {
			return Ref{}, fmt.Errorf("invalid reference \"%s\"", path)
		}

	case "ocidir", "ocifile":
		matchPath := pathRE.FindStringSubmatch(path)
		if matchPath == nil || len(matchPath) < 2 || matchPath[1] == "" {
			return Ref{}, fmt.Errorf("invalid path for scheme \"%s\": %s", scheme, path)
		}
		ret.Path = matchPath[1]
		if len(matchPath) > 2 && matchPath[2] != "" {
			ret.Tag = matchPath[2]
		}
		if len(matchPath) > 3 && matchPath[3] != "" {
			ret.Digest = matchPath[3]
		}

	default:
		return Ref{}, fmt.Errorf("unhandled reference scheme \"%s\" in \"%s\"", scheme, parse)
	}
	return ret, nil
}

镜像导出

const (
	dockerManifestFilename = "manifest.json"
	ociLayoutVersion       = "1.0.0"
	ociIndexFilename       = "index.json"
	ociLayoutFilename      = "oci-layout"
	annotationRefName      = "org.opencontainers.image.ref.name"
	annotationImageName    = "io.containerd.image.name"
)


// ImageExport exports an image to an output stream.
// The format is compatible with "docker load" if a single image is selected and not a manifest list.
// The ref must include a tag for exporting to docker (defaults to latest), and may also include a digest.
// The export is also formatted according to OCI layout which supports multi-platform images.
// <https://github.com/opencontainers/image-spec/blob/master/image-layout.md>
// A tar file will be sent to outStream.
//
// Resulting filesystem:
// oci-layout: created at top level, can be done at the start
// index.json: created at top level, single descriptor with org.opencontainers.image.ref.name annotation pointing to the tag
// manifest.json: created at top level, based on every layer added, only works for a single arch image
// blobs/$algo/$hash: each content addressable object (manifest, config, or layer), created recursively
func (rc *RegClient) ImageExport(ctx context.Context, r ref.Ref, outStream io.Writer) error {
	var ociIndex v1.Index

	// create tar writer object
    //创建一个压缩流
	tw := tar.NewWriter(outStream)
	defer tw.Close()
	twd := &tarWriteData{
		tw:    tw,
		dirs:  map[string]bool{},
		files: map[string]bool{},
		mode:  0644,
	}

	// retrieve image manifest
    //获取镜像的manifest
	m, err := rc.ManifestGet(ctx, r)
	if err != nil {
		rc.log.WithFields(logrus.Fields{
			"ref": r.CommonName(),
			"err": err,
		}).Warn("Failed to get manifest")
		return err
	}

	// build/write oci-layout
	ociLayout := v1.ImageLayout{Version: ociLayoutVersion}
	err = twd.tarWriteFileJSON(ociLayoutFilename, ociLayout)
	if err != nil {
		return err
	}

	// create a manifest descriptor
	mDesc := m.GetDescriptor()
	if mDesc.Annotations == nil {
		mDesc.Annotations = map[string]string{}
	}
    //io.containerd.image.name
	mDesc.Annotations[annotationImageName] = r.CommonName()
    //org.opencontainers.image.ref.name
	mDesc.Annotations[annotationRefName] = r.Tag

	// generate/write an OCI index
	ociIndex.Versioned = v1.IndexSchemaVersion
	ociIndex.Manifests = []types.Descriptor{mDesc} // initialize with the descriptor to the manifest list
	//manifest信息列表写入到index.json
    err = twd.tarWriteFileJSON(ociIndexFilename, ociIndex)
	if err != nil {
		return err
	}

	// append to docker manifest with tag, config filename, each layer filename, and layer descriptors
	if mi, ok := m.(manifest.Imager); ok {
		conf, err := mi.GetConfig()
		if err != nil {
			return err
		}
		refTag := r.ToReg()
		if refTag.Digest != "" {
			refTag.Digest = ""
		}
		if refTag.Tag == "" {
			refTag.Tag = "latest"
		}
		dockerManifest := dockerTarManifest{
			RepoTags:     []string{refTag.CommonName()},
			Config:       tarOCILayoutDescPath(conf),
			Layers:       []string{},
			LayerSources: map[digest.Digest]types.Descriptor{},
		}
		dl, err := mi.GetLayers()
		if err != nil {
			return err
		}
		for _, d := range dl {
			dockerManifest.Layers = append(dockerManifest.Layers, tarOCILayoutDescPath(d))
			dockerManifest.LayerSources[d.Digest] = d
		}

		// marshal manifest and write manifest.json
		err = twd.tarWriteFileJSON(dockerManifestFilename, []dockerTarManifest{dockerManifest})
		if err != nil {
			return err
		}
	}

	// recursively include manifests and nested blobs
	err = rc.imageExportDescriptor(ctx, r, mDesc, twd)
	if err != nil {
		return err
	}

	return nil
}
0条评论
0 / 1000
d****n
4文章数
0粉丝数
d****n
4 文章 | 0 粉丝
原创

regctl 源码分析

2023-06-26 09:33:11
160
0

regctl 没有使用第三方的库,镜像的导出和保存都是自己实现的

镜像导出

命令示例

从docker hub拉取nginx最新镜像保存到本地,然后推送到本地的harbor源

regctl image  export nginx ng.tar -v debug
regctl registry set docker.xx.cn:60001 --tls=disabled
regctl image import docker.xx.cn:60001/library/nginx:v1 ng.tar

ng.tar 导出后的文件列表

tar tvf ng1.tar
-rw-r--r-- 0/0              30 1970-01-01 08:00 oci-layout
-rw-r--r-- 0/0             323 1970-01-01 08:00 index.json
drwxr-xr-x 0/0               0 1970-01-01 08:00 blobs/sha256
-rw-r--r-- 0/0            1862 1970-01-01 08:00 blobs/sha256/b95a99feebf7797479e0c5eb5ec0bdfa5d9f504bc94da550c2f58e839ea6914f
-rw-r--r-- 0/0            1570 1970-01-01 08:00 blobs/sha256/89020cd33be2767f3f894484b8dd77bc2e5a1ccc864350b92c53262213257dfc
-rw-r--r-- 0/0            7654 1970-01-01 08:00 blobs/sha256/2b7d6430f78d432f89109b29d88d4c36c868cdbf15dc31d2132ceaa02b993763
-rw-r--r-- 0/0        31381485 1970-01-01 08:00 blobs/sha256/7a6db449b51b92eac5c81cdbd82917785343f1664b2be57b22337b0a40c5b29d
-rw-r--r-- 0/0        25348180 1970-01-01 08:00 blobs/sha256/ca1981974b581a41cc58598a6b51580d317ac61590be75a8a63fa479e53890da
-rw-r--r-- 0/0             602 1970-01-01 08:00 blobs/sha256/d4019c921e20447eea3c9658bd0780a7e3771641bf29b85f222ec3f54c11a84f
-rw-r--r-- 0/0             894 1970-01-01 08:00 blobs/sha256/7cb804d746d48520f1c0322fcda93249b96b4ed0bbd7f9912b2eb21bd8da6b43

image命令入口

func init(){
    ...
	imageCmd.AddCommand(imageCheckBaseCmd)
	imageCmd.AddCommand(imageCopyCmd)
	imageCmd.AddCommand(imageDeleteCmd)
	imageCmd.AddCommand(imageDigestCmd)
	imageCmd.AddCommand(imageExportCmd)
	imageCmd.AddCommand(imageImportCmd)
	imageCmd.AddCommand(imageInspectCmd)
	imageCmd.AddCommand(imageManifestCmd)
	imageCmd.AddCommand(imageModCmd)
	imageCmd.AddCommand(imageRateLimitCmd)
	rootCmd.AddCommand(imageCmd)
}    

---
var imageExportCmd = &cobra.Command{
	Use:   "export <image_ref> [filename]",
	Short: "export image",
	Long: `Exports an image into a tar file that can be later loaded into a docker
engine with "docker load". The tar file is output to stdout by default.
Example usage: regctl image export registry:5000/yourimg:v1 >yourimg-v1.tar`,
	Args:              cobra.RangeArgs(1, 2),
	ValidArgsFunction: completeArgTag,
    //命令调用的函数
	RunE:              runImageExport,
}


---
//这个导出函数封装的很棒
func runImageExport(cmd *cobra.Command, args []string) error {
	ctx := cmd.Context()
	r, err := ref.New(args[0])
	if err != nil {
		return err
	}
	var w io.Writer
	if len(args) == 2 {
		w, err = os.Create(args[1])
		if err != nil {
			return err
		}
	} else {
		w = os.Stdout
	}
	rc := newRegClient()
	defer rc.Close(ctx, r)
	log.WithFields(logrus.Fields{
		"ref": r.CommonName(),
	}).Debug("Image export")
	return rc.ImageExport(ctx, r, w)
}

分析下ref.New 干了啥,解析镜像ref

var (
	hostPartS = `(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)`
	// host with port allows a short name in addition to hostDomainS
	hostPortS = `(?:` + hostPartS + `(?:` + regexp.QuoteMeta(`.`) + hostPartS + `)*` + regexp.QuoteMeta(`.`) + `?` + regexp.QuoteMeta(`:`) + `[0-9]+)`
	// hostname may be ip, fqdn (example.com), or trailing dot (example.)
	hostDomainS = `(?:` + hostPartS + `(?:(?:` + regexp.QuoteMeta(`.`) + hostPartS + `)+` + regexp.QuoteMeta(`.`) + `?|` + regexp.QuoteMeta(`.`) + `))`
	hostUpperS  = `(?:[a-zA-Z0-9]*[A-Z][a-zA-Z0-9-]*[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[A-Z][a-zA-Z0-9]*)`
	registryS   = `(?:` + hostDomainS + `|` + hostPortS + `|` + hostUpperS + `|localhost(?:` + regexp.QuoteMeta(`:`) + `[0-9]+))`
	repoPartS   = `[a-z0-9]+(?:(?:[_.]|__|[-]*)[a-z0-9]+)*`
	pathS       = `[/a-zA-Z0-9_\-. ]+`
	tagS        = `[\w][\w.-]{0,127}`
	digestS     = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`
	refRE       = regexp.MustCompile(`^(?:(` + registryS + `)` + regexp.QuoteMeta(`/`) + `)?` +
		`(` + repoPartS + `(?:` + regexp.QuoteMeta(`/`) + repoPartS + `)*)` +
		`(?:` + regexp.QuoteMeta(`:`) + `(` + tagS + `))?` +
		`(?:` + regexp.QuoteMeta(`@`) + `(` + digestS + `))?$`)
	schemeRE = regexp.MustCompile(`^([a-z]+)://(.+)$`)
	pathRE   = regexp.MustCompile(`^(` + pathS + `)` +
		`(?:` + regexp.QuoteMeta(`:`) + `(` + tagS + `))?` +
		`(?:` + regexp.QuoteMeta(`@`) + `(` + digestS + `))?$`)
)



// New returns a reference based on the scheme, defaulting to a
func New(parse string) (Ref, error) {
	scheme := ""
	path := parse
    //schemeRE = regexp.MustCompile(`^([a-z]+)://(.+)$`)
    //FindStringSubmatch 返回一个字符串切片,其中包含 s 中正则表达式的最左侧匹配的
    //文本及其子表达式的匹配(如果有),
    //如包注释中的 'Submatch' 说明所定义。返回值 nil 表示不匹配。
	matchScheme := schemeRE.FindStringSubmatch(parse)
	if len(matchScheme) == 3 {
		scheme = matchScheme[1] //协议
		path = matchScheme[2] //路径
	}
	ret := Ref{
		Scheme:    scheme,
		Reference: parse,
	}
	switch scheme {
	case "":
		ret.Scheme = "reg"
		matchRef := refRE.FindStringSubmatch(path)
		if matchRef == nil || len(matchRef) < 5 {
			if refRE.FindStringSubmatch(strings.ToLower(path)) != nil {
                //repo必须是小写字符
				return Ref{}, fmt.Errorf("invalid reference \"%s\", repo must be lowercase", path)
			}
			return Ref{}, fmt.Errorf("invalid reference \"%s\"", path)
		}
		ret.Registry = matchRef[1] //仓库地址
		ret.Repository = matchRef[2]
		ret.Tag = matchRef[3]
		ret.Digest = matchRef[4] //签名

		// handle localhost use case since it matches the regex for a repo path entry
		repoPath := strings.Split(ret.Repository, "/")
		if ret.Registry == "" && repoPath[0] == "localhost" {
			ret.Registry = repoPath[0]
			ret.Repository = strings.Join(repoPath[1:], "/")
		}
		switch ret.Registry {
		case "", dockerRegistryDNS, dockerRegistryLegacy:
			ret.Registry = dockerRegistry
		}
		if ret.Registry == dockerRegistry && !strings.Contains(ret.Repository, "/") {
			ret.Repository = dockerLibrary + "/" + ret.Repository
		}
		if ret.Tag == "" && ret.Digest == "" {
			ret.Tag = "latest"
		}
		if ret.Repository == "" {
			return Ref{}, fmt.Errorf("invalid reference \"%s\"", path)
		}

	case "ocidir", "ocifile":
		matchPath := pathRE.FindStringSubmatch(path)
		if matchPath == nil || len(matchPath) < 2 || matchPath[1] == "" {
			return Ref{}, fmt.Errorf("invalid path for scheme \"%s\": %s", scheme, path)
		}
		ret.Path = matchPath[1]
		if len(matchPath) > 2 && matchPath[2] != "" {
			ret.Tag = matchPath[2]
		}
		if len(matchPath) > 3 && matchPath[3] != "" {
			ret.Digest = matchPath[3]
		}

	default:
		return Ref{}, fmt.Errorf("unhandled reference scheme \"%s\" in \"%s\"", scheme, parse)
	}
	return ret, nil
}

镜像导出

const (
	dockerManifestFilename = "manifest.json"
	ociLayoutVersion       = "1.0.0"
	ociIndexFilename       = "index.json"
	ociLayoutFilename      = "oci-layout"
	annotationRefName      = "org.opencontainers.image.ref.name"
	annotationImageName    = "io.containerd.image.name"
)


// ImageExport exports an image to an output stream.
// The format is compatible with "docker load" if a single image is selected and not a manifest list.
// The ref must include a tag for exporting to docker (defaults to latest), and may also include a digest.
// The export is also formatted according to OCI layout which supports multi-platform images.
// <https://github.com/opencontainers/image-spec/blob/master/image-layout.md>
// A tar file will be sent to outStream.
//
// Resulting filesystem:
// oci-layout: created at top level, can be done at the start
// index.json: created at top level, single descriptor with org.opencontainers.image.ref.name annotation pointing to the tag
// manifest.json: created at top level, based on every layer added, only works for a single arch image
// blobs/$algo/$hash: each content addressable object (manifest, config, or layer), created recursively
func (rc *RegClient) ImageExport(ctx context.Context, r ref.Ref, outStream io.Writer) error {
	var ociIndex v1.Index

	// create tar writer object
    //创建一个压缩流
	tw := tar.NewWriter(outStream)
	defer tw.Close()
	twd := &tarWriteData{
		tw:    tw,
		dirs:  map[string]bool{},
		files: map[string]bool{},
		mode:  0644,
	}

	// retrieve image manifest
    //获取镜像的manifest
	m, err := rc.ManifestGet(ctx, r)
	if err != nil {
		rc.log.WithFields(logrus.Fields{
			"ref": r.CommonName(),
			"err": err,
		}).Warn("Failed to get manifest")
		return err
	}

	// build/write oci-layout
	ociLayout := v1.ImageLayout{Version: ociLayoutVersion}
	err = twd.tarWriteFileJSON(ociLayoutFilename, ociLayout)
	if err != nil {
		return err
	}

	// create a manifest descriptor
	mDesc := m.GetDescriptor()
	if mDesc.Annotations == nil {
		mDesc.Annotations = map[string]string{}
	}
    //io.containerd.image.name
	mDesc.Annotations[annotationImageName] = r.CommonName()
    //org.opencontainers.image.ref.name
	mDesc.Annotations[annotationRefName] = r.Tag

	// generate/write an OCI index
	ociIndex.Versioned = v1.IndexSchemaVersion
	ociIndex.Manifests = []types.Descriptor{mDesc} // initialize with the descriptor to the manifest list
	//manifest信息列表写入到index.json
    err = twd.tarWriteFileJSON(ociIndexFilename, ociIndex)
	if err != nil {
		return err
	}

	// append to docker manifest with tag, config filename, each layer filename, and layer descriptors
	if mi, ok := m.(manifest.Imager); ok {
		conf, err := mi.GetConfig()
		if err != nil {
			return err
		}
		refTag := r.ToReg()
		if refTag.Digest != "" {
			refTag.Digest = ""
		}
		if refTag.Tag == "" {
			refTag.Tag = "latest"
		}
		dockerManifest := dockerTarManifest{
			RepoTags:     []string{refTag.CommonName()},
			Config:       tarOCILayoutDescPath(conf),
			Layers:       []string{},
			LayerSources: map[digest.Digest]types.Descriptor{},
		}
		dl, err := mi.GetLayers()
		if err != nil {
			return err
		}
		for _, d := range dl {
			dockerManifest.Layers = append(dockerManifest.Layers, tarOCILayoutDescPath(d))
			dockerManifest.LayerSources[d.Digest] = d
		}

		// marshal manifest and write manifest.json
		err = twd.tarWriteFileJSON(dockerManifestFilename, []dockerTarManifest{dockerManifest})
		if err != nil {
			return err
		}
	}

	// recursively include manifests and nested blobs
	err = rc.imageExportDescriptor(ctx, r, mDesc, twd)
	if err != nil {
		return err
	}

	return nil
}
文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0