-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #235 from cert-manager/self-upgrade
[CI] Merge self-upgrade into main
- Loading branch information
Showing
10 changed files
with
428 additions
and
286 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
/* | ||
Copyright 2023 The cert-manager Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"archive/tar" | ||
"bytes" | ||
"io" | ||
"io/fs" | ||
"log/slog" | ||
"os" | ||
"path/filepath" | ||
|
||
v1 "github.com/google/go-containerregistry/pkg/v1" | ||
"github.com/google/go-containerregistry/pkg/v1/layout" | ||
"github.com/google/go-containerregistry/pkg/v1/match" | ||
"github.com/google/go-containerregistry/pkg/v1/mutate" | ||
"github.com/google/go-containerregistry/pkg/v1/tarball" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var CommandAppendLayers = cobra.Command{ | ||
Use: "append-layers oci-path [path-to-tarball...]", | ||
Short: "Appends a tarball or directory to every image in an OCI index.", | ||
Args: cobra.MinimumNArgs(1), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
oci := args[0] | ||
extra := args[1:] | ||
|
||
if len(extra) == 0 { | ||
return | ||
} | ||
|
||
layers := []v1.Layer{} | ||
for _, path := range extra { | ||
layers = append(layers, loadLayerFromDirOrTarball(path)) | ||
} | ||
|
||
appendLayersToAllImages(oci, layers...) | ||
}, | ||
} | ||
|
||
func loadLayerFromDirOrTarball(path string) v1.Layer { | ||
stat, err := os.Stat(path) | ||
must("could not open directory or tarball", err) | ||
|
||
var layer v1.Layer | ||
if stat.IsDir() { | ||
var buf bytes.Buffer | ||
|
||
tw := tar.NewWriter(&buf) | ||
|
||
filepath.Walk(path, func(target string, info fs.FileInfo, err error) error { | ||
must("walk error", err) | ||
|
||
header, err := tar.FileInfoHeader(info, info.Name()) | ||
must("could not create tar header", err) | ||
|
||
name, err := filepath.Rel(path, target) | ||
must("could not build relative path", err) | ||
|
||
// Write simplified header, this removes all fields that would cause | ||
// the build to be non-reproducible (like modtime for example) | ||
err = tw.WriteHeader(&tar.Header{ | ||
Typeflag: header.Typeflag, | ||
Name: name, | ||
Mode: header.Mode, | ||
Linkname: header.Linkname, | ||
Size: header.Size, | ||
}) | ||
|
||
must("could not write tar header", err) | ||
|
||
if !info.IsDir() { | ||
file, err := os.Open(target) | ||
must("could not write tar contents", err) | ||
|
||
defer file.Close() | ||
|
||
_, err = io.Copy(tw, file) | ||
must("could not write tar contents", err) | ||
} | ||
|
||
return nil | ||
}) | ||
|
||
tw.Close() | ||
|
||
byts := buf.Bytes() | ||
|
||
layer, err = tarball.LayerFromOpener(func() (io.ReadCloser, error) { | ||
return io.NopCloser(bytes.NewReader(byts)), nil | ||
}) | ||
|
||
} else { | ||
layer, err = tarball.LayerFromFile(path) | ||
} | ||
|
||
must("could not open directory or tarball", err) | ||
return layer | ||
} | ||
|
||
func appendLayersToAllImages(oci string, layers ...v1.Layer) { | ||
path, err := layout.FromPath(oci) | ||
must("could not load oci directory", err) | ||
|
||
index, err := path.ImageIndex() | ||
must("could not load oci image index", err) | ||
|
||
index = appendLayersToImageIndex(index, layers) | ||
|
||
_, err = layout.Write(oci, index) | ||
must("could not write image", err) | ||
} | ||
|
||
func appendLayersToImageIndex(index v1.ImageIndex, layers []v1.Layer) v1.ImageIndex { | ||
manifest, err := index.IndexManifest() | ||
must("could not load oci image manifest", err) | ||
|
||
for _, descriptor := range manifest.Manifests { | ||
switch { | ||
case descriptor.MediaType.IsImage(): | ||
slog.Info("found image", "digest", descriptor.Digest, "platform", descriptor.Platform) | ||
|
||
img, err := index.Image(descriptor.Digest) | ||
must("could not load oci image with digest", err) | ||
|
||
img, err = mutate.AppendLayers(img, layers...) | ||
must("could not load append layer to image", err) | ||
|
||
digest, err := img.Digest() | ||
must("could not get image digest", err) | ||
|
||
slog.Info("appended layers to image", "old_digest", descriptor.Digest, "digest", digest, "platform", descriptor.Platform) | ||
|
||
index = mutate.RemoveManifests(index, match.Digests(descriptor.Digest)) | ||
|
||
descriptor.Digest = digest | ||
index = mutate.AppendManifests(index, mutate.IndexAddendum{ | ||
Add: img, | ||
Descriptor: descriptor, | ||
}) | ||
|
||
case descriptor.MediaType.IsIndex(): | ||
slog.Info("found image index", "digest", descriptor.Digest) | ||
|
||
child, err := index.ImageIndex(descriptor.Digest) | ||
must("could not load oci image manifest", err) | ||
|
||
child = appendLayersToImageIndex(child, layers) | ||
|
||
digest, err := child.Digest() | ||
must("could not get image digest", err) | ||
|
||
index = mutate.RemoveManifests(index, match.Digests(descriptor.Digest)) | ||
|
||
descriptor.Digest = digest | ||
index = mutate.AppendManifests(index, mutate.IndexAddendum{ | ||
Add: child, | ||
Descriptor: descriptor, | ||
}) | ||
} | ||
} | ||
|
||
return index | ||
} |
97 changes: 97 additions & 0 deletions
97
make/_shared/oci-build/image_tool/convert_to_docker_tar.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
Copyright 2023 The cert-manager Authors. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"runtime" | ||
|
||
"github.com/google/go-containerregistry/pkg/name" | ||
v1 "github.com/google/go-containerregistry/pkg/v1" | ||
"github.com/google/go-containerregistry/pkg/v1/layout" | ||
"github.com/google/go-containerregistry/pkg/v1/match" | ||
"github.com/google/go-containerregistry/pkg/v1/tarball" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var CommandConvertToDockerTar = cobra.Command{ | ||
Use: "convert-to-docker-tar oci-path output image-name", | ||
Short: "Reads the OCI directory and outputs a tarball that is compatible with \"docker load\"", | ||
Args: cobra.ExactArgs(3), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
path := args[0] | ||
output := args[1] | ||
imageName := args[2] | ||
|
||
ociLayout, err := layout.FromPath(path) | ||
must("could not load oci directory", err) | ||
|
||
index, err := ociLayout.ImageIndex() | ||
must("could not load oci image index", err) | ||
|
||
images := getImagesFromIndex(index, func(desc v1.Descriptor) bool { | ||
return desc.Platform != nil && desc.Platform.Architecture == runtime.GOARCH | ||
}) | ||
|
||
switch { | ||
case len(images) == 0: | ||
fail("no matching images found") | ||
case len(images) > 1: | ||
fail("multiple matching images found") | ||
} | ||
|
||
ref, err := name.ParseReference(imageName) | ||
must("invalid image name", err) | ||
|
||
err = tarball.WriteToFile(output, ref, images[0]) | ||
must("could not write tarball", err) | ||
}, | ||
} | ||
|
||
func getImagesFromIndex(index v1.ImageIndex, matcher match.Matcher) (images []v1.Image) { | ||
manifest, err := index.IndexManifest() | ||
must("could not load oci index manifest", err) | ||
|
||
for _, descriptor := range manifest.Manifests { | ||
switch { | ||
case descriptor.MediaType.IsImage(): | ||
// If the platform is not part of the index manifest, attempt to | ||
// load it from the image config | ||
if descriptor.Platform == nil { | ||
img, err := index.Image(descriptor.Digest) | ||
must("could not load image", err) | ||
|
||
cfg, err := img.ConfigFile() | ||
must("could not load image config", err) | ||
|
||
descriptor.Platform = cfg.Platform() | ||
} | ||
|
||
if matcher(descriptor) { | ||
img, err := index.Image(descriptor.Digest) | ||
must("could not load image", err) | ||
images = append(images, img) | ||
} | ||
|
||
case descriptor.MediaType.IsIndex(): | ||
idx, err := index.ImageIndex(descriptor.Digest) | ||
must("could not load image index", err) | ||
images = append(images, getImagesFromIndex(idx, matcher)...) | ||
} | ||
} | ||
|
||
return | ||
} |
Oops, something went wrong.