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

Commit

Permalink
Show container image manifest for pods (#2810)
Browse files Browse the repository at this point in the history
* Show container image manifest for pods

Signed-off-by: Milan Klanjsek <[email protected]>

Signed-off-by: Milan Klanjsek <[email protected]>

* Add image related tags to go command lane tools

Signed-off-by: Milan Klanjsek <[email protected]>

* Specify host os for image inspection

Signed-off-by: Milan Klanjsek <[email protected]>

* Update go modules

Signed-off-by: Milan Klanjsek <[email protected]>

* Install libraries for verify generated code step

Signed-off-by: Milan Klanjsek <[email protected]>

* Fix mock files

Signed-off-by: Milan Klanjsek <[email protected]>

* fix go mod conflicts

Signed-off-by: Wayne Witzel III <[email protected]>

Co-authored-by: Milan Klanjsek <[email protected]>
  • Loading branch information
wwitzel3 and mklanjsek committed Aug 31, 2021
1 parent c8fb303 commit 376a7d4
Show file tree
Hide file tree
Showing 1,464 changed files with 229,933 additions and 26 deletions.
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
445 changes: 442 additions & 3 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)))
}

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 <[email protected]>\"\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 <[email protected]>\",\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:https://keyserver.ubuntu.com:80 \\t\\thkp:https://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:https://$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, ":https://", 2) // if format not specified, assume docker
if len(parts) != 2 {
imageName = "docker:https://" + 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

0 comments on commit 376a7d4

Please sign in to comment.