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