Skip to content

Commit

Permalink
Add tape view command
Browse files Browse the repository at this point in the history
  • Loading branch information
errordeveloper committed Sep 26, 2023
1 parent f98e5c4 commit 2a8a7e4
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Tape has the following commands:
- `tape images` - examine images referenced by a given set of manifests before packaging them
- `tape package` - package an artifcat and push it to a registry
- `tape pull` – downlowad and extract contents and attestations from an existing artifact
- `tape view` – inspect an existing artifact

### Example

Expand Down
7 changes: 7 additions & 0 deletions tape/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ func Run() int {
OutputManifestDirOptions: OutputManifestDirOptions{},
},
},
{
name: "view",
short: "View an artefact",
options: &TapeViewCommand{
tape: tape,
},
},
}

for _, c := range commands {
Expand Down
119 changes: 119 additions & 0 deletions tape/app/view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package app

import (
"context"
"encoding/json"
"fmt"
"os"

"github.com/docker/labs-brown-tape/oci"
)

type TapeViewCommand struct {
tape *TapeCommand
OutputFormatOptions

Image string `short:"I" long:"image" description:"Name of the image to view" required:"true"`
}

type artefactInfo struct {
RawManifests struct {
Index rawManifest[oci.IndexManifest] `json:"index"`
Content rawManifest[oci.Manifest] `json:"content"`
Attest rawManifest[oci.Manifest] `json:"attest"`
} `json:"rawManifests"`
}

type rawManifest[T oci.Manifest | oci.IndexManifest] struct {
Digest string `json:"digest,omitempty"`
Manifest *T `json:"manifest,omitempty"`
}

func (c *TapeViewCommand) Execute(args []string) error {
ctx := context.WithValue(c.tape.ctx, "command", "view")
if len(args) != 0 {
return fmt.Errorf("unexpected arguments: %v", args)
}

if err := c.tape.Init(); err != nil {
return err
}

client := oci.NewClient(nil)

outputInfo, err := c.CollectInfo(ctx, client)
if err != nil {
return fmt.Errorf("failed to collect info about artifact: %w", err)
}

if err := c.PrintInfo(ctx, outputInfo); err != nil {
return fmt.Errorf("failed to print info about artifact: %w", err)
}

return nil
}

func (c *TapeViewCommand) CollectInfo(ctx context.Context, client *oci.Client) (*artefactInfo, error) {
artefactInfo := &artefactInfo{}

imageIndex, indexManifest, _, err := client.GetIndexOrImage(ctx, c.Image)
if err != nil {
return nil, err
}
if indexManifest == nil {
return nil, fmt.Errorf("no index manifest found for %q", c.Image)
}

imageIndexDigest, err := imageIndex.Digest()
if err != nil {
return nil, err
}

artefactInfo.RawManifests.Index = rawManifest[oci.IndexManifest]{
Digest: imageIndexDigest.String(),
Manifest: indexManifest,
}

imageInfo, manifests, err := client.FetchFromIndexOrImage(ctx, imageIndex, indexManifest, nil)
if err != nil {
return nil, err
}

if len(imageInfo) == 0 {
return nil, fmt.Errorf("no images found in index %q", c.Image)
}

for i := range imageInfo {
info := imageInfo[i]
switch info.MediaType {
case oci.ContentMediaType:
case oci.AttestMediaType:
}
}

for digest := range manifests {
m := rawManifest[oci.Manifest]{
Digest: digest.String(),
Manifest: manifests[digest],
}
switch m.Manifest.Config.MediaType {
case oci.ContentMediaType:
artefactInfo.RawManifests.Content = m
case oci.AttestMediaType:
artefactInfo.RawManifests.Attest = m
}
}
return artefactInfo, nil
}

func (c *TapeViewCommand) PrintInfo(ctx context.Context, outputInfo *artefactInfo) error {
stdj := json.NewEncoder(os.Stdout)
switch c.OutputFormat {
case OutputFormatDirectJSON:
stdj.SetIndent("", " ")
if err := stdj.Encode(outputInfo); err != nil {
return fmt.Errorf("failed to marshal output: %w", err)
}
}
return nil
}

0 comments on commit 2a8a7e4

Please sign in to comment.