Skip to content

Commit

Permalink
Improve separation between type implementations and CLI code (#339)
Browse files Browse the repository at this point in the history
* Refactor PKI factory and add type checking

This allows for more DRY addition of new PKI types, and stricter
type checking. This also allows for simpler enumeration of
supported PKI formats which will be used in further updates to
simplify the CLI codebase.

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>

* revamp CLI flags; support different versions for upload

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>

* Add Alpine Package type

This adds support for the alpine package format used by Alpine Linux,
which is the concatenation of three tgz files (signature, control data,
and then the actual package files).

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>

* use shaFlag for --artifact-hash

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>

* change arg type to PKIFormat

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>

* defer type-specific validation logic to type code (instead of in CLI); also use CliLogger throughout CLI

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>

* refactor factory code

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>

* review comments

Signed-off-by: Bob Callaway <bob.callaway@gmail.com>
  • Loading branch information
bobcallaway authored Jul 14, 2021
1 parent e63fe71 commit 53d71cd
Show file tree
Hide file tree
Showing 51 changed files with 1,399 additions and 1,195 deletions.
6 changes: 3 additions & 3 deletions cmd/rekor-cli/app/format/wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ package format
import (
"encoding/json"
"fmt"
"log"

"github.com/sigstore/rekor/pkg/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand All @@ -32,7 +32,7 @@ func WrapCmd(f formatCmd) CobraCmd {
return func(cmd *cobra.Command, args []string) {
obj, err := f(args)
if err != nil {
log.Fatal(err)
log.CliLogger.Fatal(err)
}

// TODO: add flags to control output formatting (JSON, plaintext, etc.)
Expand All @@ -53,7 +53,7 @@ func WrapCmd(f formatCmd) CobraCmd {
func toJSON(i interface{}) string {
b, err := json.Marshal(i)
if err != nil {
log.Fatal(err)
log.CliLogger.Fatal(err)
}
return string(b)
}
7 changes: 4 additions & 3 deletions cmd/rekor-cli/app/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ var getCmd = &cobra.Command{
PreRun: func(cmd *cobra.Command, args []string) {
// these are bound here so that they are not overwritten by other commands
if err := viper.BindPFlags(cmd.Flags()); err != nil {
log.Logger.Fatal("Error initializing cmd line args: ", err)
log.CliLogger.Fatal("Error initializing cmd line args: ", err)
}
},
Run: format.WrapCmd(func(args []string) (interface{}, error) {
Expand Down Expand Up @@ -161,11 +161,12 @@ func parseEntry(uuid string, e models.LogEntryAnon) (interface{}, error) {
}

func init() {
initializePFlagMap()
if err := addUUIDPFlags(getCmd, false); err != nil {
log.Logger.Fatal("Error parsing cmd line args:", err)
log.CliLogger.Fatal("Error parsing cmd line args: ", err)
}
if err := addLogIndexFlag(getCmd, false); err != nil {
log.Logger.Fatal("Error parsing cmd line args:", err)
log.CliLogger.Fatal("Error parsing cmd line args: ", err)
}

rootCmd.AddCommand(getCmd)
Expand Down
1 change: 1 addition & 0 deletions cmd/rekor-cli/app/log_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,6 @@ var logInfoCmd = &cobra.Command{
}

func init() {
initializePFlagMap()
rootCmd.AddCommand(logInfoCmd)
}
1 change: 1 addition & 0 deletions cmd/rekor-cli/app/log_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ var logProofCmd = &cobra.Command{
}

func init() {
initializePFlagMap()
logProofCmd.Flags().Uint64("first-size", 1, "the size of the log where the proof should begin")
logProofCmd.Flags().Uint64("last-size", 0, "the size of the log where the proof should end")
if err := logProofCmd.MarkFlagRequired("last-size"); err != nil {
Expand Down
177 changes: 177 additions & 0 deletions cmd/rekor-cli/app/pflag_groups.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package app

import (
"errors"
"fmt"
"net/url"
"strings"

"github.com/sigstore/rekor/pkg/pki"
"github.com/sigstore/rekor/pkg/types"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// addFlagToCmd adds the specified command of a specified type to the command's flag set
func addFlagToCmd(cmd *cobra.Command, required bool, flagType FlagType, flag, desc string) error {
cmd.Flags().Var(NewFlagValue(flagType, ""), flag, desc)
if required {
return cmd.MarkFlagRequired(flag)
}
return nil
}

// addLogIndexFlag adds the "log-index" command to the command's flag set
func addLogIndexFlag(cmd *cobra.Command, required bool) error {
return addFlagToCmd(cmd, required, logIndexFlag, "log-index", "the index of the entry in the transparency log")
}

// addUUIDPFlags adds the "uuid" command to the command's flag set
func addUUIDPFlags(cmd *cobra.Command, required bool) error {
return addFlagToCmd(cmd, required, uuidFlag, "uuid", "UUID of entry in transparency log (if known)")
}

func addArtifactPFlags(cmd *cobra.Command) error {
flags := map[string]struct {
flagType FlagType
desc string
required bool
}{
"signature": {
fileOrURLFlag,
"path or URL to detached signature file",
false,
},
"type": {
typeFlag,
fmt.Sprintf("type of entry expressed as type(:version)?; supported types = %v", types.ListImplementedTypes()),
false,
},
"pki-format": {
pkiFormatFlag,
fmt.Sprintf("format of the signature and/or public key; options = %v", pki.SupportedFormats()),
false,
},
"public-key": {
fileOrURLFlag,
"path or URL to public key file",
false,
},
"artifact": {
fileOrURLFlag,
"path or URL to artifact file",
false,
},
"artifact-hash": {
shaFlag,
"hex encoded SHA256 hash of artifact (when using URL)",
false,
},
"entry": {
fileOrURLFlag,
"path or URL to pre-formatted entry file",
false,
},
}

for flag, flagVal := range flags {
if err := addFlagToCmd(cmd, flagVal.required, flagVal.flagType, flag, flagVal.desc); err != nil {
return err
}
}

return nil
}

func validateArtifactPFlags(uuidValid, indexValid bool) error {
uuidGiven := false
if uuidValid && viper.GetString("uuid") != "" {
uuidGiven = true
}
indexGiven := false
if indexValid && viper.GetString("log-index") != "" {
indexGiven = true
}

// if neither --entry or --artifact were given, then a reference to a uuid or index is needed
if viper.GetString("entry") == "" && viper.GetString("artifact") == "" {
if (uuidGiven && uuidValid) || (indexGiven && indexValid) {
return nil
}
return errors.New("either 'entry' or 'artifact' must be specified")
}

return nil
}

func CreatePropsFromPflags() *types.ArtifactProperties {
props := &types.ArtifactProperties{}

artifactString := viper.GetString("artifact")
if artifactString != "" {
if isURL(artifactString) {
props.ArtifactPath, _ = url.Parse(artifactString)
} else {
props.ArtifactPath = &url.URL{Path: artifactString}
}
}

props.ArtifactHash = viper.GetString("artifact-hash")

signatureString := viper.GetString("signature")
if signatureString != "" {
if isURL(signatureString) {
props.SignaturePath, _ = url.Parse(signatureString)
} else {
props.SignaturePath = &url.URL{Path: signatureString}
}
}

publicKeyString := viper.GetString("public-key")
if publicKeyString != "" {
if isURL(publicKeyString) {
props.PublicKeyPath, _ = url.Parse(publicKeyString)
} else {
props.PublicKeyPath = &url.URL{Path: publicKeyString}
}
}

props.PKIFormat = viper.GetString("pki-format")

return props
}

//TODO: add tests for this
func ParseTypeFlag(typeStr string) (string, string, error) {
// typeStr can come in as:
// type -> use default version for this kind
// type:version_string -> attempt to use specified version string

typeStrings := strings.SplitN(typeStr, ":", 2)
if _, ok := types.TypeMap.Load(typeStrings[0]); !ok {
return "", "", fmt.Errorf("unknown type %v", typeStrings[0])
}

switch len(typeStrings) {
case 1:
return typeStrings[0], "", nil
case 2:
return typeStrings[0], typeStrings[1], nil
}
return "", "", errors.New("malformed type string")
}
Loading

0 comments on commit 53d71cd

Please sign in to comment.