Skip to content
This repository has been archived by the owner on Jan 19, 2023. It is now read-only.

Show container image manifest for pods #2695

Closed
wants to merge 6 commits into from
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .github/workflows/verify-generated.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
uses: actions/setup-go@v2
with:
go-version: 1.16.x
- name: Install libraries
run: sudo apt-get update && sudo apt install -y libgpgme-dev libassuan-dev libdevmapper-dev libbtrfs-dev
- name: Checkout code
uses: actions/checkout@v2
- name: Verify generated code
Expand Down
11 changes: 6 additions & 5 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var (
BUILD_TIME = time.Now().UTC().Format(time.RFC3339)
LD_FLAGS = fmt.Sprintf("-X \"main.buildTime=%s\" -X main.gitCommit=%s", BUILD_TIME, GIT_COMMIT)
GO_FLAGS = fmt.Sprintf("-ldflags=%s", LD_FLAGS)
IMAGE_FLAGS = "exclude_graphdriver_devicemapper exclude_graphdriver_btrfs containers_image_openpgp"
)

func main() {
Expand Down Expand Up @@ -262,7 +263,7 @@ func goInstall() {

func generate() {
removeFakes()
runCmd("go", nil, "generate", "-v", "./pkg/...", "./internal/...")
runCmd("go", nil, "generate", "-tags", IMAGE_FLAGS, "-v", "./pkg/...", "./internal/...")
}

func build() {
Expand All @@ -273,7 +274,7 @@ func build() {
if runtime.GOOS == "windows" {
artifact = "octant.exe"
}
runCmd("go", nil, "build", "-tags", "embedded", "-mod=vendor", "-o", "build/"+artifact, GO_FLAGS, "-v", "./cmd/octant")
runCmd("go", nil, "build", "-tags", "embedded " + IMAGE_FLAGS, "-mod=vendor", "-o", "build/"+artifact, GO_FLAGS, "-v", "./cmd/octant")
}

// buildElectron builds an Octant binary without web assets
Expand All @@ -294,7 +295,7 @@ func buildElectron() {
if runtime.GOOS == "windows" {
artifact = "octant.exe"
}
runCmd("go", nil, "build", "-mod=vendor", "-o", "web/extraResources/"+artifact, GO_FLAGS, "-v", "./cmd/octant")
runCmd("go", nil, "build", "-tags", IMAGE_FLAGS, "-mod=vendor", "-o", "web/extraResources/"+artifact, GO_FLAGS, "-v", "./cmd/octant")
}

func runDev() {
Expand All @@ -307,11 +308,11 @@ func runDev() {
}

func test() {
runCmd("go", nil, "test", "-v", "./internal/...", "./pkg/...")
runCmd("go", nil, "test", "-tags", IMAGE_FLAGS, "-v", "./internal/...", "./pkg/...")
}

func vet() {
runCmd("go", nil, "vet", "./internal/...", "./pkg/...")
runCmd("go", nil, "vet", "-tags", IMAGE_FLAGS, "./internal/...", "./pkg/...")
goFmt(false)
}

Expand Down
1 change: 1 addition & 0 deletions changelogs/unreleased/156-mklanjsek
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Show container image manifest for pods
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.16

require (
contrib.go.opencensus.io/exporter/jaeger v0.2.1
github.com/containers/image/v5 v5.14.0
github.com/davecgh/go-spew v1.1.1
github.com/dlclark/regexp2 v1.2.0 // indirect
github.com/dop251/goja v0.0.0-20200629185240-bfd59704b500
Expand All @@ -28,7 +29,6 @@ require (
github.com/hashicorp/golang-lru v0.5.4
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/iancoleman/strcase v0.2.0
github.com/imdario/mergo v0.3.11 // indirect
github.com/json-iterator/go v1.1.11
github.com/onsi/ginkgo v1.14.0 // indirect
github.com/pkg/errors v0.9.1
Expand Down
447 changes: 443 additions & 4 deletions go.sum

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions internal/printer/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"context"
"fmt"

"github.com/vmware-tanzu/octant/internal/api"
"github.com/vmware-tanzu/octant/internal/util/json"

"github.com/vmware-tanzu/octant/internal/log"
Expand Down Expand Up @@ -97,6 +98,8 @@ func (cc *ContainerConfiguration) Create() (*component.Summary, error) {
}

var containerStatus *corev1.ContainerStatus
var hostOS = "linux"

if pod, ok := cc.parent.(*corev1.Pod); ok {
var err error
containerStatus, err = findContainerStatus(pod, cc.container.Name, cc.isInit)
Expand All @@ -113,6 +116,9 @@ func (cc *ContainerConfiguration) Create() (*component.Summary, error) {
title = "Ephemeral Container"
}
}
if api.IsWindowsContainer(pod) {
hostOS = "windows"
}
}

sections := component.SummarySections{}
Expand All @@ -122,6 +128,14 @@ func (cc *ContainerConfiguration) Create() (*component.Summary, error) {
sections.AddText("Image ID", containerStatus.ImageID)
}

manifest, configuration, err := ManifestManager.GetImageManifest(cc.context, hostOS, c.Image)
if err == nil {
sections.Add("Image Manifest", component.NewJSONEditor(manifest, true))
sections.Add("Image Configuration", component.NewJSONEditor(configuration, true))
} else {
sections.Add("Image Manifest", component.NewText(fmt.Sprintf("Unable to load image manifest %s", err)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error shown in the UI is hard to read when all the errors from the error chain are concatenated together.
An easy way to reproduce this is to kill the internet before caching and see the error in the pod summary.
It shows something like:
Unable to load image manifest error creating image source for image docker://nginx:1.14.2: pinging container registry registry-1.docker.io: Get "https://registry-1.docker.io/v2/": dial tcp: lookup registry-1.docker.io: no such host.
The error from https://github.com/vmware-tanzu/octant/pull/2695/files#diff-85fedc5e144e89139dc0296c0fb2574d2576d493b8bad648bd79c31d32dbd73bR63 and others in the manifest.go and any subsequent errors can be formatted a little better with some delimiters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not a huge blocker and can be made afterwards.

}

hostPorts := describeContainerHostPorts(c.Ports)
if hostPorts != "" {
sections.AddText("Host Ports", hostPorts)
Expand Down
16 changes: 16 additions & 0 deletions internal/printer/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ func Test_ContainerConfiguration(t *testing.T) {
Header: "Image ID",
Content: component.NewText("nginx-image-id"),
},
{
Header: "Image Manifest",
Content: component.NewJSONEditor("{\"manifests\":[{\"digest\":\"sha256:e770165fef9e36b990882a4083d8ccf5e29e469a8609bb6b2e3b47d9510e2c8d\",\"mediaType\":\"application\\/vnd.docker.distribution.manifest.v2+json\",\"platform\":{\"architecture\":\"amd64\",\"os\":\"linux\"},\"size\":948},{\"digest\":\"sha256:26687467368eba1745b3af5f673156e5598b0d3609ddc041d4afb3000a7c97c4\",\"mediaType\":\"application\\/vnd.docker.distribution.manifest.v2+json\",\"platform\":{\"architecture\":\"arm\",\"os\":\"linux\",\"variant\":\"v7\"},\"size\":948},{\"digest\":\"sha256:322d209ca0e9dcd69cf1bb9354cb2c573255e96689f31b0964753389b780269c\",\"mediaType\":\"application\\/vnd.docker.distribution.manifest.v2+json\",\"platform\":{\"architecture\":\"arm64\",\"os\":\"linux\",\"variant\":\"v8\"},\"size\":948},{\"digest\":\"sha256:2393dbb3ac0f27a4b097908f78510aa20dce07c029540762447ab4731119bab7\",\"mediaType\":\"application\\/vnd.docker.distribution.manifest.v2+json\",\"platform\":{\"architecture\":\"386\",\"os\":\"linux\"},\"size\":948},{\"digest\":\"sha256:16f53d8a8fcef518bfc7ad0b87f572c036eedc5307a2539e4c73741a7fe8ea76\",\"mediaType\":\"application\\/vnd.docker.distribution.manifest.v2+json\",\"platform\":{\"architecture\":\"ppc64le\",\"os\":\"linux\"},\"size\":948},{\"digest\":\"sha256:a89d88340baf686e95076902c5f89bd54755cbb324eaae5a2a470f98db342f55\",\"mediaType\":\"application\\/vnd.docker.distribution.manifest.v2+json\",\"platform\":{\"architecture\":\"s390x\",\"os\":\"linux\"},\"size\":948}],\"mediaType\":\"application\\/vnd.docker.distribution.manifest.list.v2+json\",\"schemaVersion\":2}", true),
},
{
Header: "Image Configuration",
Content: component.NewJSONEditor("{\n \"created\": \"2019-05-08T03:01:41.947151778Z\",\n \"architecture\": \"amd64\",\n \"os\": \"linux\",\n \"config\": {\n \"ExposedPorts\": {\n \"80/tcp\": {}\n },\n \"Env\": [\n \"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\",\n \"NGINX_VERSION=1.15.12-1~stretch\",\n \"NJS_VERSION=1.15.12.0.3.1-1~stretch\"\n ],\n \"Cmd\": [\n \"nginx\",\n \"-g\",\n \"daemon off;\"\n ],\n \"Labels\": {\n \"maintainer\": \"NGINX Docker Maintainers <docker-maint@nginx.com>\"\n },\n \"StopSignal\": \"SIGTERM\"\n },\n \"rootfs\": {\n \"type\": \"layers\",\n \"diff_ids\": [\n \"sha256:6270adb5794c6987109e54af00ab456977c5d5cc6f1bc52c1ce58d32ec0f15f4\",\n \"sha256:6ba094226eea86e21761829b88bdfdc9feb14bd83d60fb7e666f0943253657e8\",\n \"sha256:332fa54c58864e2dcd3df0ad88c69b2707d45f2d8121dad6278a15148900e490\"\n ]\n },\n \"history\": [\n {\n \"created\": \"2019-05-08T00:33:32.152758355Z\",\n \"created_by\": \"/bin/sh -c #(nop) ADD file:fcb9328ea4c1156709f3d04c3d9a5f3667e77fb36a4a83390ae2495555fc0238 in / \"\n },\n {\n \"created\": \"2019-05-08T00:33:32.718284983Z\",\n \"created_by\": \"/bin/sh -c #(nop) CMD [\\\"bash\\\"]\",\n \"empty_layer\": true\n },\n {\n \"created\": \"2019-05-08T03:01:16.010671568Z\",\n \"created_by\": \"/bin/sh -c #(nop) LABEL maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>\",\n \"empty_layer\": true\n },\n {\n \"created\": \"2019-05-08T03:01:16.175452264Z\",\n \"created_by\": \"/bin/sh -c #(nop) ENV NGINX_VERSION=1.15.12-1~stretch\",\n \"empty_layer\": true\n },\n {\n \"created\": \"2019-05-08T03:01:16.36342084Z\",\n \"created_by\": \"/bin/sh -c #(nop) ENV NJS_VERSION=1.15.12.0.3.1-1~stretch\",\n \"empty_layer\": true\n },\n {\n \"created\": \"2019-05-08T03:01:40.497446007Z\",\n \"created_by\": \"/bin/sh -c set -x \\t&& apt-get update \\t&& apt-get install --no-install-recommends --no-install-suggests -y gnupg1 apt-transport-https ca-certificates \\t&& \\tNGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \\tfound=''; \\tfor server in \\t\\tha.pool.sks-keyservers.net \\t\\thkp://keyserver.ubuntu.com:80 \\t\\thkp://p80.pool.sks-keyservers.net:80 \\t\\tpgp.mit.edu \\t; do \\t\\techo \\\"Fetching GPG key $NGINX_GPGKEY from $server\\\"; \\t\\tapt-key adv --keyserver \\\"$server\\\" --keyserver-options timeout=10 --recv-keys \\\"$NGINX_GPGKEY\\\" && found=yes && break; \\tdone; \\ttest -z \\\"$found\\\" && echo >&2 \\\"error: failed to fetch GPG key $NGINX_GPGKEY\\\" && exit 1; \\tapt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \\t&& dpkgArch=\\\"$(dpkg --print-architecture)\\\" \\t&& nginxPackages=\\\" \\t\\tnginx=${NGINX_VERSION} \\t\\tnginx-module-xslt=${NGINX_VERSION} \\t\\tnginx-module-geoip=${NGINX_VERSION} \\t\\tnginx-module-image-filter=${NGINX_VERSION} \\t\\tnginx-module-njs=${NJS_VERSION} \\t\\\" \\t&& case \\\"$dpkgArch\\\" in \\t\\tamd64|i386) \\t\\t\\techo \\\"deb https://nginx.org/packages/mainline/debian/ stretch nginx\\\" >> /etc/apt/sources.list.d/nginx.list \\t\\t\\t&& apt-get update \\t\\t\\t;; \\t\\t*) \\t\\t\\techo \\\"deb-src https://nginx.org/packages/mainline/debian/ stretch nginx\\\" >> /etc/apt/sources.list.d/nginx.list \\t\\t\\t\\t\\t\\t&& tempDir=\\\"$(mktemp -d)\\\" \\t\\t\\t&& chmod 777 \\\"$tempDir\\\" \\t\\t\\t\\t\\t\\t&& savedAptMark=\\\"$(apt-mark showmanual)\\\" \\t\\t\\t\\t\\t\\t&& apt-get update \\t\\t\\t&& apt-get build-dep -y $nginxPackages \\t\\t\\t&& ( \\t\\t\\t\\tcd \\\"$tempDir\\\" \\t\\t\\t\\t&& DEB_BUILD_OPTIONS=\\\"nocheck parallel=$(nproc)\\\" \\t\\t\\t\\t\\tapt-get source --compile $nginxPackages \\t\\t\\t) \\t\\t\\t\\t\\t\\t&& apt-mark showmanual | xargs apt-mark auto > /dev/null \\t\\t\\t&& { [ -z \\\"$savedAptMark\\\" ] || apt-mark manual $savedAptMark; } \\t\\t\\t\\t\\t\\t&& ls -lAFh \\\"$tempDir\\\" \\t\\t\\t&& ( cd \\\"$tempDir\\\" && dpkg-scanpackages . > Packages ) \\t\\t\\t&& grep '^Package: ' \\\"$tempDir/Packages\\\" \\t\\t\\t&& echo \\\"deb [ trusted=yes ] file://$tempDir ./\\\" > /etc/apt/sources.list.d/temp.list \\t\\t\\t&& apt-get -o Acquire::GzipIndexes=false update \\t\\t\\t;; \\tesac \\t\\t&& apt-get install --no-install-recommends --no-install-suggests -y \\t\\t\\t\\t\\t\\t$nginxPackages \\t\\t\\t\\t\\t\\tgettext-base \\t&& apt-get remove --purge --auto-remove -y apt-transport-https ca-certificates && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \\t\\t&& if [ -n \\\"$tempDir\\\" ]; then \\t\\tapt-get purge -y --auto-remove \\t\\t&& rm -rf \\\"$tempDir\\\" /etc/apt/sources.list.d/temp.list; \\tfi\"\n },\n {\n \"created\": \"2019-05-08T03:01:41.355881721Z\",\n \"created_by\": \"/bin/sh -c ln -sf /dev/stdout /var/log/nginx/access.log \\t&& ln -sf /dev/stderr /var/log/nginx/error.log\"\n },\n {\n \"created\": \"2019-05-08T03:01:41.538214273Z\",\n \"created_by\": \"/bin/sh -c #(nop) EXPOSE 80\",\n \"empty_layer\": true\n },\n {\n \"created\": \"2019-05-08T03:01:41.740886057Z\",\n \"created_by\": \"/bin/sh -c #(nop) STOPSIGNAL SIGTERM\",\n \"empty_layer\": true\n },\n {\n \"created\": \"2019-05-08T03:01:41.947151778Z\",\n \"created_by\": \"/bin/sh -c #(nop) CMD [\\\"nginx\\\" \\\"-g\\\" \\\"daemon off;\\\"]\",\n \"empty_layer\": true\n }\n ]\n}", true),
},
{
Header: "Host Ports",
Content: component.NewText("80/TCP, 8080/TCP"),
Expand Down Expand Up @@ -262,6 +270,14 @@ func Test_ContainerConfiguration(t *testing.T) {
Header: "Image ID",
Content: component.NewText("busybox-image-id"),
},
{
Header: "Image Manifest",
Content: component.NewJSONEditor("{\n \"schemaVersion\": 2,\n \"mediaType\": \"application/vnd.docker.distribution.manifest.list.v2+json\",\n \"manifests\": [\n {\n \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n \"size\": 527,\n \"digest\": \"sha256:74f634b1bc1bd74535d5209589734efbd44a25f4e2dc96d78784576a3eb5b335\",\n \"platform\": {\n \"architecture\": \"amd64\",\n \"os\": \"linux\"\n }\n },\n {\n \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n \"size\": 527,\n \"digest\": \"sha256:35e28b647bd4976b7cacfaa32b7b253817d0881d77b6cda731ad46a29d08c2cb\",\n \"platform\": {\n \"architecture\": \"arm\",\n \"os\": \"linux\",\n \"variant\": \"v5\"\n }\n },\n {\n \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n \"size\": 527,\n \"digest\": \"sha256:420befcb0c197618f0252108d553d8a112e291e2a6a75d8a2b4933f511480ea3\",\n \"platform\": {\n \"architecture\": \"arm\",\n \"os\": \"linux\",\n \"variant\": \"v6\"\n }\n },\n {\n \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n \"size\": 527,\n \"digest\": \"sha256:4df1e7dbe58b7fe24145291700e4fdf89a80677ffeb9b972840b42e3ec065e1f\",\n \"platform\": {\n \"architecture\": \"arm\",\n \"os\": \"linux\",\n \"variant\": \"v7\"\n }\n },\n {\n \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n \"size\": 527,\n \"digest\": \"sha256:859d41e4316c182cb559f9ae3c5ffcac8602ee1179794a1707c06cd092a008d3\",\n \"platform\": {\n \"architecture\": \"arm64\",\n \"os\": \"linux\",\n \"variant\": \"v8\"\n }\n },\n {\n \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n \"size\": 527,\n \"digest\": \"sha256:19f468f7dde9dc85d1576e6eb244b190661764199e21fcb53d84378bef16e334\",\n \"platform\": {\n \"architecture\": \"386\",\n \"os\": \"linux\"\n }\n },\n {\n \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n \"size\": 528,\n \"digest\": \"sha256:2d8967e4a68583a4bb2d7e236c60a1d72a585439b41e7a77555edad8df0f2bf4\",\n \"platform\": {\n \"architecture\": \"ppc64le\",\n \"os\": \"linux\"\n }\n },\n {\n \"mediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n \"size\": 528,\n \"digest\": \"sha256:67510360fd7c837d71ecfbd9f7991d72a2d2cbda3b383115a0dda0f0936b57f6\",\n \"platform\": {\n \"architecture\": \"s390x\",\n \"os\": \"linux\"\n }\n }\n ]\n}", true),
},
{
Header: "Image Configuration",
Content: component.NewJSONEditor("{\n \"created\": \"2018-05-23T21:19:31.132152818Z\",\n \"architecture\": \"amd64\",\n \"os\": \"linux\",\n \"config\": {\n \"Env\": [\n \"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"\n ],\n \"Cmd\": [\n \"sh\"\n ]\n },\n \"rootfs\": {\n \"type\": \"layers\",\n \"diff_ids\": [\n \"sha256:432b65032b9466b4dadcc5c7b11701e71d21c18400aae946b101ad16be62333a\"\n ]\n },\n \"history\": [\n {\n \"created\": \"2018-05-23T21:19:30.902651601Z\",\n \"created_by\": \"/bin/sh -c #(nop) ADD file:5f0439d8328ab58c087cd067c91ce92765da98916d91b083df6590477b7b9f19 in / \"\n },\n {\n \"created\": \"2018-05-23T21:19:31.132152818Z\",\n \"created_by\": \"/bin/sh -c #(nop) CMD [\\\"sh\\\"]\",\n \"empty_layer\": true\n }\n ]\n}", true),
},
{
Header: "Command",
Content: component.NewText("['sh']"),
Expand Down
89 changes: 89 additions & 0 deletions internal/printer/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package printer

import (
context "context"
"fmt"
"strings"
"sync"

"github.com/containers/image/v5/image"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"

"github.com/vmware-tanzu/octant/internal/util/json"
)

type ImageManifest struct {
Manifest string
Configuration string
}

type ImageEntry struct {
ImageName string
HostOS string
}

type ManifestConfiguration struct {
imageCache map[ImageEntry]ImageManifest
imageLock sync.Mutex
}

var (
ManifestManager = *NewManifestConfiguration()
)

func NewManifestConfiguration() *ManifestConfiguration {
mc := &ManifestConfiguration{}
return mc
}

func (manifest *ManifestConfiguration) GetImageManifest(ctx context.Context, hostOS, imageName string) (string, string, error) {
parts := strings.SplitN(imageName, "://", 2) // if format not specified, assume docker
if len(parts) != 2 {
imageName = "docker://" + imageName
}

imageEntry := ImageEntry{ImageName: imageName, HostOS: hostOS}
if _, ok := manifest.imageCache[imageEntry]; ok {
return manifest.imageCache[imageEntry].Manifest, manifest.imageCache[imageEntry].Configuration, nil
}

manifest.imageLock.Lock()
defer manifest.imageLock.Unlock()

srcRef, err := alltransports.ParseImageName(imageName)
if err != nil {
return "", "", fmt.Errorf("error parsing image name for image %s: %w", imageName, err)
}

systemCtx := &types.SystemContext{OSChoice: hostOS}

imageSrc, err := srcRef.NewImageSource(ctx, systemCtx)
if err != nil {
return "", "", fmt.Errorf("error creating image source for image %s: %w", imageName, err)
}

rawManifest, _, err := imageSrc.GetManifest(ctx, nil)
if err != nil {
return "", "", fmt.Errorf("error getting manifest for for image %s: %w", imageName, err)
}

image, err := image.FromUnparsedImage(ctx, systemCtx, image.UnparsedInstance(imageSrc, nil))
if err != nil {
return "", "", fmt.Errorf("error parsing manifest for for image %s: %w", imageName, err)
}

rawConfiguration, err := image.OCIConfig(ctx)
if err != nil {
return "", "", fmt.Errorf("error getting image config blob for for image %s: %w", imageName, err)
}

configOutput, err := json.MarshalIndent(rawConfiguration, "", " ")

if manifest.imageCache == nil {
manifest.imageCache = make(map[ImageEntry]ImageManifest)
}
manifest.imageCache[imageEntry] = ImageManifest{string(rawManifest), string(configOutput)}

return string(rawManifest), string(configOutput), nil
}
Loading