github.1git.de/docker/cli@v26.1.3+incompatible/cli/command/trust/inspect.go (about) 1 // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: 2 //go:build go1.19 3 4 package trust 5 6 import ( 7 "context" 8 "encoding/json" 9 "fmt" 10 "sort" 11 12 "github.com/docker/cli/cli" 13 "github.com/docker/cli/cli/command" 14 "github.com/docker/cli/cli/command/inspect" 15 "github.com/spf13/cobra" 16 "github.com/theupdateframework/notary/tuf/data" 17 ) 18 19 type inspectOptions struct { 20 remotes []string 21 // FIXME(n4ss): this is consistent with `docker service inspect` but we should provide 22 // a `--format` flag too. (format and pretty-print should be exclusive) 23 prettyPrint bool 24 } 25 26 func newInspectCommand(dockerCli command.Cli) *cobra.Command { 27 options := inspectOptions{} 28 cmd := &cobra.Command{ 29 Use: "inspect IMAGE[:TAG] [IMAGE[:TAG]...]", 30 Short: "Return low-level information about keys and signatures", 31 Args: cli.RequiresMinArgs(1), 32 RunE: func(cmd *cobra.Command, args []string) error { 33 options.remotes = args 34 35 return runInspect(cmd.Context(), dockerCli, options) 36 }, 37 } 38 39 flags := cmd.Flags() 40 flags.BoolVar(&options.prettyPrint, "pretty", false, "Print the information in a human friendly format") 41 42 return cmd 43 } 44 45 func runInspect(ctx context.Context, dockerCLI command.Cli, opts inspectOptions) error { 46 if opts.prettyPrint { 47 var err error 48 49 for index, remote := range opts.remotes { 50 if err = prettyPrintTrustInfo(ctx, dockerCLI, remote); err != nil { 51 return err 52 } 53 54 // Additional separator between the inspection output of each image 55 if index < len(opts.remotes)-1 { 56 fmt.Fprint(dockerCLI.Out(), "\n\n") 57 } 58 } 59 60 return err 61 } 62 63 getRefFunc := func(ref string) (any, []byte, error) { 64 i, err := getRepoTrustInfo(ctx, dockerCLI, ref) 65 return nil, i, err 66 } 67 return inspect.Inspect(dockerCLI.Out(), opts.remotes, "", getRefFunc) 68 } 69 70 func getRepoTrustInfo(ctx context.Context, dockerCLI command.Cli, remote string) ([]byte, error) { 71 signatureRows, adminRolesWithSigs, delegationRoles, err := lookupTrustInfo(ctx, dockerCLI, remote) 72 if err != nil { 73 return []byte{}, err 74 } 75 // process the signatures to include repo admin if signed by the base targets role 76 for idx, sig := range signatureRows { 77 if len(sig.Signers) == 0 { 78 signatureRows[idx].Signers = []string{releasedRoleName} 79 } 80 } 81 82 signerList, adminList := []trustSigner{}, []trustSigner{} 83 84 signerRoleToKeyIDs := getDelegationRoleToKeyMap(delegationRoles) 85 86 for signerName, signerKeys := range signerRoleToKeyIDs { 87 signerKeyList := []trustKey{} 88 for _, keyID := range signerKeys { 89 signerKeyList = append(signerKeyList, trustKey{ID: keyID}) 90 } 91 signerList = append(signerList, trustSigner{signerName, signerKeyList}) 92 } 93 sort.Slice(signerList, func(i, j int) bool { return signerList[i].Name > signerList[j].Name }) 94 95 for _, adminRole := range adminRolesWithSigs { 96 switch adminRole.Name { 97 case data.CanonicalRootRole: 98 rootKeys := []trustKey{} 99 for _, keyID := range adminRole.KeyIDs { 100 rootKeys = append(rootKeys, trustKey{ID: keyID}) 101 } 102 adminList = append(adminList, trustSigner{"Root", rootKeys}) 103 case data.CanonicalTargetsRole: 104 targetKeys := []trustKey{} 105 for _, keyID := range adminRole.KeyIDs { 106 targetKeys = append(targetKeys, trustKey{ID: keyID}) 107 } 108 adminList = append(adminList, trustSigner{"Repository", targetKeys}) 109 } 110 } 111 sort.Slice(adminList, func(i, j int) bool { return adminList[i].Name > adminList[j].Name }) 112 113 return json.Marshal(trustRepo{ 114 Name: remote, 115 SignedTags: signatureRows, 116 Signers: signerList, 117 AdministrativeKeys: adminList, 118 }) 119 }