1
0
mirror of https://github.com/docker/cli.git synced 2026-01-26 15:41:42 +03:00
Files
cli/cmd/docker-trust/trust/formatter_test.go
Sebastiaan van Stijn c9bb291154 implement docker trust as plugin
move the `trust` subcommands to a plugin, so that the subcommands can
be installed separate from the `docker trust` integration in push/pull
(for situations where trust verification happens on the daemon side).

    make binary
    go build -o /usr/libexec/docker/cli-plugins/docker-trust ./cmd/docker-trust

    docker info
    Client:
     Version:    28.2.0-dev
     Context:    default
     Debug Mode: false
     Plugins:
      buildx: Docker Buildx (Docker Inc.)
        Version:  v0.24.0
        Path:     /usr/libexec/docker/cli-plugins/docker-buildx
      trust: Manage trust on Docker images (Docker Inc.)
        Version:  unknown-version
        Path:     /usr/libexec/docker/cli-plugins/docker-trust

    docker trust --help
    Usage:  docker trust [OPTIONS] COMMAND

    Extended build capabilities with BuildKit

    Options:
      -D, --debug   Enable debug logging

    Management Commands:
      key         Manage keys for signing Docker images
      signer      Manage entities who can sign Docker images

    Commands:
      inspect     Return low-level information about keys and signatures
      revoke      Remove trust for an image
      sign        Sign an image

    Run 'docker trust COMMAND --help' for more information on a command.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-06 15:24:46 +01:00

245 lines
5.1 KiB
Go

package trust
import (
"bytes"
"testing"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/cmd/docker-trust/internal/test"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestTrustTag(t *testing.T) {
digest := test.RandomID()
trustedTag := "tag"
var ctx trustTagContext
cases := []struct {
trustTagCtx trustTagContext
expValue string
call func() string
}{
{
trustTagContext{
s: signedTagInfo{
Name: trustedTag,
Digest: digest,
Signers: nil,
},
},
digest,
ctx.Digest,
},
{
trustTagContext{
s: signedTagInfo{
Name: trustedTag,
Digest: digest,
Signers: nil,
},
},
trustedTag,
ctx.SignedTag,
},
// Empty signers makes a row with empty string
{
trustTagContext{
s: signedTagInfo{
Name: trustedTag,
Digest: digest,
Signers: nil,
},
},
"",
ctx.Signers,
},
{
trustTagContext{
s: signedTagInfo{
Name: trustedTag,
Digest: digest,
Signers: []string{"alice", "bob", "claire"},
},
},
"alice, bob, claire",
ctx.Signers,
},
// alphabetic signing on Signers
{
trustTagContext{
s: signedTagInfo{
Name: trustedTag,
Digest: digest,
Signers: []string{"claire", "bob", "alice"},
},
},
"alice, bob, claire",
ctx.Signers,
},
}
for _, c := range cases {
ctx = c.trustTagCtx
v := c.call()
if v != c.expValue {
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
}
}
}
func TestTrustTagContextWrite(t *testing.T) {
cases := []struct {
context formatter.Context
expected string
}{
// Errors
{
formatter.Context{
Format: "{{InvalidFunction}}",
},
`template parsing error: template: :1: function "InvalidFunction" not defined`,
},
{
formatter.Context{
Format: "{{nil}}",
},
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
},
// Table Format
{
formatter.Context{
Format: defaultTrustTagTableFormat,
},
`SIGNED TAG DIGEST SIGNERS
tag1 deadbeef alice
tag2 aaaaaaaa alice, bob
tag3 bbbbbbbb
`,
},
}
signedTags := []signedTagInfo{
{Name: "tag1", Digest: "deadbeef", Signers: []string{"alice"}},
{Name: "tag2", Digest: "aaaaaaaa", Signers: []string{"alice", "bob"}},
{Name: "tag3", Digest: "bbbbbbbb", Signers: []string{}},
}
for _, tc := range cases {
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := tagWrite(tc.context, signedTags); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}
// With no trust data, the formatWrite will print an empty table:
// it's up to the caller to decide whether or not to print this versus an error
func TestTrustTagContextEmptyWrite(t *testing.T) {
emptyCase := struct {
context formatter.Context
expected string
}{
formatter.Context{
Format: defaultTrustTagTableFormat,
},
`SIGNED TAG DIGEST SIGNERS
`,
}
emptySignedTags := []signedTagInfo{}
out := bytes.NewBufferString("")
emptyCase.context.Output = out
err := tagWrite(emptyCase.context, emptySignedTags)
assert.NilError(t, err)
assert.Check(t, is.Equal(emptyCase.expected, out.String()))
}
func TestSignerInfoContextEmptyWrite(t *testing.T) {
emptyCase := struct {
context formatter.Context
expected string
}{
formatter.Context{
Format: defaultSignerInfoTableFormat,
},
`SIGNER KEYS
`,
}
emptySignerInfo := []signerInfo{}
out := bytes.NewBufferString("")
emptyCase.context.Output = out
err := signerInfoWrite(emptyCase.context, emptySignerInfo)
assert.NilError(t, err)
assert.Check(t, is.Equal(emptyCase.expected, out.String()))
}
func TestSignerInfoContextWrite(t *testing.T) {
cases := []struct {
context formatter.Context
expected string
}{
// Errors
{
formatter.Context{
Format: "{{InvalidFunction}}",
},
`template parsing error: template: :1: function "InvalidFunction" not defined`,
},
{
formatter.Context{
Format: "{{nil}}",
},
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
},
// Table Format
{
formatter.Context{
Format: defaultSignerInfoTableFormat,
Trunc: true,
},
`SIGNER KEYS
alice key11, key12
bob key21
eve foobarbazqux, key31, key32
`,
},
// No truncation
{
formatter.Context{
Format: defaultSignerInfoTableFormat,
},
`SIGNER KEYS
alice key11, key12
bob key21
eve foobarbazquxquux, key31, key32
`,
},
}
signerInfo := []signerInfo{
{Name: "alice", Keys: []string{"key11", "key12"}},
{Name: "bob", Keys: []string{"key21"}},
{Name: "eve", Keys: []string{"key31", "key32", "foobarbazquxquux"}},
}
for _, tc := range cases {
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := signerInfoWrite(tc.context, signerInfo); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}