diff --git a/subcommands/targets/offline-update.go b/subcommands/targets/offline-update.go index e5af34eb..210fdcbd 100644 --- a/subcommands/targets/offline-update.go +++ b/subcommands/targets/offline-update.go @@ -32,6 +32,16 @@ type ( buildTag string fetchedApps *client.FetchedApps } + + ouBundleMeta struct { + Type string `json:"type"` + Tag string `json:"tag"` + Targets []string `json:"targets"` + } + ouBundleTufMeta struct { + tuf.SignedCommon + ouBundleMeta `json:"x-fio-offline-bundle"` + } ) var ( @@ -80,6 +90,7 @@ func init() { offlineUpdateCmd.MarkFlagsMutuallyExclusive("tag", "wave") offlineUpdateCmd.MarkFlagsMutuallyExclusive("prod", "wave") initSignCmd(offlineUpdateCmd) + initCheckCmd(offlineUpdateCmd) } func initSignCmd(parentCmd *cobra.Command) { @@ -100,6 +111,21 @@ and is printed by this command.`, parentCmd.AddCommand(signCmd) } +func initCheckCmd(parentCmd *cobra.Command) { + checkCmd := &cobra.Command{ + Use: "check ", + Short: "Check and print info about an offline bundle", + Long: `Check and print info about an offline bundle. + +Run this command if you would like to get information about an offline bundle. +Specifically, what targets it includes, what the type of the targets (CI or production), +a bundle's expiration time', etc.`, + Run: doCheckBundle, + Args: cobra.ExactArgs(1), + } + parentCmd.AddCommand(checkCmd) +} + func doOfflineUpdate(cmd *cobra.Command, args []string) { factory := viper.GetString("factory") targetName := args[0] @@ -511,3 +537,38 @@ func signBundleTargets(rootMeta *client.AtsTufRoot, bundleTargetsMeta *tuf.Signe bundleTargetsMeta.Signatures = append(bundleTargetsMeta.Signatures, signatures[0]) return nil } + +func doCheckBundle(cmd *cobra.Command, args []string) { + tufMetaPath := path.Join(args[0], "tuf") + bundleTufMeta, err := getBundleTargetsMeta(tufMetaPath) + subcommands.DieNotNil(err) + if bundleTufMeta == nil { + subcommands.DieNotNil(errors.New("The bundle metadata has not been found; `bundle-targets.json` is missing in " + tufMetaPath)) + } + bundleMeta := ouBundleTufMeta{} + subcommands.DieNotNil(json.Unmarshal(*bundleTufMeta.Signed, &bundleMeta)) + fmt.Println("Bundle targets info:") + fmt.Printf("\tType:\t\t%s\n", bundleMeta.ouBundleMeta.Type) + fmt.Printf("\tTag:\t\t%s\n", bundleMeta.Tag) + fmt.Printf("\tExpires:\t%s\n", bundleMeta.SignedCommon.Expires) + fmt.Println("\tTargets:") + for _, target := range bundleMeta.Targets { + fmt.Printf("\t\t\t%s\n", target) + } + fmt.Println("\tSignatures:") + for _, sig := range bundleTufMeta.Signatures { + fmt.Printf("\t\t\t%s\n", sig.KeyID) + } + + rootMeta, err := getLatestRoot(tufMetaPath) + subcommands.DieNotNil(err) + fmt.Println("\tAllowed keys:") + for _, key := range rootMeta.Signed.Roles["targets"].KeyIDs { + fmt.Printf("\t\t\t%s\n", key) + } + fmt.Printf("\tThreshold:\t%d\n", rootMeta.Signed.Roles["targets"].Threshold) + numberOfMissingSignatures := rootMeta.Signed.Roles["targets"].Threshold - len(bundleTufMeta.Signatures) + if numberOfMissingSignatures > 0 { + fmt.Printf("\tMissing:\t%d\n", numberOfMissingSignatures) + } +}