Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kind build node-image: Support Docker v25.0.1 #3487

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions pkg/build/nodeimage/buildcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package nodeimage

import (
"fmt"
"io"
"math/rand"
"os"
"path"
Expand Down Expand Up @@ -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(), ", "))
Expand Down Expand Up @@ -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
})
}

Expand All @@ -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()
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/build/nodeimage/imageimporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
}
117 changes: 0 additions & 117 deletions pkg/build/nodeimage/internal/container/docker/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"fmt"
"io"
"os"
"strings"

"sigs.k8s.io/kind/pkg/errors"
)
Expand Down Expand Up @@ -84,136 +83,20 @@ 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
// https://github.com/moby/moby/blob/master/image/spec/v1.1.md
// 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"`
RepoTags []string `json:"RepoTags"`
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
Expand Down
Loading