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

fix: allow policies from stdin in apply again (#5668)

Signed-off-by: bakito <github@bakito.ch>

Signed-off-by: bakito <github@bakito.ch>
Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
Marc Brugger 2022-12-16 14:50:15 +01:00 committed by GitHub
parent 810b1335b6
commit a80ee683c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 177 additions and 59 deletions

View file

@ -0,0 +1 @@
/disallow_latest_tag.yaml

View file

@ -67,7 +67,8 @@ type ApplyCommandConfig struct {
warnExitCode int warnExitCode int
} }
var applyHelp = ` var (
applyHelp = `
To apply on a resource: To apply on a resource:
kyverno apply /path/to/policy.yaml /path/to/folderOfPolicies --resource=/path/to/resource1 --resource=/path/to/resource2 kyverno apply /path/to/policy.yaml /path/to/folderOfPolicies --resource=/path/to/resource1 --resource=/path/to/resource2
@ -145,6 +146,10 @@ To apply policy with variables:
More info: https://kyverno.io/docs/kyverno-cli/ More info: https://kyverno.io/docs/kyverno-cli/
` `
// allow os.exit to be overwritten during unit tests
osExit = os.Exit
)
func Command() *cobra.Command { func Command() *cobra.Command {
var cmd *cobra.Command var cmd *cobra.Command
applyCommandConfig := &ApplyCommandConfig{} applyCommandConfig := &ApplyCommandConfig{}
@ -243,43 +248,45 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError("a stdin pipe can be used for either policies or resources, not both", err) return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError("a stdin pipe can be used for either policies or resources, not both", err)
} }
isGit := common.IsGitSourcePath(c.PolicyPaths)
var policies []kyvernov1.PolicyInterface var policies []kyvernov1.PolicyInterface
isGit := common.IsGitSourcePath(c.PolicyPaths)
if isGit {
gitSourceURL, err := url.Parse(c.PolicyPaths[0]) gitSourceURL, err := url.Parse(c.PolicyPaths[0])
if err != nil { if err != nil {
fmt.Printf("Error: failed to load policies\nCause: %s\n", err) fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
os.Exit(1) osExit(1)
} }
pathElems := strings.Split(gitSourceURL.Path[1:], "/") pathElems := strings.Split(gitSourceURL.Path[1:], "/")
if len(pathElems) <= 1 { if len(pathElems) <= 1 {
err := fmt.Errorf("invalid URL path %s - expected https://<any_git_source_domain>/:owner/:repository/:branch (without --git-branch flag) OR https://<any_git_source_domain>/:owner/:repository/:directory (with --git-branch flag)", gitSourceURL.Path) err := fmt.Errorf("invalid URL path %s - expected https://<any_git_source_domain>/:owner/:repository/:branch (without --git-branch flag) OR https://<any_git_source_domain>/:owner/:repository/:directory (with --git-branch flag)", gitSourceURL.Path)
fmt.Printf("Error: failed to parse URL \nCause: %s\n", err) fmt.Printf("Error: failed to parse URL \nCause: %s\n", err)
os.Exit(1) osExit(1)
} }
gitSourceURL.Path = strings.Join([]string{pathElems[0], pathElems[1]}, "/") gitSourceURL.Path = strings.Join([]string{pathElems[0], pathElems[1]}, "/")
repoURL := gitSourceURL.String() repoURL := gitSourceURL.String()
var gitPathToYamls string var gitPathToYamls string
if isGit {
c.GitBranch, gitPathToYamls = common.GetGitBranchOrPolicyPaths(c.GitBranch, repoURL, c.PolicyPaths) c.GitBranch, gitPathToYamls = common.GetGitBranchOrPolicyPaths(c.GitBranch, repoURL, c.PolicyPaths)
_, cloneErr := gitutils.Clone(repoURL, fs, c.GitBranch) _, cloneErr := gitutils.Clone(repoURL, fs, c.GitBranch)
if cloneErr != nil { if cloneErr != nil {
fmt.Printf("Error: failed to clone repository \nCause: %s\n", cloneErr) fmt.Printf("Error: failed to clone repository \nCause: %s\n", cloneErr)
log.Log.V(3).Info(fmt.Sprintf("failed to clone repository %v as it is not valid", repoURL), "error", cloneErr) log.Log.V(3).Info(fmt.Sprintf("failed to clone repository %v as it is not valid", repoURL), "error", cloneErr)
os.Exit(1) osExit(1)
} }
policyYamls, err := gitutils.ListYamls(fs, gitPathToYamls) policyYamls, err := gitutils.ListYamls(fs, gitPathToYamls)
if err != nil { if err != nil {
return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError("failed to list YAMLs in repository", err) return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError("failed to list YAMLs in repository", err)
} }
c.PolicyPaths = policyYamls
sort.Strings(policyYamls) sort.Strings(policyYamls)
c.PolicyPaths = policyYamls
} }
policies, err = common.GetPoliciesFromPaths(fs, c.PolicyPaths, isGit, "") policies, err = common.GetPoliciesFromPaths(fs, c.PolicyPaths, isGit, "")
if err != nil { if err != nil {
fmt.Printf("Error: failed to load policies\nCause: %s\n", err) fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
os.Exit(1) osExit(1)
} }
if len(c.ResourcePaths) == 0 && !c.Cluster { if len(c.ResourcePaths) == 0 && !c.Cluster {
@ -310,13 +317,13 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
err = common.PrintMutatedPolicy(policies) err = common.PrintMutatedPolicy(policies)
if err != nil { if err != nil {
return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError("failed to marsal mutated policy", err) return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError("failed to marshal mutated policy", err)
} }
resources, err = common.GetResourceAccordingToResourcePath(fs, c.ResourcePaths, c.Cluster, policies, dClient, c.Namespace, c.PolicyReport, false, "") resources, err = common.GetResourceAccordingToResourcePath(fs, c.ResourcePaths, c.Cluster, policies, dClient, c.Namespace, c.PolicyReport, false, "")
if err != nil { if err != nil {
fmt.Printf("Error: failed to load resources\nCause: %s\n", err) fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
os.Exit(1) osExit(1)
} }
if (len(resources) > 1 || len(policies) > 1) && c.VariablesString != "" { if (len(resources) > 1 || len(policies) > 1) && c.VariablesString != "" {
@ -330,7 +337,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
userInfo, subjectInfo, err = common.GetUserInfoFromPath(fs, c.UserInfoPath, false, "") userInfo, subjectInfo, err = common.GetUserInfoFromPath(fs, c.UserInfoPath, false, "")
if err != nil { if err != nil {
fmt.Printf("Error: failed to load request info\nCause: %s\n", err) fmt.Printf("Error: failed to load request info\nCause: %s\n", err)
os.Exit(1) osExit(1)
} }
store.SetSubjects(subjectInfo) store.SetSubjects(subjectInfo)
} }
@ -499,9 +506,9 @@ func PrintReportOrViolation(policyReport bool, rc *common.ResultCounts, resource
} }
if rc.Fail > 0 || rc.Error > 0 { if rc.Fail > 0 || rc.Error > 0 {
os.Exit(1) osExit(1)
} else if rc.Warn > 0 && warnExitCode != 0 { } else if rc.Warn > 0 && warnExitCode != 0 {
os.Exit(warnExitCode) osExit(warnExitCode)
} }
} }

