mirror of
https://github.com/kubernetes-sigs/kind.git
synced 2025-12-01 07:26:05 +07:00
kind build node-image: Support Docker v25.0.1
`docker save` in Docker v25 produces Docker/OCI dual-format archives: - `repositories`, `manifest.json`: for legacy Docker format - `oci-layout`, `index.json` (and blobs): for OCI format However, `pkg/build/nodeimage/internal/container/docker.EditArchive` did not support rewriting OCI Index. This was resulting in producing broken images with Docker v25. We can just drop `docker.EditArchive` and use `ctr images tag` instead. See kubernetes/kubernetes issue 122894 NOTE: This is still incompatible with Docker v25.0.0 due to moby/moby issue 47150. The issue was fixed in v25.0.1. Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
@@ -18,7 +18,6 @@ package nodeimage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
@@ -185,13 +184,16 @@ func (c *buildContext) prePullImagesAndWriteManifests(bits kube.Bits, parsedVers
|
||||
// correct set of built tags using the same logic we will use to rewrite
|
||||
// the tags as we load the archives
|
||||
fixedImages := sets.NewString()
|
||||
fixedImagesMap := make(map[string]string, builtImages.Len()) // key: original images, value: fixed images
|
||||
for _, image := range builtImages.List() {
|
||||
registry, tag, err := docker.SplitImage(image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
registry = fixRepository(registry)
|
||||
fixedImages.Insert(registry + ":" + tag)
|
||||
fixedImage := registry + ":" + tag
|
||||
fixedImages.Insert(fixedImage)
|
||||
fixedImagesMap[image] = fixedImage
|
||||
}
|
||||
builtImages = fixedImages
|
||||
c.logger.V(1).Info("Detected built images: " + strings.Join(builtImages.List(), ", "))
|
||||
@@ -286,14 +288,8 @@ func (c *buildContext) prePullImagesAndWriteManifests(bits kube.Bits, parsedVers
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
//return importer.LoadCommand().SetStdout(os.Stdout).SetStderr(os.Stderr).SetStdin(f).Run()
|
||||
// we will rewrite / correct the tags as we load the image
|
||||
if err := exec.RunWithStdinWriter(importer.LoadCommand().SetStdout(os.Stdout).SetStderr(os.Stdout), func(w io.Writer) error {
|
||||
return docker.EditArchive(f, w, fixRepository, c.arch)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return importer.LoadCommand().SetStdout(os.Stdout).SetStderr(os.Stderr).SetStdin(f).Run()
|
||||
// we will rewrite / correct the tags in tagFns below
|
||||
})
|
||||
}
|
||||
|
||||
@@ -303,6 +299,23 @@ func (c *buildContext) prePullImagesAndWriteManifests(bits kube.Bits, parsedVers
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create a plan of image re-tagging
|
||||
tagFns := []func() error{}
|
||||
for unfixed, fixed := range fixedImagesMap {
|
||||
unfixed, fixed := unfixed, fixed // capture loop var
|
||||
if unfixed != fixed {
|
||||
tagFns = append(tagFns, func() error {
|
||||
return importer.Tag(unfixed, fixed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// run all image re-tragging concurrently until one fails or all succeed
|
||||
if err := errors.UntilErrorConcurrent(tagFns); err != nil {
|
||||
c.logger.Errorf("Image build Failed! Failed to re-tag images %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return importer.ListImported()
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,12 @@ func (c *containerdImporter) LoadCommand() exec.Cmd {
|
||||
)
|
||||
}
|
||||
|
||||
func (c *containerdImporter) Tag(src, target string) error {
|
||||
return c.containerCmder.Command(
|
||||
"ctr", "--namespace=k8s.io", "images", "tag", "--force", src, target,
|
||||
).Run()
|
||||
}
|
||||
|
||||
func (c *containerdImporter) ListImported() ([]string, error) {
|
||||
return exec.OutputLines(c.containerCmder.Command("ctr", "--namespace=k8s.io", "images", "list", "-q"))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kind/pkg/errors"
|
||||
)
|
||||
@@ -84,84 +83,6 @@ func GetArchiveTags(path string) ([]string, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// EditArchive applies edit to reader's image repositories,
|
||||
// IE the repository part of repository:tag in image tags
|
||||
// This supports v1 / v1.1 / v1.2 Docker Image Archives
|
||||
//
|
||||
// editRepositories should be a function that returns the input or an edited
|
||||
// form, where the input is the image repository
|
||||
//
|
||||
// https://github.com/moby/moby/blob/master/image/spec/v1.md
|
||||
// https://github.com/moby/moby/blob/master/image/spec/v1.1.md
|
||||
// https://github.com/moby/moby/blob/master/image/spec/v1.2.md
|
||||
func EditArchive(reader io.Reader, writer io.Writer, editRepositories func(string) string, architectureOverride string) error {
|
||||
tarReader := tar.NewReader(reader)
|
||||
tarWriter := tar.NewWriter(writer)
|
||||
// iterate all entries in the tarball
|
||||
for {
|
||||
// read an entry
|
||||
hdr, err := tarReader.Next()
|
||||
if err == io.EOF {
|
||||
return tarWriter.Close()
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := io.ReadAll(tarReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// edit the repostories and manifests files when we find them
|
||||
if hdr.Name == "repositories" {
|
||||
b, err = editRepositoriesFile(b, editRepositories)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Size = int64(len(b))
|
||||
} else if hdr.Name == "manifest.json" {
|
||||
b, err = editManifestRepositories(b, editRepositories)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Size = int64(len(b))
|
||||
// edit image config when we find that
|
||||
} else if strings.HasSuffix(hdr.Name, ".json") {
|
||||
if architectureOverride != "" {
|
||||
b, err = editConfigArchitecture(b, architectureOverride)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hdr.Size = int64(len(b))
|
||||
}
|
||||
}
|
||||
|
||||
// write to the output tarball
|
||||
if err := tarWriter.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(b) > 0 {
|
||||
if _, err := tarWriter.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* helpers */
|
||||
|
||||
func editConfigArchitecture(raw []byte, architectureOverride string) ([]byte, error) {
|
||||
var cfg map[string]interface{}
|
||||
if err := json.Unmarshal(raw, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
const architecture = "architecture"
|
||||
if _, ok := cfg[architecture]; !ok {
|
||||
return raw, nil
|
||||
}
|
||||
cfg[architecture] = architectureOverride
|
||||
return json.Marshal(cfg)
|
||||
}
|
||||
|
||||
// archiveRepositories represents repository:tag:ref
|
||||
//
|
||||
// https://github.com/moby/moby/blob/master/image/spec/v1.md
|
||||
@@ -169,20 +90,6 @@ func editConfigArchitecture(raw []byte, architectureOverride string) ([]byte, er
|
||||
// https://github.com/moby/moby/blob/master/image/spec/v1.2.md
|
||||
type archiveRepositories map[string]map[string]string
|
||||
|
||||
func editRepositoriesFile(raw []byte, editRepositories func(string) string) ([]byte, error) {
|
||||
tags, err := parseRepositories(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fixed := make(archiveRepositories)
|
||||
for repository, tagsToRefs := range tags {
|
||||
fixed[editRepositories(repository)] = tagsToRefs
|
||||
}
|
||||
|
||||
return json.Marshal(fixed)
|
||||
}
|
||||
|
||||
// https://github.com/moby/moby/blob/master/image/spec/v1.2.md#combined-image-json--filesystem-changeset-format
|
||||
type metadataEntry struct {
|
||||
Config string `json:"Config"`
|
||||
@@ -190,30 +97,6 @@ type metadataEntry struct {
|
||||
Layers []string `json:"Layers"`
|
||||
}
|
||||
|
||||
// applies
|
||||
func editManifestRepositories(raw []byte, editRepositories func(string) string) ([]byte, error) {
|
||||
var entries []metadataEntry
|
||||
if err := json.Unmarshal(raw, &entries); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, entry := range entries {
|
||||
fixed := make([]string, len(entry.RepoTags))
|
||||
for i, tag := range entry.RepoTags {
|
||||
parts := strings.Split(tag, ":")
|
||||
if len(parts) > 2 {
|
||||
return nil, fmt.Errorf("invalid repotag: %s", entry)
|
||||
}
|
||||
parts[0] = editRepositories(parts[0])
|
||||
fixed[i] = strings.Join(parts, ":")
|
||||
}
|
||||
|
||||
entries[i].RepoTags = fixed
|
||||
}
|
||||
|
||||
return json.Marshal(entries)
|
||||
}
|
||||
|
||||
// returns repository:tag:ref
|
||||
func parseRepositories(data []byte) (archiveRepositories, error) {
|
||||
var repoTags archiveRepositories
|
||||
|
||||
Reference in New Issue
Block a user