mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-07 08:26:53 +00:00
The io/ioutil package has been deprecated as of Go 1.16 [1]. This commit replaces the existing io/ioutil functions with their new definitions in io and os packages. Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
130 lines
4 KiB
Go
130 lines
4 KiB
Go
package jp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
gojmespath "github.com/jmespath/go-jmespath"
|
|
jmespath "github.com/kyverno/kyverno/pkg/engine/jmespath"
|
|
"github.com/spf13/cobra"
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
var applyHelp = `
|
|
For more information visit: https://kyverno.io/docs/writing-policies/jmespath/
|
|
`
|
|
|
|
// Command returns jp command
|
|
func Command() *cobra.Command {
|
|
var compact, unquoted, ast, listFunctions bool
|
|
var filename, exprFile string
|
|
cmd := &cobra.Command{
|
|
Use: "jp",
|
|
Short: "Provides a command-line interface to JMESPath, enhanced with Kyverno specific custom functions",
|
|
SilenceUsage: true,
|
|
Example: applyHelp,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if listFunctions {
|
|
printFunctionList()
|
|
return nil
|
|
}
|
|
// The following function has been adapted from
|
|
// https://github.com/jmespath/jp/blob/54882e03bd277fc4475a677fab1d35eaa478b839/jp.go
|
|
var expression string
|
|
if exprFile != "" {
|
|
byteExpr, err := os.ReadFile(filepath.Clean(exprFile))
|
|
if err != nil {
|
|
return fmt.Errorf("error opening expression file: %w", err)
|
|
}
|
|
expression = string(byteExpr)
|
|
} else {
|
|
if len(args) == 0 {
|
|
return fmt.Errorf("must provide at least one argument")
|
|
}
|
|
expression = args[0]
|
|
}
|
|
if ast {
|
|
parser := gojmespath.NewParser()
|
|
parsed, err := parser.Parse(expression)
|
|
if err != nil {
|
|
if syntaxError, ok := err.(gojmespath.SyntaxError); ok {
|
|
return fmt.Errorf("%w\n%s",
|
|
syntaxError,
|
|
syntaxError.HighlightLocation())
|
|
}
|
|
return err
|
|
}
|
|
fmt.Printf("%s", parsed)
|
|
return nil
|
|
}
|
|
var input interface{}
|
|
if filename != "" {
|
|
f, err := os.ReadFile(filepath.Clean(filename))
|
|
if err != nil {
|
|
return fmt.Errorf("error opening input file: %w", err)
|
|
}
|
|
if err := yaml.Unmarshal(f, &input); err != nil {
|
|
return fmt.Errorf("error parsing input json: %w", err)
|
|
}
|
|
} else {
|
|
f, err := io.ReadAll(os.Stdin)
|
|
if err != nil {
|
|
return fmt.Errorf("error opening input file: %w", err)
|
|
}
|
|
if err := yaml.Unmarshal(f, &input); err != nil {
|
|
return fmt.Errorf("error parsing input json: %w", err)
|
|
}
|
|
}
|
|
jp, err := jmespath.New(expression)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to compile JMESPath: %s, error: %v", expression, err)
|
|
}
|
|
result, err := jp.Search(input)
|
|
if err != nil {
|
|
if syntaxError, ok := err.(gojmespath.SyntaxError); ok {
|
|
return fmt.Errorf("%s\n%s",
|
|
syntaxError,
|
|
syntaxError.HighlightLocation())
|
|
}
|
|
return fmt.Errorf("error evaluating JMESPath expression: %w", err)
|
|
}
|
|
converted, isString := result.(string)
|
|
if unquoted && isString {
|
|
fmt.Println(converted)
|
|
} else {
|
|
var toJSON []byte
|
|
if compact {
|
|
toJSON, err = json.Marshal(result)
|
|
} else {
|
|
toJSON, err = json.MarshalIndent(result, "", " ")
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("error marshalling result to JSON: %w", err)
|
|
}
|
|
fmt.Println(string(toJSON))
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
cmd.Flags().BoolVarP(&compact, "compact", "c", false, "Produce compact JSON output that omits nonessential whitespace")
|
|
cmd.Flags().BoolVarP(&listFunctions, "list-functions", "l", false, "Output a list of custom JMESPath functions in Kyverno")
|
|
cmd.Flags().BoolVarP(&unquoted, "unquoted", "u", false, "If the final result is a string, it will be printed without quotes")
|
|
cmd.Flags().BoolVar(&ast, "ast", false, "Only print the AST of the parsed expression. Do not rely on this output, only useful for debugging purposes")
|
|
cmd.Flags().StringVarP(&exprFile, "expr-file", "e", "", "Read JMESPath expression from the specified file")
|
|
cmd.Flags().StringVarP(&filename, "filename", "f", "", "Read input from a JSON or YAML file instead of stdin")
|
|
return cmd
|
|
}
|
|
|
|
func printFunctionList() {
|
|
functions := []string{}
|
|
for _, function := range jmespath.GetFunctions() {
|
|
functions = append(functions, function.String())
|
|
}
|
|
sort.Strings(functions)
|
|
fmt.Println(strings.Join(functions, "\n"))
|
|
}
|