This replaces the visitAll recursive function with a test that verifies that the option is set for all commands and subcommands, so that it doesn't have to be modified at runtime. We currently still have to loop over all functions for the setValidateArgs call, but that can be looked at separately. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
121 lines
3.6 KiB
Go
121 lines
3.6 KiB
Go
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
|
//go:build go1.23
|
|
|
|
package trust
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/docker/cli/cli"
|
|
"github.com/docker/cli/cli/command"
|
|
"github.com/docker/cli/cli/command/inspect"
|
|
"github.com/spf13/cobra"
|
|
"github.com/theupdateframework/notary/tuf/data"
|
|
)
|
|
|
|
type inspectOptions struct {
|
|
remotes []string
|
|
// FIXME(n4ss): this is consistent with `docker service inspect` but we should provide
|
|
// a `--format` flag too. (format and pretty-print should be exclusive)
|
|
prettyPrint bool
|
|
}
|
|
|
|
func newInspectCommand(dockerCLI command.Cli) *cobra.Command {
|
|
options := inspectOptions{}
|
|
cmd := &cobra.Command{
|
|
Use: "inspect IMAGE[:TAG] [IMAGE[:TAG]...]",
|
|
Short: "Return low-level information about keys and signatures",
|
|
Args: cli.RequiresMinArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
options.remotes = args
|
|
|
|
return runInspect(cmd.Context(), dockerCLI, options)
|
|
},
|
|
DisableFlagsInUseLine: true,
|
|
}
|
|
|
|
flags := cmd.Flags()
|
|
flags.BoolVar(&options.prettyPrint, "pretty", false, "Print the information in a human friendly format")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func runInspect(ctx context.Context, dockerCLI command.Cli, opts inspectOptions) error {
|
|
if opts.prettyPrint {
|
|
var err error
|
|
|
|
for index, remote := range opts.remotes {
|
|
if err = prettyPrintTrustInfo(ctx, dockerCLI, remote); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Additional separator between the inspection output of each image
|
|
if index < len(opts.remotes)-1 {
|
|
_, _ = fmt.Fprint(dockerCLI.Out(), "\n\n")
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
getRefFunc := func(ref string) (any, []byte, error) {
|
|
i, err := getRepoTrustInfo(ctx, dockerCLI, ref)
|
|
return nil, i, err
|
|
}
|
|
return inspect.Inspect(dockerCLI.Out(), opts.remotes, "", getRefFunc)
|
|
}
|
|
|
|
func getRepoTrustInfo(ctx context.Context, dockerCLI command.Cli, remote string) ([]byte, error) {
|
|
signatureRows, adminRolesWithSigs, delegationRoles, err := lookupTrustInfo(ctx, dockerCLI, remote)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
// process the signatures to include repo admin if signed by the base targets role
|
|
for idx, sig := range signatureRows {
|
|
if len(sig.Signers) == 0 {
|
|
signatureRows[idx].Signers = []string{releasedRoleName}
|
|
}
|
|
}
|
|
|
|
signerList, adminList := []trustSigner{}, []trustSigner{}
|
|
|
|
signerRoleToKeyIDs := getDelegationRoleToKeyMap(delegationRoles)
|
|
|
|
for signerName, signerKeys := range signerRoleToKeyIDs {
|
|
signerKeyList := []trustKey{}
|
|
for _, keyID := range signerKeys {
|
|
signerKeyList = append(signerKeyList, trustKey{ID: keyID})
|
|
}
|
|
signerList = append(signerList, trustSigner{signerName, signerKeyList})
|
|
}
|
|
sort.Slice(signerList, func(i, j int) bool { return signerList[i].Name > signerList[j].Name })
|
|
|
|
for _, adminRole := range adminRolesWithSigs {
|
|
switch adminRole.Name {
|
|
case data.CanonicalRootRole:
|
|
rootKeys := []trustKey{}
|
|
for _, keyID := range adminRole.KeyIDs {
|
|
rootKeys = append(rootKeys, trustKey{ID: keyID})
|
|
}
|
|
adminList = append(adminList, trustSigner{"Root", rootKeys})
|
|
case data.CanonicalTargetsRole:
|
|
targetKeys := []trustKey{}
|
|
for _, keyID := range adminRole.KeyIDs {
|
|
targetKeys = append(targetKeys, trustKey{ID: keyID})
|
|
}
|
|
adminList = append(adminList, trustSigner{"Repository", targetKeys})
|
|
}
|
|
}
|
|
sort.Slice(adminList, func(i, j int) bool { return adminList[i].Name > adminList[j].Name })
|
|
|
|
return json.Marshal(trustRepo{
|
|
Name: remote,
|
|
SignedTags: signatureRows,
|
|
Signers: signerList,
|
|
AdministrativeKeys: adminList,
|
|
})
|
|
}
|