View file

@ -1,6 +1,10 @@
package apply package apply
import ( import (
"fmt"
"os"
"path/filepath"
"strings"
"testing" "testing"
preport "github.com/kyverno/kyverno/api/policyreport/v1alpha2" preport "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
@ -9,15 +13,17 @@ import (
func Test_Apply(t *testing.T) { func Test_Apply(t *testing.T) {
type TestCase struct { type TestCase struct {
PolicyPaths []string gitBranch string
GitBranch string
ResourcePaths []string
Cluster bool
expectedPolicyReports []preport.PolicyReport expectedPolicyReports []preport.PolicyReport
config ApplyCommandConfig config ApplyCommandConfig
stdinFile string
} }
// copy disallow_latest_tag.yaml to local path
localFileName, err := copyFileToThisDir("../../../../test/best_practices/disallow_latest_tag.yaml")
assert.NilError(t, err)
defer func() { _ = os.Remove(localFileName) }()
testcases := []TestCase{ testcases := []*TestCase{
{ {
config: ApplyCommandConfig{ config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"}, PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
@ -36,6 +42,24 @@ func Test_Apply(t *testing.T) {
}, },
}, },
}, },
{
config: ApplyCommandConfig{
PolicyPaths: []string{localFileName},
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
PolicyReport: true,
},
expectedPolicyReports: []preport.PolicyReport{
{
Summary: preport.PolicyReportSummary{
Pass: 2,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
},
},
},
{ {
config: ApplyCommandConfig{ config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"}, PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
@ -72,25 +96,6 @@ func Test_Apply(t *testing.T) {
}, },
}, },
}, },
{
config: ApplyCommandConfig{
PolicyPaths: []string{"https://github.com/kyverno/policies/openshift/team-validate-ns-name/"},
GitBranch: "main",
PolicyReport: true,
Cluster: true,
},
expectedPolicyReports: []preport.PolicyReport{
{
Summary: preport.PolicyReportSummary{
Pass: 6,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
},
},
},
{ {
config: ApplyCommandConfig{ config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"}, PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
@ -110,21 +115,114 @@ func Test_Apply(t *testing.T) {
}, },
}, },
}, },
{
config: ApplyCommandConfig{
PolicyPaths: []string{"-"},
ResourcePaths: []string{"../../../../test/resources/pod_with_latest_tag.yaml"},
PolicyReport: true,
AuditWarn: true,
},
stdinFile: "../../../../test/best_practices/disallow_latest_tag.yaml",
expectedPolicyReports: []preport.PolicyReport{
{
Summary: preport.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 1,
},
},
},
},
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"-"},
PolicyReport: true,
AuditWarn: true,
},
stdinFile: "../../../../test/resources/pod_with_latest_tag.yaml",
expectedPolicyReports: []preport.PolicyReport{
{
Summary: preport.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 1,
},
},
},
},
{
config: ApplyCommandConfig{
PolicyPaths: []string{"https://github.com/kyverno/policies/openshift/team-validate-ns-name/"},
ResourcePaths: []string{"../../../../test/openshift/team-validate-ns-name.yaml"},
GitBranch: "main",
PolicyReport: true,
},
expectedPolicyReports: []preport.PolicyReport{
{
Summary: preport.PolicyReportSummary{
Pass: 2,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
},
},
},
} }
compareSummary := func(expected preport.PolicyReportSummary, actual map[string]interface{}) { compareSummary := func(expected preport.PolicyReportSummary, actual map[string]interface{}, desc string) {
assert.Equal(t, actual[preport.StatusPass].(int64), int64(expected.Pass)) assert.Equal(t, actual[preport.StatusPass].(int64), int64(expected.Pass), desc)
assert.Equal(t, actual[preport.StatusFail].(int64), int64(expected.Fail)) assert.Equal(t, actual[preport.StatusFail].(int64), int64(expected.Fail), desc)
assert.Equal(t, actual[preport.StatusSkip].(int64), int64(expected.Skip)) assert.Equal(t, actual[preport.StatusSkip].(int64), int64(expected.Skip), desc)
assert.Equal(t, actual[preport.StatusWarn].(int64), int64(expected.Warn)) assert.Equal(t, actual[preport.StatusWarn].(int64), int64(expected.Warn), desc)
assert.Equal(t, actual[preport.StatusError].(int64), int64(expected.Error)) assert.Equal(t, actual[preport.StatusError].(int64), int64(expected.Error), desc)
}
verifyTestcase := func(t *testing.T, tc *TestCase, compareSummary func(preport.PolicyReportSummary, map[string]interface{}, string)) {
if tc.stdinFile != "" {
oldStdin := os.Stdin
input, err := os.OpenFile(tc.stdinFile, os.O_RDONLY, 0)
assert.NilError(t, err)
os.Stdin = input
defer func() {
// Restore original Stdin
os.Stdin = oldStdin
_ = input.Close()
}()
}
desc := fmt.Sprintf("Policies: [%s], / Resources: [%s]", strings.Join(tc.config.PolicyPaths, ","), strings.Join(tc.config.ResourcePaths, ","))
// prevent os.Exit from being called
osExit = func(code int) {
assert.Check(t, false, "os.Exit(%d) should not be called: %s", code, desc)
}
defer func() { osExit = os.Exit }()
_, _, _, info, err := tc.config.applyCommandHelper()
assert.NilError(t, err, desc)
resps := buildPolicyReports(info)
assert.Assert(t, len(resps) > 0, "policy reports should not be empty: %s", desc)
for i, resp := range resps {
compareSummary(tc.expectedPolicyReports[i].Summary, resp.UnstructuredContent()["summary"].(map[string]interface{}), desc)
}
} }
for _, tc := range testcases { for _, tc := range testcases {
_, _, _, info, _ := tc.config.applyCommandHelper() verifyTestcase(t, tc, compareSummary)
resps := buildPolicyReports(info)
for i, resp := range resps {
compareSummary(tc.expectedPolicyReports[i].Summary, resp.UnstructuredContent()["summary"].(map[string]interface{}))
} }
} }
func copyFileToThisDir(sourceFile string) (string, error) {
input, err := os.ReadFile(sourceFile)
if err != nil {
return "", err
}
return filepath.Base(sourceFile), os.WriteFile(filepath.Base(sourceFile), input, 0644)
} }

View file

@ -0,0 +1,12 @@
### Namespace good
---
apiVersion: v1
kind: Namespace
metadata:
name: team1-test
### Namespace bad
---
apiVersion: v1
kind: Namespace
metadata:
name: test-namespace