diff --git a/cmd/octant-sample-plugin/main.go b/cmd/octant-sample-plugin/main.go index c4ef5360b2..f6232b68bf 100644 --- a/cmd/octant-sample-plugin/main.go +++ b/cmd/octant-sample-plugin/main.go @@ -64,8 +64,8 @@ func main() { logger.Info("octant-sample-plugin is starting, with logger helper") service.SetupPluginLogger(service.Info) // OUTPUT: 2021-08-23T11:35:02.714-0500 INFO octant-sample-plugin plugin/logger.go:43 octant-sample-plugin is starting, with logger helper {"timestamp": "2021-08-23T11:35:02.714-0500"} - log.Println("octant-sample-plugin is starting, with with prefix logger") - // OUTPUT: 2021-08-23T11:35:02.714-0500 INFO octant-sample-plugin plugin/logger.go:43 [INFO] 2021/08/23 11:35:02 octant-sample-plugin is starting, with with prefix logger + log.Println("octant-sample-plugin is starting with prefix logger") + // OUTPUT: 2021-08-23T11:35:02.714-0500 INFO octant-sample-plugin plugin/logger.go:43 [INFO] 2021/08/23 11:35:02 octant-sample-plugin is starting with prefix logger p.Serve() } diff --git a/internal/printer/manifest.go b/internal/manifest/manifest.go similarity index 65% rename from internal/printer/manifest.go rename to internal/manifest/manifest.go index 9d61629d43..595992225e 100644 --- a/internal/printer/manifest.go +++ b/internal/manifest/manifest.go @@ -1,12 +1,12 @@ -package printer +package manifest import ( - context "context" + "context" "fmt" "strings" "sync" - "github.com/containers/image/v5/image" + imagev5 "github.com/containers/image/v5/image" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" @@ -33,15 +33,32 @@ var ( ) func NewManifestConfiguration() *ManifestConfiguration { - mc := &ManifestConfiguration{} + mc := &ManifestConfiguration{ + imageCache: make(map[ImageEntry]ImageManifest), + } return mc } -func (manifest *ManifestConfiguration) GetImageManifest(ctx context.Context, hostOS, imageName string) (string, string, error) { +func (manifest *ManifestConfiguration) SetManifest(imageEntry ImageEntry, imageManifest ImageManifest) { + manifest.imageCache[imageEntry] = imageManifest +} + +func (manifest *ManifestConfiguration) HasEntry(hostOS, imageName string) bool { + imageName = parseName(imageName) + _, ok := manifest.imageCache[ImageEntry{ImageName: imageName, HostOS: hostOS}] + return ok +} + +func parseName(imageName string) string { parts := strings.SplitN(imageName, "://", 2) // if format not specified, assume docker if len(parts) != 2 { imageName = "docker://" + imageName } + return imageName +} + +func (manifest *ManifestConfiguration) GetImageManifest(ctx context.Context, hostOS, imageName string) (string, string, error) { + imageName = parseName(imageName) imageEntry := ImageEntry{ImageName: imageName, HostOS: hostOS} if _, ok := manifest.imageCache[imageEntry]; ok { @@ -62,28 +79,28 @@ func (manifest *ManifestConfiguration) GetImageManifest(ctx context.Context, hos if err != nil { return "", "", fmt.Errorf("error creating image source for image %s: %w", imageName, err) } + defer func() { + err = imageSrc.Close() + }() rawManifest, _, err := imageSrc.GetManifest(ctx, nil) if err != nil { - return "", "", fmt.Errorf("error getting manifest for for image %s: %w", imageName, err) + return "", "", fmt.Errorf("error getting manifest for image %s: %w", imageName, err) } - image, err := image.FromUnparsedImage(ctx, systemCtx, image.UnparsedInstance(imageSrc, nil)) + image, err := imagev5.FromUnparsedImage(ctx, systemCtx, imagev5.UnparsedInstance(imageSrc, nil)) if err != nil { - return "", "", fmt.Errorf("error parsing manifest for for image %s: %w", imageName, err) + return "", "", fmt.Errorf("error parsing manifest 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) + return "", "", fmt.Errorf("error getting image config blob 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 + return string(rawManifest), string(configOutput), err } diff --git a/internal/printer/manifest_test.go b/internal/manifest/manifest_test.go similarity index 86% rename from internal/printer/manifest_test.go rename to internal/manifest/manifest_test.go index 581084f029..a3ca65a184 100644 --- a/internal/printer/manifest_test.go +++ b/internal/manifest/manifest_test.go @@ -1,8 +1,8 @@ -package printer +package manifest import ( "context" - "fmt" + "os" "testing" "github.com/stretchr/testify/assert" @@ -13,6 +13,9 @@ import ( ) func Test_GetImageManifest(t *testing.T) { + if os.Getenv("CI") != "" { + t.Skip("Skipping test in CI to avoid rate limits") + } tests := []struct { name string input string @@ -48,7 +51,7 @@ func Test_GetImageManifest(t *testing.T) { manifestPath: "alpine_manifest.json", configPath: "alpine_config.json", hostOS: "windows", - error: "error parsing manifest for for image docker://alpine:3.14.0: choosing image instance: no image found in manifest list for architecture amd64, variant \"\", OS windows", + error: "error parsing manifest for image docker://alpine:3.14.0: choosing image instance: no image found in manifest list for architecture amd64, variant \"\", OS windows", }, } mc := NewManifestConfiguration() @@ -61,7 +64,6 @@ func Test_GetImageManifest(t *testing.T) { expectedConfig := string(inputConfig)[:len(inputConfig)-1] manifest, config, err := mc.GetImageManifest(context.Background(), tt.hostOS, tt.input) - fmt.Println("err", err) if len(tt.error) > 0 { assert.EqualError(t, err, tt.error) return diff --git a/internal/printer/testdata/alpine_config.json b/internal/manifest/testdata/alpine_config.json similarity index 100% rename from internal/printer/testdata/alpine_config.json rename to internal/manifest/testdata/alpine_config.json diff --git a/internal/printer/testdata/alpine_manifest.json b/internal/manifest/testdata/alpine_manifest.json similarity index 100% rename from internal/printer/testdata/alpine_manifest.json rename to internal/manifest/testdata/alpine_manifest.json diff --git a/internal/printer/testdata/nginx_config.json b/internal/manifest/testdata/nginx_config.json similarity index 100% rename from internal/printer/testdata/nginx_config.json rename to internal/manifest/testdata/nginx_config.json diff --git a/internal/printer/testdata/nginx_manifest.json b/internal/manifest/testdata/nginx_manifest.json similarity index 100% rename from internal/printer/testdata/nginx_manifest.json rename to internal/manifest/testdata/nginx_manifest.json diff --git a/internal/modules/overview/overview.go b/internal/modules/overview/overview.go index e4eee48003..6464f61204 100644 --- a/internal/modules/overview/overview.go +++ b/internal/modules/overview/overview.go @@ -303,6 +303,7 @@ func (co *Overview) ActionPaths() map[string]action.DispatcherFunc { octant.NewCronJobResume(co.dashConfig.ObjectStore(), co.dashConfig.ClusterClient()), octant.NewObjectUpdaterDispatcher(co.dashConfig.ObjectStore()), octant.NewApplyYaml(co.logger, co.dashConfig.ObjectStore()), + octant.NewManifest(co.logger), } return dispatchers.ToActionPaths() diff --git a/internal/octant/actions.go b/internal/octant/actions.go index 4996338fb1..bf64b91c22 100644 --- a/internal/octant/actions.go +++ b/internal/octant/actions.go @@ -22,6 +22,7 @@ const ( ActionOverviewServiceEditor = "action.octant.dev/serviceEditor" ActionDeploymentConfiguration = "action.octant.dev/deploymentConfiguration" ActionUpdateObject = "action.octant.dev/update" + ActionGetManifest = "action.octant.dev/manifest" ) func sendAlert(alerter action.Alerter, alertType action.AlertType, message string, expiration *time.Time) { diff --git a/internal/octant/manifest.go b/internal/octant/manifest.go new file mode 100644 index 0000000000..73b6c83a56 --- /dev/null +++ b/internal/octant/manifest.go @@ -0,0 +1,42 @@ +package octant + +import ( + "context" + "fmt" + + "github.com/vmware-tanzu/octant/internal/manifest" + "github.com/vmware-tanzu/octant/pkg/action" + "github.com/vmware-tanzu/octant/pkg/log" +) + +type Manifest struct { + logger log.Logger +} + +var _ action.Dispatcher = (*Manifest)(nil) + +func NewManifest(logger log.Logger) *Manifest { + return &Manifest{logger: logger} +} + +func (m *Manifest) ActionName() string { + return ActionGetManifest +} + +func (m *Manifest) Handle(ctx context.Context, _ action.Alerter, payload action.Payload) error { + m.logger.With("payload", payload).Debugf("received action payload") + image, err := payload.String("image") + if err != nil { + return err + } + hostOS, err := payload.String("host") + if err != nil { + return err + } + + _, _, err = manifest.ManifestManager.GetImageManifest(ctx, hostOS, image) + if err != nil { + return fmt.Errorf("getting manifest for image %s: %w", image, err) + } + return nil +} diff --git a/internal/printer/container.go b/internal/printer/container.go index c8d6252c1b..fb59ed7589 100644 --- a/internal/printer/container.go +++ b/internal/printer/container.go @@ -8,16 +8,16 @@ package printer import ( "context" "fmt" + "path" + "sort" + "strings" "github.com/vmware-tanzu/octant/internal/api" - "github.com/vmware-tanzu/octant/internal/util/json" - "github.com/vmware-tanzu/octant/internal/log" + "github.com/vmware-tanzu/octant/internal/manifest" + "github.com/vmware-tanzu/octant/internal/util/json" "github.com/vmware-tanzu/octant/internal/util/kubernetes" - - "path" - "sort" - "strings" + "github.com/vmware-tanzu/octant/pkg/action" "github.com/vmware-tanzu/octant/internal/octant" @@ -128,12 +128,16 @@ 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)) + if !manifest.ManifestManager.HasEntry(hostOS, c.Image) { + sections.Add("Image Manifest", component.NewButton("Show Manifest", action.CreatePayload(octant.ActionGetManifest, map[string]interface{}{"host": hostOS, "image": c.Image}))) } else { - sections.Add("Image Manifest", component.NewText(fmt.Sprintf("Unable to load image manifest %s", err))) + manifest, configuration, err := manifest.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) diff --git a/internal/printer/container_test.go b/internal/printer/container_test.go index 7811b220be..4fcab135d4 100644 --- a/internal/printer/container_test.go +++ b/internal/printer/container_test.go @@ -12,6 +12,10 @@ import ( "testing" "time" + "github.com/vmware-tanzu/octant/internal/manifest" + + "github.com/vmware-tanzu/octant/pkg/action" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -183,6 +187,7 @@ func Test_ContainerConfiguration(t *testing.T) { "Propagation": component.NewText("HostToContainer"), }, ) + var hostOS = "linux" cases := []struct { name string @@ -191,6 +196,7 @@ func Test_ContainerConfiguration(t *testing.T) { isInit bool action component.Action expected *component.Summary + initFunc func() }{ { name: "in general", @@ -206,11 +212,7 @@ func Test_ContainerConfiguration(t *testing.T) { }, { 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 \"\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 \",\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), + Content: component.NewButton("Show Manifest", action.CreatePayload(octant.ActionGetManifest, map[string]interface{}{"host": hostOS, "image": validContainer.Image})), }, { Header: "Host Ports", @@ -272,19 +274,91 @@ func Test_ContainerConfiguration(t *testing.T) { }, { 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), + Content: component.NewButton("Show Manifest", action.CreatePayload(octant.ActionGetManifest, map[string]interface{}{"host": hostOS, "image": validInitContainer.Image})), + }, + { + Header: "Command", + Content: component.NewText("['sh']"), + }, + { + Header: "Args", + Content: component.NewText("['-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']"), + }, + }...), + }, + { + name: "cached manifest for container", + container: validContainer, + initFunc: func() { + imageEntry := manifest.ImageEntry{ + ImageName: "docker://" + validContainer.Image, + HostOS: hostOS, + } + imageManifest := manifest.ImageManifest{ + Manifest: "{\"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}", + Configuration: "{\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 \"\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 \",\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}", + } + manifest.ManifestManager.SetManifest(imageEntry, imageManifest) + }, + expected: component.NewSummary("Container nginx", []component.SummarySection{ + { + Header: "Image", + Content: component.NewText("nginx:1.15"), + }, + { + 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\": \"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), + 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 \"\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 \",\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"), + }, + { + Header: "Container Ports", + Content: component.NewPorts([]component.Port{ + *component.NewPort("namespace", "v1", "Pod", "pod", 8443, "TCP", component.PortForwardState{IsForwardable: true, IsForwarded: true}), + *component.NewPort("namespace", "v1", "Pod", "pod", 443, "UDP", component.PortForwardState{IsForwardable: false, IsForwarded: false}), + }), + }, + { + Header: "Last State", + Content: component.NewText(fmt.Sprintf("terminated with 255 at %s: reason", now)), + }, + { + Header: "Current State", + Content: component.NewText(fmt.Sprintf("started at %s", now)), + }, + { + Header: "Ready", + Content: component.NewText("true"), + }, + { + Header: "Restart Count", + Content: component.NewText("2"), + }, + { + Header: "Environment", + Content: envTable, }, { Header: "Command", - Content: component.NewText("['sh']"), + Content: component.NewText("['/usr/bin/nginx']"), }, { Header: "Args", - Content: component.NewText("['-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']"), + Content: component.NewText("['-v', '-p', '80']"), + }, + { + Header: "Volume Mounts", + Content: volTable, }, }...), }, @@ -299,6 +373,9 @@ func Test_ContainerConfiguration(t *testing.T) { t.Run(tc.name, func(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() + if tc.initFunc != nil { + tc.initFunc() + } tpo := newTestPrinterOptions(controller) printOptions := tpo.ToOptions() diff --git a/web/src/app/modules/shared/components/presentation/json-editor/json-editor.component.scss b/web/src/app/modules/shared/components/presentation/json-editor/json-editor.component.scss index b4d70e9896..cd4c406007 100644 --- a/web/src/app/modules/shared/components/presentation/json-editor/json-editor.component.scss +++ b/web/src/app/modules/shared/components/presentation/json-editor/json-editor.component.scss @@ -7,6 +7,10 @@ border: none; } +div.jsoneditor-tree { + background: none; +} + div.jsoneditor-field, div.jsoneditor-value { color: var(--tableValue-color);