1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

chore: add cli commands unit tests (#8366)

* chore: add cli unit tests

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* chore: add cli commands unit tests

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

---------

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2023-09-12 23:47:03 +02:00 committed by GitHub
parent fb97629ab5
commit d24b0848a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 847 additions and 378 deletions

View file

@ -18,9 +18,10 @@ func RootCommand(experimental bool) *cobra.Command {
Use: "kyverno",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, _ []string) error {
return cmd.Help()
},
}

View file

@ -12,10 +12,13 @@ import (
func Command() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Use: "create",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},

View file

@ -0,0 +1,22 @@
package create
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -27,11 +27,13 @@ func Command() *cobra.Command {
var rules, any, all []string
var options options
cmd := &cobra.Command{
Use: "exception [name]",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.ExactArgs(1),
Use: "exception [name]",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.ExactArgs(1),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
tmpl, err := template.New("exception").Parse(templates.ExceptionTemplate)
if err != nil {

View file

@ -25,16 +25,19 @@ func Command() *cobra.Command {
var path string
var options options
cmd := &cobra.Command{
Use: "metrics-config",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
RunE: func(cmd *cobra.Command, args []string) error {
Use: "metrics-config",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, _ []string) error {
tmpl, err := template.New("metricsconfig").Funcs(sprig.HermeticTxtFuncMap()).Parse(templates.MetricsConfigTemplate)
if err != nil {
return err
}
output := os.Stdout
output := cmd.OutOrStdout()
if path != "" {
file, err := os.Create(path)
if err != nil {

View file

@ -0,0 +1,22 @@
package metricsconfig
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -33,11 +33,14 @@ func Command() *cobra.Command {
var options options
var pass, fail, skip []string
cmd := &cobra.Command{
Use: "test",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
RunE: func(cmd *cobra.Command, args []string) error {
Use: "test",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, _ []string) error {
tmpl, err := template.New("test").Parse(templates.TestTemplate)
if err != nil {
return err
@ -60,7 +63,7 @@ func Command() *cobra.Command {
options.Results = append(options.Results, result)
}
}
output := os.Stdout
output := cmd.OutOrStdout()
if path != "" {
file, err := os.Create(path)
if err != nil {

View file

@ -0,0 +1,22 @@
package test
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -16,16 +16,19 @@ func Command() *cobra.Command {
var username string
var roles, clusterRoles, groups []string
cmd := &cobra.Command{
Use: "user-info",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
RunE: func(cmd *cobra.Command, args []string) error {
Use: "user-info",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, _ []string) error {
tmpl, err := template.New("userinfo").Parse(templates.UserInfoTemplate)
if err != nil {
return err
}
output := os.Stdout
output := cmd.OutOrStdout()
if path != "" {
file, err := os.Create(path)
if err != nil {

View file

@ -0,0 +1,22 @@
package userinfo
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -15,16 +15,19 @@ func Command() *cobra.Command {
var path string
var globalValues, namespaceSelector, rules, resources []string
cmd := &cobra.Command{
Use: "values",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
RunE: func(cmd *cobra.Command, args []string) error {
Use: "values",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, _ []string) error {
tmpl, err := template.New("values").Parse(templates.ValuesTemplate)
if err != nil {
return err
}
output := os.Stdout
output := cmd.OutOrStdout()
if path != "" {
file, err := os.Create(path)
if err != nil {

View file

@ -0,0 +1,22 @@
package values
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -1,74 +1,32 @@
package docs
import (
"errors"
"fmt"
"log"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)
const fmTemplate = `---
date: %s
title: "%s"
weight: 35
---
`
func websitePrepender(filename string) string {
now := time.Now().Format(time.RFC3339)
name := filepath.Base(filename)
base := strings.TrimSuffix(name, path.Ext(name))
return fmt.Sprintf(fmTemplate, now, strings.Replace(base, "_", " ", -1))
}
func websiteLinkHandler(filename string) string {
return "../" + strings.TrimSuffix(filename, filepath.Ext(filename))
}
func identity(s string) string {
return s
}
func empty(s string) string {
return ""
}
func Command(root *cobra.Command) *cobra.Command {
var path string
var website bool
var autogenTag bool
var options options
cmd := &cobra.Command{
Use: "docs",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
RunE: func(_ *cobra.Command, args []string) error {
prepender := empty
linkHandler := identity
if website {
prepender = websitePrepender
linkHandler = websiteLinkHandler
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
if err := options.validate(root); err != nil {
return err
}
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(path, os.ModeDir|os.ModePerm); err != nil {
return err
}
}
root.DisableAutoGenTag = !autogenTag
return doc.GenMarkdownTreeCustom(root, path, prepender, linkHandler)
cmd.SilenceUsage = true
cmd.SilenceErrors = true
return options.execute(root)
},
}
cmd.Flags().StringVarP(&path, "output", "o", ".", "Output path")
cmd.Flags().BoolVar(&website, "website", false, "Website version")
cmd.Flags().BoolVar(&autogenTag, "autogenTag", true, "Determines if the generated docs should contain a timestamp")
cmd.Flags().StringVarP(&options.path, "output", "o", ".", "Output path")
cmd.Flags().BoolVar(&options.website, "website", false, "Website version")
cmd.Flags().BoolVar(&options.autogenTag, "autogenTag", true, "Determines if the generated docs should contain a timestamp")
if err := cmd.MarkFlagDirname("output"); err != nil {
log.Println("WARNING", err)
}

View file

@ -0,0 +1,23 @@
package docs
import (
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)
func TestCommandWithNilRoot(t *testing.T) {
cmd := Command(nil)
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"-o", "foo"})
err := cmd.Execute()
assert.Error(t, err)
}
func TestCommandWithoutArgs(t *testing.T) {
cmd := Command(&cobra.Command{})
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -0,0 +1,41 @@
package docs
import (
"errors"
"os"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)
type options struct {
path string
website bool
autogenTag bool
}
func (o options) validate(root *cobra.Command) error {
if o.path == "" {
return errors.New("path is required")
}
if root == nil {
return errors.New("root command is required")
}
return nil
}
func (o options) execute(root *cobra.Command) error {
prepender := empty
linkHandler := identity
if o.website {
prepender = websitePrepender
linkHandler = websiteLinkHandler
}
if _, err := os.Stat(o.path); errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(o.path, os.ModeDir|os.ModePerm); err != nil {
return err
}
}
root.DisableAutoGenTag = !o.autogenTag
return doc.GenMarkdownTreeCustom(root, o.path, prepender, linkHandler)
}

View file

@ -0,0 +1,35 @@
package docs
import (
"fmt"
"path"
"path/filepath"
"strings"
"time"
)
const fmTemplate = `---
date: %s
title: "%s"
weight: 35
---
`
func websitePrepender(filename string) string {
now := time.Now().Format(time.RFC3339)
name := filepath.Base(filename)
base := strings.TrimSuffix(name, path.Ext(name))
return fmt.Sprintf(fmTemplate, now, strings.Replace(base, "_", " ", -1))
}
func websiteLinkHandler(filename string) string {
return "../" + strings.TrimSuffix(filename, filepath.Ext(filename))
}
func identity(s string) string {
return s
}
func empty(s string) string {
return ""
}

View file

@ -8,10 +8,13 @@ import (
func Command() *cobra.Command {
cmd := &cobra.Command{
Use: "fix",
Short: command.FormatDescription(true, websiteUrl, true, description...),
Long: command.FormatDescription(false, websiteUrl, true, description...),
Example: command.FormatExamples(examples...),
Use: "fix",
Short: command.FormatDescription(true, websiteUrl, true, description...),
Long: command.FormatDescription(false, websiteUrl, true, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},

View file

@ -0,0 +1,22 @@
package fix
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -8,17 +8,18 @@ import (
func Command() *cobra.Command {
var options options
cmd := &cobra.Command{
Use: "test [folder]...",
Use: "test [dir]...",
Short: command.FormatDescription(true, websiteUrl, true, description...),
Long: command.FormatDescription(false, websiteUrl, true, description...),
Example: command.FormatExamples(examples...),
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if err := options.validate(); err != nil {
if err := options.validate(args...); err != nil {
return err
}
cmd.SilenceUsage = true
cmd.SilenceErrors = true
return options.execute(args...)
return options.execute(cmd.OutOrStdout(), args...)
},
}
cmd.Flags().StringVarP(&options.fileName, "file-name", "f", "kyverno-test.yaml", "Test filename")

View file

@ -0,0 +1,22 @@
package test
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.Error(t, err)
}
func TestCommandInvalidFileName(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo", "-f", ""})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -3,6 +3,7 @@ package test
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
@ -17,16 +18,19 @@ type options struct {
compress bool
}
func (o options) validate() error {
func (o options) validate(dirs ...string) error {
if o.fileName == "" {
return errors.New("file-name must not be set to an empty string")
}
if len(dirs) == 0 {
return errors.New("at least one test directory is required")
}
return nil
}
func (o options) execute(args ...string) error {
func (o options) execute(out io.Writer, dirs ...string) error {
var testCases []test.TestCase
for _, arg := range args {
for _, arg := range dirs {
tests, err := test.LoadTests(arg, o.fileName)
if err != nil {
return err
@ -34,48 +38,48 @@ func (o options) execute(args ...string) error {
testCases = append(testCases, tests...)
}
for _, testCase := range testCases {
fmt.Printf("Processing test file (%s)...", testCase.Path)
fmt.Println()
fmt.Fprintf(out, "Processing test file (%s)...", testCase.Path)
fmt.Fprintln(out)
if testCase.Err != nil {
fmt.Printf(" ERROR: loading test file (%s): %s", testCase.Path, testCase.Err)
fmt.Println()
fmt.Fprintf(out, " ERROR: loading test file (%s): %s", testCase.Path, testCase.Err)
fmt.Fprintln(out)
continue
}
test := testCase.Test
needsSave := false
if test.Name == "" {
fmt.Println(" WARNING: name is not set")
fmt.Fprintln(out, " WARNING: name is not set")
test.Name = filepath.Base(testCase.Path)
needsSave = true
}
if len(test.Policies) == 0 {
fmt.Println(" WARNING: test has no policies")
fmt.Fprintln(out, " WARNING: test has no policies")
}
if len(test.Resources) == 0 {
fmt.Println(" WARNING: test has no resources")
fmt.Fprintln(out, " WARNING: test has no resources")
}
for i := range test.Results {
result := &test.Results[i]
if result.Resource != "" && len(result.Resources) != 0 {
fmt.Println(" WARNING: test result should not use both `resource` and `resources` fields")
fmt.Fprintln(out, " WARNING: test result should not use both `resource` and `resources` fields")
}
if result.Resource != "" {
fmt.Println(" WARNING: test result uses deprecated `resource` field, moving it into the `resources` field")
fmt.Fprintln(out, " WARNING: test result uses deprecated `resource` field, moving it into the `resources` field")
result.Resources = append(result.Resources, result.Resource)
result.Resource = ""
needsSave = true
}
if result.Namespace != "" {
fmt.Println(" WARNING: test result uses deprecated `namespace` field, replacing `policy` with a `<namespace>/<name>` pattern")
fmt.Fprintln(out, " WARNING: test result uses deprecated `namespace` field, replacing `policy` with a `<namespace>/<name>` pattern")
result.Policy = fmt.Sprintf("%s/%s", result.Namespace, result.Policy)
result.Namespace = ""
needsSave = true
}
if result.Status != "" && result.Result != "" {
fmt.Println(" ERROR: test result should not use both `status` and `result` fields")
fmt.Fprintln(out, " ERROR: test result should not use both `status` and `result` fields")
}
if result.Status != "" && result.Result == "" {
fmt.Println(" WARNING: test result uses deprecated `status` field, moving it into the `result` field")
fmt.Fprintln(out, " WARNING: test result uses deprecated `status` field, moving it into the `result` field")
result.Result = result.Status
result.Status = ""
needsSave = true
@ -98,23 +102,23 @@ func (o options) execute(args ...string) error {
}
}
if o.save && needsSave {
fmt.Printf(" Saving test file (%s)...", testCase.Path)
fmt.Println()
fmt.Fprintf(out, " Saving test file (%s)...", testCase.Path)
fmt.Fprintln(out)
yamlBytes, err := yaml.Marshal(test)
if err != nil {
fmt.Printf(" ERROR: converting test to yaml: %s", err)
fmt.Println()
fmt.Fprintf(out, " ERROR: converting test to yaml: %s", err)
fmt.Fprintln(out)
continue
}
if err := os.WriteFile(testCase.Path, yamlBytes, os.ModePerm); err != nil {
fmt.Printf(" ERROR: saving test file (%s): %s", testCase.Path, err)
fmt.Println()
fmt.Fprintf(out, " ERROR: saving test file (%s): %s", testCase.Path, err)
fmt.Fprintln(out)
continue
}
fmt.Println(" OK")
fmt.Fprintln(out, " OK")
}
fmt.Println()
fmt.Fprintln(out)
}
fmt.Println("Done.")
fmt.Fprintln(out, "Done.")
return nil
}

View file

@ -10,16 +10,21 @@ import (
func Command() *cobra.Command {
cmd := &cobra.Command{
Use: "jp",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
RunE: func(cmd *cobra.Command, args []string) error {
Use: "jp",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, _ []string) error {
return cmd.Help()
},
}
cmd.AddCommand(query.Command())
cmd.AddCommand(function.Command())
cmd.AddCommand(parse.Command())
cmd.AddCommand(
function.Command(),
parse.Command(),
query.Command(),
)
return cmd
}

View file

@ -0,0 +1,22 @@
package jp
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -2,6 +2,7 @@ package function
import (
"fmt"
"io"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
"github.com/kyverno/kyverno/pkg/config"
@ -13,18 +14,19 @@ import (
func Command() *cobra.Command {
return &cobra.Command{
Use: "function [function_name]...",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
SilenceUsage: true,
Use: "function [function_name]...",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
SilenceErrors: true,
SilenceUsage: true,
Run: func(cmd *cobra.Command, args []string) {
printFunctions(args...)
printFunctions(cmd.OutOrStdout(), args...)
},
}
}
func printFunctions(names ...string) {
func printFunctions(out io.Writer, names ...string) {
functions := jmespath.GetFunctions(config.NewDefaultConfiguration(false))
slices.SortFunc(functions, func(a, b jmespath.FunctionEntry) bool {
return a.String() < b.String()
@ -34,12 +36,12 @@ func printFunctions(names ...string) {
if len(namesSet) == 0 || namesSet.Has(function.Name) {
note := function.Note
function.Note = ""
fmt.Println("Name:", function.Name)
fmt.Println(" Signature:", function.String())
fmt.Fprintln(out, "Name:", function.Name)
fmt.Fprintln(out, " Signature:", function.String())
if note != "" {
fmt.Println(" Note: ", note)
fmt.Fprintln(out, " Note: ", note)
}
fmt.Println()
fmt.Fprintln(out)
}
}
}

View file

@ -0,0 +1,30 @@
package function
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithOneArg(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"truncate"})
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"truncate", "to_upper"})
err := cmd.Execute()
assert.NoError(t, err)
}

View file

@ -14,18 +14,19 @@ import (
func Command() *cobra.Command {
var files []string
cmd := &cobra.Command{
Use: "parse [-f file|expression]...",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
SilenceUsage: true,
Use: "parse [-f file|expression]...",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
expressions, err := loadExpressions(cmd, args, files)
if err != nil {
return err
}
for _, expression := range expressions {
if err := printAst(expression); err != nil {
if err := printAst(cmd.OutOrStdout(), expression); err != nil {
return err
}
}
@ -44,14 +45,14 @@ func readFile(reader io.Reader) (string, error) {
return string(data), nil
}
func loadFile(file string) (string, error) {
func loadFile(cmd *cobra.Command, file string) (string, error) {
reader, err := os.Open(filepath.Clean(file))
if err != nil {
return "", fmt.Errorf("failed open file %s: %v", file, err)
}
defer func() {
if err := reader.Close(); err != nil {
fmt.Printf("Error closing file: %s\n", err)
fmt.Fprintf(cmd.OutOrStdout(), "Error closing file: %s\n", err)
}
}()
content, err := readFile(reader)
@ -65,15 +66,15 @@ func loadExpressions(cmd *cobra.Command, args []string, files []string) ([]strin
var expressions []string
expressions = append(expressions, args...)
for _, file := range files {
expression, err := loadFile(file)
expression, err := loadFile(cmd, file)
if err != nil {
return nil, err
}
expressions = append(expressions, expression)
}
if len(expressions) == 0 {
fmt.Println("Reading from terminal input.")
fmt.Println("Enter a jmespath expression and hit Ctrl+D.")
fmt.Fprintln(cmd.OutOrStdout(), "Reading from terminal input.")
fmt.Fprintln(cmd.OutOrStdout(), "Enter a jmespath expression and hit Ctrl+D.")
data, err := readFile(cmd.InOrStdin())
if err != nil {
return nil, fmt.Errorf("failed to read file STDIN: %v", err)
@ -85,7 +86,7 @@ func loadExpressions(cmd *cobra.Command, args []string, files []string) ([]strin
// The following function has been adapted from
// https://github.com/jmespath/jp/blob/54882e03bd277fc4475a677fab1d35eaa478b839/jp.go
func printAst(expression string) error {
func printAst(out io.Writer, expression string) error {
parser := gojmespath.NewParser()
parsed, err := parser.Parse(expression)
if err != nil {
@ -94,7 +95,7 @@ func printAst(expression string) error {
}
return err
}
fmt.Println("#", expression)
fmt.Println(parsed)
fmt.Fprintln(out, "#", expression)
fmt.Fprintln(out, parsed)
return nil
}

View file

@ -0,0 +1,15 @@
package parse
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"request.object.metadata.name | truncate(@, `9`)"})
err := cmd.Execute()
assert.NoError(t, err)
}

View file

@ -21,17 +21,18 @@ func Command() *cobra.Command {
var input string
var queries []string
cmd := &cobra.Command{
Use: "query [-i input] [-q query|query]...",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
SilenceUsage: true,
Use: "query [-i input] [-q query|query]...",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
queries, err := loadQueries(args, queries)
queries, err := loadQueries(cmd, args, queries)
if err != nil {
return err
}
input, err := loadInput(input)
input, err := loadInput(cmd, input)
if err != nil {
return err
}
@ -57,7 +58,7 @@ func Command() *cobra.Command {
if err != nil {
return err
}
if err := printResult(query, result, unquoted, compact); err != nil {
if err := printResult(cmd, query, result, unquoted, compact); err != nil {
return err
}
}
@ -79,14 +80,14 @@ func readFile(reader io.Reader) ([]byte, error) {
return data, nil
}
func loadFile(file string) ([]byte, error) {
func loadFile(cmd *cobra.Command, file string) ([]byte, error) {
reader, err := os.Open(filepath.Clean(file))
if err != nil {
return nil, fmt.Errorf("failed open file %s: %v", file, err)
}
defer func() {
if err := reader.Close(); err != nil {
fmt.Printf("Error closing file: %s\n", err)
fmt.Fprintf(cmd.OutOrStdout(), "Error closing file: %s\n", err)
}
}()
content, err := readFile(reader)
@ -97,8 +98,8 @@ func loadFile(file string) ([]byte, error) {
}
func readQuery(cmd *cobra.Command) (string, error) {
fmt.Println("Reading from terminal input.")
fmt.Println("Enter a jmespath expression and hit Ctrl+D.")
fmt.Fprintln(cmd.OutOrStdout(), "Reading from terminal input.")
fmt.Fprintln(cmd.OutOrStdout(), "Enter a jmespath expression and hit Ctrl+D.")
data, err := readFile(cmd.InOrStdin())
if err != nil {
return "", err
@ -106,11 +107,11 @@ func readQuery(cmd *cobra.Command) (string, error) {
return string(data), nil
}
func loadQueries(args []string, files []string) ([]string, error) {
func loadQueries(cmd *cobra.Command, args []string, files []string) ([]string, error) {
var queries []string
queries = append(queries, args...)
for _, file := range files {
query, err := loadFile(file)
query, err := loadFile(cmd, file)
if err != nil {
return nil, err
}
@ -120,8 +121,8 @@ func loadQueries(args []string, files []string) ([]string, error) {
}
func readInput(cmd *cobra.Command) (interface{}, error) {
fmt.Println("Reading from terminal input.")
fmt.Println("Enter input object and hit Ctrl+D.")
fmt.Fprintln(cmd.OutOrStdout(), "Reading from terminal input.")
fmt.Fprintln(cmd.OutOrStdout(), "Enter input object and hit Ctrl+D.")
data, err := readFile(cmd.InOrStdin())
if err != nil {
return nil, err
@ -133,11 +134,11 @@ func readInput(cmd *cobra.Command) (interface{}, error) {
return input, nil
}
func loadInput(file string) (interface{}, error) {
func loadInput(cmd *cobra.Command, file string) (interface{}, error) {
if file == "" {
return nil, nil
}
data, err := loadFile(file)
data, err := loadFile(cmd, file)
if err != nil {
return nil, err
}
@ -164,11 +165,11 @@ func evaluate(input interface{}, query string) (interface{}, error) {
return result, nil
}
func printResult(query string, result interface{}, unquoted bool, compact bool) error {
func printResult(cmd *cobra.Command, query string, result interface{}, unquoted bool, compact bool) error {
converted, isString := result.(string)
fmt.Println("#", query)
fmt.Fprintln(cmd.OutOrStdout(), "#", query)
if unquoted && isString {
fmt.Println(converted)
fmt.Fprintln(cmd.OutOrStdout(), converted)
} else {
var toJSON []byte
var err error
@ -180,7 +181,7 @@ func printResult(query string, result interface{}, unquoted bool, compact bool)
if err != nil {
return fmt.Errorf("error marshalling result to JSON: %w", err)
}
fmt.Println(string(toJSON))
fmt.Fprintln(cmd.OutOrStdout(), string(toJSON))
}
return nil
}

View file

@ -0,0 +1,15 @@
package query
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"-i", "object.yaml", "-q", "query-file"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -19,11 +19,14 @@ func Command() *cobra.Command {
registryclient.AzureKeychain,
)
cmd := &cobra.Command{
Use: "oci",
Short: command.FormatDescription(true, websiteUrl, true, description...),
Long: command.FormatDescription(false, websiteUrl, true, description...),
Example: command.FormatExamples(examples...),
RunE: func(cmd *cobra.Command, args []string) error {
Use: "oci",
Short: command.FormatDescription(true, websiteUrl, true, description...),
Long: command.FormatDescription(false, websiteUrl, true, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, _ []string) error {
return cmd.Help()
},
}

View file

@ -0,0 +1,22 @@
package oci
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommand(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.NoError(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -1,121 +1,34 @@
package pull
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"log"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/oci/internal"
policyutils "github.com/kyverno/kyverno/pkg/utils/policy"
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
"github.com/spf13/cobra"
)
func Command(keychain authn.Keychain) *cobra.Command {
var dir string
var imageRef string
var options options
cmd := &cobra.Command{
Use: "pull",
Use: "pull [dir]",
Short: command.FormatDescription(true, websiteUrl, true, description...),
Long: command.FormatDescription(false, websiteUrl, true, description...),
Example: command.FormatExamples(examples...),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if imageRef == "" {
return errors.New("image reference is required")
dir := args[0]
if err := options.validate(dir); err != nil {
return err
}
dir = filepath.Clean(dir)
if !filepath.IsAbs(dir) {
cwd, err := os.Getwd()
if err != nil {
return err
}
dir, err = securejoin.SecureJoin(cwd, dir)
if err != nil {
return err
}
}
fi, err := os.Lstat(dir)
// Dir does not need to exist, as it can later be created.
if err != nil && errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(dir, 0o750); err != nil {
return fmt.Errorf("unable to create directory %s: %w", dir, err)
}
}
if err == nil && !fi.IsDir() {
return fmt.Errorf("dir '%s' must be a directory", dir)
}
ref, err := name.ParseReference(imageRef)
if err != nil {
return fmt.Errorf("parsing image reference: %v", err)
}
fmt.Fprintf(os.Stderr, "Downloading policies from an image [%s]...\n", ref.Name())
rmt, err := remote.Get(ref, remote.WithContext(cmd.Context()), remote.WithAuthFromKeychain(keychain))
if err != nil {
return fmt.Errorf("getting image: %v", err)
}
img, err := rmt.Image()
if err != nil {
return fmt.Errorf("getting image: %v", err)
}
l, err := img.Layers()
if err != nil {
return fmt.Errorf("getting image layers: %v", err)
}
for _, layer := range l {
lmt, err := layer.MediaType()
if err != nil {
return fmt.Errorf("getting layer media type: %v", err)
}
if lmt == internal.PolicyLayerMediaType {
blob, err := layer.Compressed()
if err != nil {
return fmt.Errorf("getting layer blob: %v", err)
}
defer blob.Close()
layerBytes, err := io.ReadAll(blob)
if err != nil {
return fmt.Errorf("reading layer blob: %v", err)
}
policies, _, err := yamlutils.GetPolicy(layerBytes)
if err != nil {
return fmt.Errorf("unmarshaling layer blob: %v", err)
}
for _, policy := range policies {
policyBytes, err := policyutils.ToYaml(policy)
if err != nil {
return fmt.Errorf("converting policy to yaml: %v", err)
}
pp := filepath.Join(dir, policy.GetName()+".yaml")
fmt.Fprintf(os.Stderr, "Saving policy into disk [%s]...\n", pp)
if err := os.WriteFile(pp, policyBytes, 0o600); err != nil {
return fmt.Errorf("creating file: %v", err)
}
}
}
}
fmt.Fprintf(os.Stderr, "Done.")
return nil
cmd.SilenceUsage = true
cmd.SilenceErrors = true
return options.execute(cmd.Context(), dir, keychain)
},
}
cmd.Flags().StringVarP(&dir, "directory", "d", ".", "path to a directory")
cmd.Flags().StringVarP(&imageRef, "image", "i", "", "image reference to push to or pull from")
cmd.Flags().StringVarP(&options.imageRef, "image", "i", "", "image reference to push to or pull from")
if err := cmd.MarkFlagRequired("image"); err != nil {
log.Println("WARNING", err)
}
return cmd
}

View file

@ -0,0 +1,33 @@
package pull
import (
"testing"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/authn/github"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/stretchr/testify/assert"
)
var keychain = authn.NewMultiKeychain(
authn.DefaultKeychain,
github.Keychain,
registryclient.AWSKeychain,
registryclient.GCPKeychain,
registryclient.AzureKeychain,
)
func TestCommandNoImageRef(t *testing.T) {
cmd := Command(keychain)
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.Error(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command(keychain)
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -9,6 +9,6 @@ var description = []string{
var examples = [][]string{
{
`# Pull policy from an OCI image and save it to the specific directory`,
`kyverno oci pull -i <imgref> -d policies`,
`kyverno oci pull . -i <imgref>`,
},
}

View file

@ -0,0 +1,108 @@
package pull
import (
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/oci/internal"
policyutils "github.com/kyverno/kyverno/pkg/utils/policy"
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
)
type options struct {
imageRef string
}
func (o options) validate(dir string) error {
if o.imageRef == "" {
return errors.New("image is required")
}
if dir == "" {
return errors.New("dir is required")
}
return nil
}
func (o options) execute(ctx context.Context, dir string, keychain authn.Keychain) error {
dir = filepath.Clean(dir)
if !filepath.IsAbs(dir) {
cwd, err := os.Getwd()
if err != nil {
return err
}
dir, err = securejoin.SecureJoin(cwd, dir)
if err != nil {
return err
}
}
fi, err := os.Lstat(dir)
// Dir does not need to exist, as it can later be created.
if err != nil && errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(dir, 0o750); err != nil {
return fmt.Errorf("unable to create directory %s: %w", dir, err)
}
}
if err == nil && !fi.IsDir() {
return fmt.Errorf("dir '%s' must be a directory", dir)
}
ref, err := name.ParseReference(o.imageRef)
if err != nil {
return fmt.Errorf("parsing image reference: %v", err)
}
fmt.Fprintf(os.Stderr, "Downloading policies from an image [%s]...\n", ref.Name())
rmt, err := remote.Get(ref, remote.WithContext(ctx), remote.WithAuthFromKeychain(keychain))
if err != nil {
return fmt.Errorf("getting image: %v", err)
}
img, err := rmt.Image()
if err != nil {
return fmt.Errorf("getting image: %v", err)
}
l, err := img.Layers()
if err != nil {
return fmt.Errorf("getting image layers: %v", err)
}
for _, layer := range l {
lmt, err := layer.MediaType()
if err != nil {
return fmt.Errorf("getting layer media type: %v", err)
}
if lmt == internal.PolicyLayerMediaType {
blob, err := layer.Compressed()
if err != nil {
return fmt.Errorf("getting layer blob: %v", err)
}
defer blob.Close()
layerBytes, err := io.ReadAll(blob)
if err != nil {
return fmt.Errorf("reading layer blob: %v", err)
}
policies, _, err := yamlutils.GetPolicy(layerBytes)
if err != nil {
return fmt.Errorf("unmarshaling layer blob: %v", err)
}
for _, policy := range policies {
policyBytes, err := policyutils.ToYaml(policy)
if err != nil {
return fmt.Errorf("converting policy to yaml: %v", err)
}
pp := filepath.Join(dir, policy.GetName()+".yaml")
fmt.Fprintf(os.Stderr, "Saving policy into disk [%s]...\n", pp)
if err := os.WriteFile(pp, policyBytes, 0o600); err != nil {
return fmt.Errorf("creating file: %v", err)
}
}
}
}
fmt.Fprintf(os.Stderr, "Done.")
return nil
}

View file

@ -1,91 +1,34 @@
package push
import (
"errors"
"fmt"
"os"
"log"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/static"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/oci/internal"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/openapi"
policyutils "github.com/kyverno/kyverno/pkg/utils/policy"
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
"github.com/spf13/cobra"
)
func Command(keychain authn.Keychain) *cobra.Command {
var policyRef string
var imageRef string
var options options
cmd := &cobra.Command{
Use: "push",
Short: command.FormatDescription(true, websiteUrl, true, description...),
Long: command.FormatDescription(false, websiteUrl, true, description...),
Example: command.FormatExamples(examples...),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if imageRef == "" {
return errors.New("image reference is required")
dir := args[0]
if err := options.validate(dir); err != nil {
return err
}
policies, _, err := policy.Load(nil, "", policyRef)
if err != nil {
return fmt.Errorf("unable to read policy file or directory %s: %w", policyRef, err)
}
openApiManager, err := openapi.NewManager(log.Log)
if err != nil {
return fmt.Errorf("creating openapi manager: %v", err)
}
for _, policy := range policies {
if _, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName())); err != nil {
return fmt.Errorf("validating policy %s: %v", policy.GetName(), err)
}
}
img := mutate.MediaType(empty.Image, types.OCIManifestSchema1)
img = mutate.ConfigMediaType(img, internal.PolicyConfigMediaType)
ref, err := name.ParseReference(imageRef)
if err != nil {
return fmt.Errorf("parsing image reference: %v", err)
}
for _, policy := range policies {
if policy.IsNamespaced() {
fmt.Fprintf(os.Stderr, "Adding policy [%s]\n", policy.GetName())
} else {
fmt.Fprintf(os.Stderr, "Adding cluster policy [%s]\n", policy.GetName())
}
policyBytes, err := policyutils.ToYaml(policy)
if err != nil {
return fmt.Errorf("converting policy to yaml: %v", err)
}
policyLayer := static.NewLayer(policyBytes, internal.PolicyLayerMediaType)
img, err = mutate.Append(img, mutate.Addendum{
Layer: policyLayer,
Annotations: internal.Annotations(policy),
})
if err != nil {
return fmt.Errorf("mutating image: %v", err)
}
}
fmt.Fprintf(os.Stderr, "Uploading [%s]...\n", ref.Name())
if err = remote.Write(ref, img, remote.WithContext(cmd.Context()), remote.WithAuthFromKeychain(keychain)); err != nil {
return fmt.Errorf("writing image: %v", err)
}
fmt.Fprintf(os.Stderr, "Done.")
return nil
cmd.SilenceUsage = true
cmd.SilenceErrors = true
return options.execute(cmd.Context(), dir, keychain)
},
}
cmd.Flags().StringVarP(&policyRef, "policy", "p", "", "path to policie(s)")
cmd.Flags().StringVarP(&imageRef, "image", "i", "", "image reference to push to or pull from")
cmd.Flags().StringVarP(&options.imageRef, "image", "i", "", "image reference to push to or pull from")
if err := cmd.MarkFlagRequired("image"); err != nil {
log.Println("WARNING", err)
}
return cmd
}

View file

@ -0,0 +1,33 @@
package push
import (
"testing"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/authn/github"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/stretchr/testify/assert"
)
var keychain = authn.NewMultiKeychain(
authn.DefaultKeychain,
github.Keychain,
registryclient.AWSKeychain,
registryclient.GCPKeychain,
registryclient.AzureKeychain,
)
func TestCommandNoImageRef(t *testing.T) {
cmd := Command(keychain)
assert.NotNil(t, cmd)
err := cmd.Execute()
assert.Error(t, err)
}
func TestCommandWithArgs(t *testing.T) {
cmd := Command(keychain)
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"foo"})
err := cmd.Execute()
assert.Error(t, err)
}

View file

@ -9,10 +9,10 @@ var description = []string{
var examples = [][]string{
{
`# Push policy to an OCI image from a given policy file`,
`kyverno oci push -p policy.yaml -i <imgref>`,
`kyverno oci push ./policy.yaml -i <imgref>`,
},
{
`# Push multiple policies to an OCI image from a given directory that includes policies`,
`kyverno oci push -p policies. -i <imgref>`,
`kyverno oci push . -i <imgref>`,
},
}

View file

@ -0,0 +1,84 @@
package push
import (
"context"
"errors"
"fmt"
"os"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/static"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/commands/oci/internal"
clilog "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/openapi"
policyutils "github.com/kyverno/kyverno/pkg/utils/policy"
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
)
type options struct {
imageRef string
}
func (o options) validate(policy string) error {
if o.imageRef == "" {
return errors.New("image is required")
}
if policy == "" {
return errors.New("policy is required")
}
return nil
}
func (o options) execute(ctx context.Context, dir string, keychain authn.Keychain) error {
policies, _, err := policy.Load(nil, "", dir)
if err != nil {
return fmt.Errorf("unable to read policy file or directory %s (%w)", dir, err)
}
openApiManager, err := openapi.NewManager(clilog.Log)
if err != nil {
return fmt.Errorf("creating openapi manager: %v", err)
}
for _, policy := range policies {
if _, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName())); err != nil {
return fmt.Errorf("validating policy %s: %v", policy.GetName(), err)
}
}
img := mutate.MediaType(empty.Image, types.OCIManifestSchema1)
img = mutate.ConfigMediaType(img, internal.PolicyConfigMediaType)
ref, err := name.ParseReference(o.imageRef)
if err != nil {
return fmt.Errorf("parsing image reference: %v", err)
}
for _, policy := range policies {
if policy.IsNamespaced() {
fmt.Fprintf(os.Stderr, "Adding policy [%s]\n", policy.GetName())
} else {
fmt.Fprintf(os.Stderr, "Adding cluster policy [%s]\n", policy.GetName())
}
policyBytes, err := policyutils.ToYaml(policy)
if err != nil {
return fmt.Errorf("converting policy to yaml: %v", err)
}
policyLayer := static.NewLayer(policyBytes, internal.PolicyLayerMediaType)
img, err = mutate.Append(img, mutate.Addendum{
Layer: policyLayer,
Annotations: internal.Annotations(policy),
})
if err != nil {
return fmt.Errorf("mutating image: %v", err)
}
}
fmt.Fprintf(os.Stderr, "Uploading [%s]...\n", ref.Name())
if err = remote.Write(ref, img, remote.WithContext(ctx), remote.WithAuthFromKeychain(keychain)); err != nil {
return fmt.Errorf("writing image: %v", err)
}
fmt.Fprintf(os.Stderr, "Done.")
return nil
}

View file

@ -14,10 +14,10 @@ func Command() *cobra.Command {
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
Example: command.FormatExamples(examples...),
Args: cobra.NoArgs,
SilenceErrors: true,
SilenceUsage: true,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, _ []string) error {
fmt.Fprintf(cmd.OutOrStdout(), "Version: %s\n", version.Version())
fmt.Fprintf(cmd.OutOrStdout(), "Time: %s\n", version.Time())
fmt.Fprintf(cmd.OutOrStdout(), "Git commit ID: %s\n", version.Hash())

View file

@ -13,6 +13,7 @@ import (
func TestCommand(t *testing.T) {
version.BuildVersion = "test"
cmd := Command()
assert.NotNil(t, cmd)
b := bytes.NewBufferString("")
cmd.SetOut(b)
err := cmd.Execute()
@ -28,6 +29,7 @@ Git commit ID: ---`
func TestCommandWithArgs(t *testing.T) {
cmd := Command()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{"test"})
err := cmd.Execute()
assert.Error(t, err)

View file

@ -9,7 +9,7 @@ Fix inconsistencies and deprecated usage in Kyverno test files.
NOTE: This is an experimental command, use `KYVERNO_EXPERIMENTAL=true` to enable it.
```
kyverno fix test [folder]... [flags]
kyverno fix test [dir]... [flags]
```
### Examples

View file

@ -11,22 +11,21 @@ Pulls policie(s) that are included in an OCI image from OCI registry and saves t
For more information visit https://kyverno.io/docs/kyverno-cli/#pulling
```
kyverno oci pull [flags]
kyverno oci pull [dir] [flags]
```
### Examples
```
# Pull policy from an OCI image and save it to the specific directory
kyverno oci pull -i <imgref> -d policies
kyverno oci pull . -i <imgref>
```
### Options
```
-d, --directory string path to a directory (default ".")
-h, --help help for pull
-i, --image string image reference to push to or pull from
-h, --help help for pull
-i, --image string image reference to push to or pull from
```
### Options inherited from parent commands

View file

@ -18,18 +18,17 @@ kyverno oci push [flags]
```
# Push policy to an OCI image from a given policy file
kyverno oci push -p policy.yaml -i <imgref>
kyverno oci push ./policy.yaml -i <imgref>
# Push multiple policies to an OCI image from a given directory that includes policies
kyverno oci push -p policies. -i <imgref>
kyverno oci push . -i <imgref>
```
### Options
```
-h, --help help for push
-i, --image string image reference to push to or pull from
-p, --policy string path to policie(s)
-h, --help help for push
-i, --image string image reference to push to or pull from
```
### Options inherited from parent commands

View file

@ -1,17 +1,19 @@
name: karpenter-annotations-to-nodeselector
policies:
- policy.yaml
- policy.yaml
resources:
- resource.yaml
- resource.yaml
results:
- policy: karpenter-annotations-to-nodeselector
rule: hard-nodeselector-lifecycle-on-demand
resource: soft-pod-antiaffinity-1
patchedResource: patched.yaml
kind: Pod
result: pass
- policy: karpenter-annotations-to-nodeselector
rule: hard-nodeselector-lifecycle-on-demand
resource: soft-pod-antiaffinity-1-copy
kind: Pod
result: pass
- kind: Pod
patchedResource: patched.yaml
policy: karpenter-annotations-to-nodeselector
resources:
- soft-pod-antiaffinity-1
result: pass
rule: hard-nodeselector-lifecycle-on-demand
- kind: Pod
policy: karpenter-annotations-to-nodeselector
resources:
- soft-pod-antiaffinity-1-copy
result: pass
rule: hard-nodeselector-lifecycle-on-demand