1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

fix: allow kyverno test variables directly in test (#8168)

* fix: allow kyverno test variables directly in test

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

* fix

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

* fix

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

* fix

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

* strict

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-08-30 18:17:28 +02:00 committed by GitHub
parent 23ef95a79c
commit a6bc35b740
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 329 additions and 310 deletions

View file

@ -12,11 +12,11 @@ import (
"github.com/go-git/go-billy/v5/memfs"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/color"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config"
@ -227,7 +227,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unst
osExit(1)
}
}
variables, globalValMap, valuesMap, namespaceSelectorMap, subresources, err := common.GetVariable(c.Variables, c.ValuesFile, nil, false, "")
variables, globalValMap, valuesMap, namespaceSelectorMap, subresources, err := common.GetVariable(c.Variables, nil, c.ValuesFile, nil, false, "")
if err != nil {
if !sanitizederror.IsErrorSanitized(err) {
return nil, nil, skipInvalidPolicies, nil, sanitizederror.NewWithError("failed to decode yaml", err)
@ -269,7 +269,7 @@ func (c *ApplyCommandConfig) getMutateLogPathIsDir(skipInvalidPolicies SkippedIn
return nil, nil, skipInvalidPolicies, nil, err, mutateLogPathIsDir
}
func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resources []*unstructured.Unstructured, rc *common.ResultCounts, dClient dclient.Interface, subresources []values.Subresource, skipInvalidPolicies SkippedInvalidPolicies, responses []engineapi.EngineResponse) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resources []*unstructured.Unstructured, rc *common.ResultCounts, dClient dclient.Interface, subresources []api.Subresource, skipInvalidPolicies SkippedInvalidPolicies, responses []engineapi.EngineResponse) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
validatingAdmissionPolicy := common.ValidatingAdmissionPolicies{}
for _, resource := range resources {
for _, policy := range validatingAdmissionPolicies {
@ -292,7 +292,7 @@ func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(validating
return rc, resources, skipInvalidPolicies, responses, nil
}
func (c *ApplyCommandConfig) applyPolicytoResource(variables map[string]string, policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resources []*unstructured.Unstructured, openApiManager openapi.Manager, skipInvalidPolicies SkippedInvalidPolicies, valuesMap map[string]map[string]values.Resource, dClient dclient.Interface, subresources []values.Subresource, globalValMap map[string]string, userInfo v1beta1.RequestInfo, mutateLogPathIsDir bool, namespaceSelectorMap map[string]map[string]string) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
func (c *ApplyCommandConfig) applyPolicytoResource(variables map[string]string, policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resources []*unstructured.Unstructured, openApiManager openapi.Manager, skipInvalidPolicies SkippedInvalidPolicies, valuesMap map[string]map[string]api.Resource, dClient dclient.Interface, subresources []api.Subresource, globalValMap map[string]string, userInfo v1beta1.RequestInfo, mutateLogPathIsDir bool, namespaceSelectorMap map[string]map[string]string) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
if len(variables) != 0 {
variables = common.SetInStoreContext(policies, variables)
}

View file

@ -6,7 +6,7 @@ import (
"text/template"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/create/templates"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
"github.com/spf13/cobra"
)
@ -31,7 +31,7 @@ func Command() *cobra.Command {
defer file.Close()
output = file
}
values := values.Values{
values := api.Values{
GlobalValues: map[string]string{},
}
for _, result := range namespaceSelector {
@ -69,12 +69,12 @@ func Command() *cobra.Command {
return cmd
}
func parseNamespaceSelector(in string) *values.NamespaceSelector {
func parseNamespaceSelector(in string) *api.NamespaceSelector {
parts := strings.Split(in, ",")
if len(parts) < 2 {
return nil
}
nsSelector := values.NamespaceSelector{
nsSelector := api.NamespaceSelector{
Name: parts[0],
Labels: map[string]string{},
}
@ -95,12 +95,12 @@ func parseKeyValue(in string) (string, string) {
return "", ""
}
func parseRule(in string) *values.Policy {
func parseRule(in string) *api.Policy {
parts := strings.Split(in, ",")
if len(parts) < 2 {
return nil
}
rule := values.Rule{
rule := api.Rule{
Name: parts[1],
Values: map[string]interface{}{},
}
@ -110,18 +110,18 @@ func parseRule(in string) *values.Policy {
rule.Values[k] = v
}
}
return &values.Policy{
return &api.Policy{
Name: parts[0],
Rules: []values.Rule{rule},
Rules: []api.Rule{rule},
}
}
func parseResource(in string) *values.Policy {
func parseResource(in string) *api.Policy {
parts := strings.Split(in, ",")
if len(parts) < 2 {
return nil
}
resource := values.Resource{
resource := api.Resource{
Name: parts[1],
Values: map[string]interface{}{},
}
@ -131,8 +131,8 @@ func parseResource(in string) *values.Policy {
resource.Values[k] = v
}
}
return &values.Policy{
return &api.Policy{
Name: parts[0],
Resources: []values.Resource{resource},
Resources: []api.Resource{resource},
}
}

View file

@ -3,6 +3,7 @@ package api
import (
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type Test struct {
@ -12,6 +13,7 @@ type Test struct {
Variables string `json:"variables"`
UserInfo string `json:"userinfo"`
Results []TestResults `json:"results"`
Values *Values `json:"values"`
}
type TestResults struct {
@ -57,3 +59,37 @@ type ReportResult struct {
TestResults
Resources []*corev1.ObjectReference `json:"resources"`
}
type Policy struct {
Name string `json:"name"`
Resources []Resource `json:"resources"`
Rules []Rule `json:"rules"`
}
type Rule struct {
Name string `json:"name"`
Values map[string]interface{} `json:"values"`
ForeachValues map[string][]interface{} `json:"foreachValues"`
}
type Values struct {
Policies []Policy `json:"policies"`
GlobalValues map[string]string `json:"globalValues"`
NamespaceSelectors []NamespaceSelector `json:"namespaceSelector"`
Subresources []Subresource `json:"subresources"`
}
type Resource struct {
Name string `json:"name"`
Values map[string]interface{} `json:"values"`
}
type Subresource struct {
APIResource metav1.APIResource `json:"subresource"`
ParentResource metav1.APIResource `json:"parentResource"`
}
type NamespaceSelector struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`
}

View file

@ -33,7 +33,7 @@ import (
func applyPoliciesFromPath(
fs billy.Filesystem,
values *api.Test,
apiTest *api.Test,
isGit bool,
policyResourcePath string,
rc *resultCounts,
@ -48,22 +48,22 @@ func applyPoliciesFromPath(
store.SetLocal(true)
var filteredResults []api.TestResults
for _, res := range values.Results {
for _, res := range apiTest.Results {
if filter.Apply(res) {
filteredResults = append(filteredResults, res)
}
}
values.Results = filteredResults
apiTest.Results = filteredResults
if len(values.Results) == 0 {
if len(apiTest.Results) == 0 {
return nil, nil, nil
}
fmt.Printf("\nExecuting %s...\n", values.Name)
valuesFile := values.Variables
userInfoFile := values.UserInfo
fmt.Printf("\nExecuting %s...\n", apiTest.Name)
valuesFile := apiTest.Variables
userInfoFile := apiTest.UserInfo
variables, globalValMap, valuesMap, namespaceSelectorMap, subresources, err := common.GetVariable(nil, values.Variables, fs, isGit, policyResourcePath)
variables, globalValMap, valuesMap, namespaceSelectorMap, subresources, err := common.GetVariable(nil, apiTest.Values, apiTest.Variables, fs, isGit, policyResourcePath)
if err != nil {
if !sanitizederror.IsErrorSanitized(err) {
return nil, nil, sanitizederror.NewWithError("failed to decode yaml", err)
@ -82,10 +82,10 @@ func applyPoliciesFromPath(
}
}
policyFullPath := pathutils.GetFullPaths(values.Policies, policyResourcePath, isGit)
resourceFullPath := pathutils.GetFullPaths(values.Resources, policyResourcePath, isGit)
policyFullPath := pathutils.GetFullPaths(apiTest.Policies, policyResourcePath, isGit)
resourceFullPath := pathutils.GetFullPaths(apiTest.Resources, policyResourcePath, isGit)
for i, result := range values.Results {
for i, result := range apiTest.Results {
arrPatchedResource := []string{result.PatchedResource}
arrGeneratedResource := []string{result.GeneratedResource}
arrCloneSourceResource := []string{result.CloneSourceResource}
@ -94,9 +94,9 @@ func applyPoliciesFromPath(
generatedResourceFullPath := pathutils.GetFullPaths(arrGeneratedResource, policyResourcePath, isGit)
CloneSourceResourceFullPath := pathutils.GetFullPaths(arrCloneSourceResource, policyResourcePath, isGit)
values.Results[i].PatchedResource = patchedResourceFullPath[0]
values.Results[i].GeneratedResource = generatedResourceFullPath[0]
values.Results[i].CloneSourceResource = CloneSourceResourceFullPath[0]
apiTest.Results[i].PatchedResource = patchedResourceFullPath[0]
apiTest.Results[i].GeneratedResource = generatedResourceFullPath[0]
apiTest.Results[i].CloneSourceResource = CloneSourceResourceFullPath[0]
}
policies, validatingAdmissionPolicies, err := common.GetPoliciesFromPaths(fs, policyFullPath, isGit, policyResourcePath)
@ -107,7 +107,7 @@ func applyPoliciesFromPath(
var filteredPolicies []kyvernov1.PolicyInterface
for _, p := range policies {
for _, res := range values.Results {
for _, res := range apiTest.Results {
if p.GetName() == res.Policy {
filteredPolicies = append(filteredPolicies, p)
break
@ -117,7 +117,7 @@ func applyPoliciesFromPath(
var filteredVAPs []v1alpha1.ValidatingAdmissionPolicy
for _, p := range validatingAdmissionPolicies {
for _, res := range values.Results {
for _, res := range apiTest.Results {
if p.GetName() == res.Policy {
filteredVAPs = append(filteredVAPs, p)
break
@ -131,7 +131,7 @@ func applyPoliciesFromPath(
var filteredRules []kyvernov1.Rule
for _, rule := range autogen.ComputeRules(p) {
for _, res := range values.Results {
for _, res := range apiTest.Results {
if res.IsValidatingAdmissionPolicy {
continue
}
@ -169,7 +169,7 @@ func applyPoliciesFromPath(
os.Exit(1)
}
checkableResources := selectResourcesForCheck(resources, values)
checkableResources := selectResourcesForCheck(resources, apiTest)
msgPolicies := "1 policy"
if len(policies)+len(validatingAdmissionPolicies) > 1 {
@ -250,7 +250,7 @@ func applyPoliciesFromPath(
engineResponses = append(engineResponses, ers...)
}
}
resultsMap, testResults := buildPolicyResults(engineResponses, values.Results, policyResourcePath, fs, isGit, auditWarn)
resultsMap, testResults := buildPolicyResults(engineResponses, apiTest.Results, policyResourcePath, fs, isGit, auditWarn)
return resultsMap, testResults, nil
}

View file

@ -15,9 +15,9 @@ import (
"github.com/kyverno/kyverno/api/kyverno"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/clients/dclient"
@ -26,10 +26,8 @@ import (
"github.com/kyverno/kyverno/pkg/engine/adapters"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/jmespath"
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
"github.com/kyverno/kyverno/pkg/imageverifycache"
"github.com/kyverno/kyverno/pkg/logging"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
yamlv2 "gopkg.in/yaml.v2"
@ -67,14 +65,7 @@ type ApplyPolicyConfig struct {
RuleToCloneSourceResource map[string]string
Client dclient.Interface
AuditWarn bool
Subresources []values.Subresource
}
// HasVariables - check for variables in the policy
func HasVariables(policy kyvernov1.PolicyInterface) [][]string {
policyRaw, _ := json.Marshal(policy)
matches := regex.RegexVariables.FindAllStringSubmatch(string(policyRaw), -1)
return matches
Subresources []api.Subresource
}
// GetPolicies - Extracting the policies from multiple YAML
@ -200,124 +191,6 @@ func RemoveDuplicateAndObjectVariables(matches [][]string) string {
return variableStr
}
func GetVariable(
variablesString []string,
valuesFile string,
fs billy.Filesystem,
isGit bool,
policyResourcePath string,
) (map[string]string, map[string]string, map[string]map[string]values.Resource, map[string]map[string]string, []values.Subresource, error) {
valuesMapResource := make(map[string]map[string]values.Resource)
valuesMapRule := make(map[string]map[string]values.Rule)
namespaceSelectorMap := make(map[string]map[string]string)
variables := make(map[string]string)
subresources := make([]values.Subresource, 0)
globalValMap := make(map[string]string)
reqObjVars := ""
for _, kvpair := range variablesString {
kvs := strings.Split(strings.Trim(kvpair, " "), "=")
if strings.Contains(kvs[0], "request.object") {
if !strings.Contains(reqObjVars, kvs[0]) {
reqObjVars = reqObjVars + "," + kvs[0]
}
continue
}
variables[strings.Trim(kvs[0], " ")] = strings.Trim(kvs[1], " ")
}
if valuesFile != "" {
vals, err := values.Load(fs, filepath.Join(policyResourcePath, valuesFile))
if err != nil {
fmt.Printf("Unable to load variable file: %s. error: %s \n", valuesFile, err)
return variables, globalValMap, valuesMapResource, namespaceSelectorMap, subresources, sanitizederror.NewWithError("unable to read yaml", err)
}
if vals.GlobalValues == nil {
vals.GlobalValues = make(map[string]string)
vals.GlobalValues["request.operation"] = "CREATE"
log.V(3).Info("Defaulting request.operation to CREATE")
} else {
if val, ok := vals.GlobalValues["request.operation"]; ok {
if val == "" {
vals.GlobalValues["request.operation"] = "CREATE"
log.V(3).Info("Globally request.operation value provided by the user is empty, defaulting it to CREATE", "request.opearation: ", vals.GlobalValues)
}
}
}
globalValMap = vals.GlobalValues
for _, p := range vals.Policies {
resourceMap := make(map[string]values.Resource)
for _, r := range p.Resources {
if val, ok := r.Values["request.operation"]; ok {
if val == "" {
r.Values["request.operation"] = "CREATE"
log.V(3).Info("No request.operation found, defaulting it to CREATE", "policy", p.Name)
}
}
for variableInFile := range r.Values {
if strings.Contains(variableInFile, "request.object") {
if !strings.Contains(reqObjVars, variableInFile) {
reqObjVars = reqObjVars + "," + variableInFile
}
delete(r.Values, variableInFile)
continue
}
}
resourceMap[r.Name] = r
}
valuesMapResource[p.Name] = resourceMap
if p.Rules != nil {
ruleMap := make(map[string]values.Rule)
for _, r := range p.Rules {
ruleMap[r.Name] = r
}
valuesMapRule[p.Name] = ruleMap
}
}
for _, n := range vals.NamespaceSelectors {
namespaceSelectorMap[n.Name] = n.Labels
}
subresources = vals.Subresources
}
if reqObjVars != "" {
fmt.Printf("\nNOTICE: request.object.* variables are automatically parsed from the supplied resource. Ignoring value of variables `%v`.\n", reqObjVars)
}
if globalValMap != nil {
if _, ok := globalValMap["request.operation"]; !ok {
globalValMap["request.operation"] = "CREATE"
log.V(3).Info("Defaulting request.operation to CREATE")
}
}
storePolicies := make([]store.Policy, 0)
for policyName, ruleMap := range valuesMapRule {
storeRules := make([]store.Rule, 0)
for _, rule := range ruleMap {
storeRules = append(storeRules, store.Rule{
Name: rule.Name,
Values: rule.Values,
ForEachValues: rule.ForeachValues,
})
}
storePolicies = append(storePolicies, store.Policy{
Name: policyName,
Rules: storeRules,
})
}
store.SetPolicies(storePolicies...)
return variables, globalValMap, valuesMapResource, namespaceSelectorMap, subresources, nil
}
// PrintMutatedOutput - function to print output in provided file or directory
func PrintMutatedOutput(mutateLogPath string, mutateLogPathIsDir bool, yaml string, fileName string) error {
var f *os.File
@ -498,38 +371,6 @@ func updateResultCounts(policy kyvernov1.PolicyInterface, engineResponse *engine
}
}
func SetInStoreContext(mutatedPolicies []kyvernov1.PolicyInterface, variables map[string]string) map[string]string {
storePolicies := make([]store.Policy, 0)
for _, policy := range mutatedPolicies {
storeRules := make([]store.Rule, 0)
for _, rule := range autogen.ComputeRules(policy) {
contextVal := make(map[string]interface{})
if len(rule.Context) != 0 {
for _, contextVar := range rule.Context {
for k, v := range variables {
if strings.HasPrefix(k, contextVar.Name) {
contextVal[k] = v
delete(variables, k)
}
}
}
storeRules = append(storeRules, store.Rule{
Name: rule.Name,
Values: contextVal,
})
}
}
storePolicies = append(storePolicies, store.Policy{
Name: policy.GetName(),
Rules: storeRules,
})
}
store.SetPolicies(storePolicies...)
return variables
}
func processMutateEngineResponse(c ApplyPolicyConfig, mutateResponse *engineapi.EngineResponse, resPath string) error {
var policyHasMutate bool
for _, rule := range autogen.ComputeRules(c.Policy) {
@ -599,37 +440,7 @@ func processMutateEngineResponse(c ApplyPolicyConfig, mutateResponse *engineapi.
return nil
}
func CheckVariableForPolicy(valuesMap map[string]map[string]values.Resource, globalValMap map[string]string, policyName string, resourceName string, resourceKind string, variables map[string]string, kindOnwhichPolicyIsApplied map[string]struct{}, variable string) (map[string]interface{}, error) {
// get values from file for this policy resource combination
thisPolicyResourceValues := make(map[string]interface{})
if len(valuesMap[policyName]) != 0 && !datautils.DeepEqual(valuesMap[policyName][resourceName], values.Resource{}) {
thisPolicyResourceValues = valuesMap[policyName][resourceName].Values
}
for k, v := range variables {
thisPolicyResourceValues[k] = v
}
if thisPolicyResourceValues == nil && len(globalValMap) > 0 {
thisPolicyResourceValues = make(map[string]interface{})
}
for k, v := range globalValMap {
if _, ok := thisPolicyResourceValues[k]; !ok {
thisPolicyResourceValues[k] = v
}
}
// skipping the variable check for non matching kind
if _, ok := kindOnwhichPolicyIsApplied[resourceKind]; ok {
if len(variable) > 0 && len(thisPolicyResourceValues) == 0 && store.HasPolicies() {
return thisPolicyResourceValues, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policyName, resourceName), nil)
}
}
return thisPolicyResourceValues, nil
}
func GetKindsFromPolicy(policy kyvernov1.PolicyInterface, subresources []values.Subresource, dClient dclient.Interface) map[string]struct{} {
func GetKindsFromPolicy(policy kyvernov1.PolicyInterface, subresources []api.Subresource, dClient dclient.Interface) map[string]struct{} {
kindOnwhichPolicyIsApplied := make(map[string]struct{})
for _, rule := range autogen.ComputeRules(policy) {
for _, kind := range rule.MatchResources.ResourceDescription.Kinds {
@ -652,7 +463,7 @@ func GetKindsFromPolicy(policy kyvernov1.PolicyInterface, subresources []values.
return kindOnwhichPolicyIsApplied
}
func getKind(kind string, subresources []values.Subresource, dClient dclient.Interface) (string, error) {
func getKind(kind string, subresources []api.Subresource, dClient dclient.Interface) (string, error) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
if subresource == "" {
return kind, nil
@ -674,7 +485,7 @@ func getKind(kind string, subresources []values.Subresource, dClient dclient.Int
return kind, nil
}
func getSubresourceKind(groupVersion, parentKind, subresourceName string, subresources []values.Subresource) (string, error) {
func getSubresourceKind(groupVersion, parentKind, subresourceName string, subresources []api.Subresource) (string, error) {
for _, subresource := range subresources {
parentResourceGroupVersion := metav1.GroupVersion{
Group: subresource.ParentResource.Group,

View file

@ -4,7 +4,7 @@ import (
"testing"
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -194,7 +194,7 @@ func Test_getSubresourceKind(t *testing.T) {
podAPIResource := metav1.APIResource{Name: "pods", SingularName: "", Namespaced: true, Kind: "Pod"}
podEvictionAPIResource := metav1.APIResource{Name: "pods/eviction", SingularName: "", Namespaced: true, Group: "policy", Version: "v1", Kind: "Eviction"}
subresources := []values.Subresource{
subresources := []api.Subresource{
{
APIResource: podEvictionAPIResource,
ParentResource: podAPIResource,

View file

@ -12,7 +12,7 @@ import (
"github.com/go-git/go-billy/v5"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/clients/dclient"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
@ -68,7 +68,7 @@ func GetResources(
return resources, err
}
func whenClusterIsTrue(resourceTypes []schema.GroupVersionKind, subresourceMap map[schema.GroupVersionKind]values.Subresource, dClient dclient.Interface, namespace string, resourcePaths []string, policyReport bool) ([]*unstructured.Unstructured, error) {
func whenClusterIsTrue(resourceTypes []schema.GroupVersionKind, subresourceMap map[schema.GroupVersionKind]api.Subresource, dClient dclient.Interface, namespace string, resourcePaths []string, policyReport bool) ([]*unstructured.Unstructured, error) {
resources := make([]*unstructured.Unstructured, 0)
resourceMap, err := getResourcesOfTypeFromCluster(resourceTypes, subresourceMap, dClient, namespace)
if err != nil {
@ -193,7 +193,7 @@ func GetResource(resourceBytes []byte) ([]*unstructured.Unstructured, error) {
return resources, nil
}
func getResourcesOfTypeFromCluster(resourceTypes []schema.GroupVersionKind, subresourceMap map[schema.GroupVersionKind]values.Subresource, dClient dclient.Interface, namespace string) (map[string]*unstructured.Unstructured, error) {
func getResourcesOfTypeFromCluster(resourceTypes []schema.GroupVersionKind, subresourceMap map[schema.GroupVersionKind]api.Subresource, dClient dclient.Interface, namespace string) (map[string]*unstructured.Unstructured, error) {
r := make(map[string]*unstructured.Unstructured)
for _, kind := range resourceTypes {
resourceList, err := dClient.ListResource(context.TODO(), kind.GroupVersion().String(), kind.Kind, namespace, nil)
@ -322,9 +322,9 @@ func GetPatchedAndGeneratedResource(resourceBytes []byte) (unstructured.Unstruct
}
// GetKindsFromRule will return the kinds from policy match block
func GetKindsFromRule(rule kyvernov1.Rule, client dclient.Interface) (map[schema.GroupVersionKind]bool, map[schema.GroupVersionKind]values.Subresource) {
func GetKindsFromRule(rule kyvernov1.Rule, client dclient.Interface) (map[schema.GroupVersionKind]bool, map[schema.GroupVersionKind]api.Subresource) {
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
subresourceMap := make(map[schema.GroupVersionKind]values.Subresource)
subresourceMap := make(map[schema.GroupVersionKind]api.Subresource)
for _, kind := range rule.MatchResources.Kinds {
addGVKToResourceTypesMap(kind, resourceTypesMap, subresourceMap, client)
}
@ -345,9 +345,9 @@ func GetKindsFromRule(rule kyvernov1.Rule, client dclient.Interface) (map[schema
return resourceTypesMap, subresourceMap
}
func getKindsFromValidatingAdmissionPolicy(policy v1alpha1.ValidatingAdmissionPolicy, client dclient.Interface) (map[schema.GroupVersionKind]bool, map[schema.GroupVersionKind]values.Subresource) {
func getKindsFromValidatingAdmissionPolicy(policy v1alpha1.ValidatingAdmissionPolicy, client dclient.Interface) (map[schema.GroupVersionKind]bool, map[schema.GroupVersionKind]api.Subresource) {
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
subresourceMap := make(map[schema.GroupVersionKind]values.Subresource)
subresourceMap := make(map[schema.GroupVersionKind]api.Subresource)
kinds := validatingadmissionpolicy.GetKinds(policy)
for _, kind := range kinds {
@ -357,7 +357,7 @@ func getKindsFromValidatingAdmissionPolicy(policy v1alpha1.ValidatingAdmissionPo
return resourceTypesMap, subresourceMap
}
func addGVKToResourceTypesMap(kind string, resourceTypesMap map[schema.GroupVersionKind]bool, subresourceMap map[schema.GroupVersionKind]values.Subresource, client dclient.Interface) {
func addGVKToResourceTypesMap(kind string, resourceTypesMap map[schema.GroupVersionKind]bool, subresourceMap map[schema.GroupVersionKind]api.Subresource, client dclient.Interface) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrss, err := client.Discovery().FindResources(group, version, kind, subresource)
if err != nil {
@ -372,7 +372,7 @@ func addGVKToResourceTypesMap(kind string, resourceTypesMap map[schema.GroupVers
gvk := schema.GroupVersionKind{
Group: child.Group, Version: child.Version, Kind: child.Kind,
}
subresourceMap[gvk] = values.Subresource{
subresourceMap[gvk] = api.Subresource{
APIResource: child,
ParentResource: metav1.APIResource{
Group: parent.Group,

View file

@ -2,7 +2,7 @@ package common
import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -19,7 +19,7 @@ func (r *KyvernoResources) FetchResourcesFromPolicy(resourcePaths []string, dCli
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
var resourceTypes []schema.GroupVersionKind
var subresourceMap map[schema.GroupVersionKind]values.Subresource
var subresourceMap map[schema.GroupVersionKind]api.Subresource
for _, policy := range r.policies {
for _, rule := range autogen.ComputeRules(policy) {

View file

@ -1,7 +1,7 @@
package common
import (
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -18,7 +18,7 @@ func (r *ValidatingAdmissionResources) FetchResourcesFromPolicy(resourcePaths []
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
var resourceTypes []schema.GroupVersionKind
var subresourceMap map[schema.GroupVersionKind]values.Subresource
var subresourceMap map[schema.GroupVersionKind]api.Subresource
for _, policy := range r.policies {
var resourceTypesInRule map[schema.GroupVersionKind]bool

View file

@ -0,0 +1,215 @@
package common
import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
"github.com/go-git/go-billy/v5"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/values"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
)
// HasVariables - check for variables in the policy
func HasVariables(policy kyvernov1.PolicyInterface) [][]string {
policyRaw, _ := json.Marshal(policy)
matches := regex.RegexVariables.FindAllStringSubmatch(string(policyRaw), -1)
return matches
}
func GetVariable(
variablesString []string,
vals *api.Values,
valuesFile string,
fs billy.Filesystem,
isGit bool,
policyResourcePath string,
) (map[string]string, map[string]string, map[string]map[string]api.Resource, map[string]map[string]string, []api.Subresource, error) {
if vals == nil && valuesFile != "" {
v, err := values.Load(fs, filepath.Join(policyResourcePath, valuesFile))
if err != nil {
fmt.Printf("Unable to load variable file: %s. error: %s \n", valuesFile, err)
return nil, nil, nil, nil, nil, sanitizederror.NewWithError("unable to read yaml", err)
}
vals = v
}
variables, globalValMap, valuesMapResource, valuesMapRule, namespaceSelectorMap, subresources := getVariable(variablesString, vals)
if globalValMap != nil {
if _, ok := globalValMap["request.operation"]; !ok {
globalValMap["request.operation"] = "CREATE"
log.V(3).Info("Defaulting request.operation to CREATE")
}
}
storePolicies := make([]store.Policy, 0)
for policyName, ruleMap := range valuesMapRule {
storeRules := make([]store.Rule, 0)
for _, rule := range ruleMap {
storeRules = append(storeRules, store.Rule{
Name: rule.Name,
Values: rule.Values,
ForEachValues: rule.ForeachValues,
})
}
storePolicies = append(storePolicies, store.Policy{
Name: policyName,
Rules: storeRules,
})
}
store.SetPolicies(storePolicies...)
return variables, globalValMap, valuesMapResource, namespaceSelectorMap, subresources, nil
}
func getVariable(
variablesString []string,
vals *api.Values,
) (map[string]string, map[string]string, map[string]map[string]api.Resource, map[string]map[string]api.Rule, map[string]map[string]string, []api.Subresource) {
valuesMapResource := make(map[string]map[string]api.Resource)
valuesMapRule := make(map[string]map[string]api.Rule)
namespaceSelectorMap := make(map[string]map[string]string)
variables := make(map[string]string)
subresources := make([]api.Subresource, 0)
globalValMap := make(map[string]string)
reqObjVars := ""
for _, kvpair := range variablesString {
kvs := strings.Split(strings.Trim(kvpair, " "), "=")
if strings.Contains(kvs[0], "request.object") {
if !strings.Contains(reqObjVars, kvs[0]) {
reqObjVars = reqObjVars + "," + kvs[0]
}
continue
}
variables[strings.Trim(kvs[0], " ")] = strings.Trim(kvs[1], " ")
}
if vals != nil {
if vals.GlobalValues == nil {
vals.GlobalValues = make(map[string]string)
vals.GlobalValues["request.operation"] = "CREATE"
log.V(3).Info("Defaulting request.operation to CREATE")
} else {
if val, ok := vals.GlobalValues["request.operation"]; ok {
if val == "" {
vals.GlobalValues["request.operation"] = "CREATE"
log.V(3).Info("Globally request.operation value provided by the user is empty, defaulting it to CREATE", "request.opearation: ", vals.GlobalValues)
}
}
}
globalValMap = vals.GlobalValues
for _, p := range vals.Policies {
resourceMap := make(map[string]api.Resource)
for _, r := range p.Resources {
if val, ok := r.Values["request.operation"]; ok {
if val == "" {
r.Values["request.operation"] = "CREATE"
log.V(3).Info("No request.operation found, defaulting it to CREATE", "policy", p.Name)
}
}
for variableInFile := range r.Values {
if strings.Contains(variableInFile, "request.object") {
if !strings.Contains(reqObjVars, variableInFile) {
reqObjVars = reqObjVars + "," + variableInFile
}
delete(r.Values, variableInFile)
continue
}
}
resourceMap[r.Name] = r
}
valuesMapResource[p.Name] = resourceMap
if p.Rules != nil {
ruleMap := make(map[string]api.Rule)
for _, r := range p.Rules {
ruleMap[r.Name] = r
}
valuesMapRule[p.Name] = ruleMap
}
}
for _, n := range vals.NamespaceSelectors {
namespaceSelectorMap[n.Name] = n.Labels
}
subresources = vals.Subresources
}
if reqObjVars != "" {
fmt.Printf("\nNOTICE: request.object.* variables are automatically parsed from the supplied resource. Ignoring value of variables `%v`.\n", reqObjVars)
}
return variables, globalValMap, valuesMapResource, valuesMapRule, namespaceSelectorMap, subresources
}
func SetInStoreContext(mutatedPolicies []kyvernov1.PolicyInterface, variables map[string]string) map[string]string {
storePolicies := make([]store.Policy, 0)
for _, policy := range mutatedPolicies {
storeRules := make([]store.Rule, 0)
for _, rule := range autogen.ComputeRules(policy) {
contextVal := make(map[string]interface{})
if len(rule.Context) != 0 {
for _, contextVar := range rule.Context {
for k, v := range variables {
if strings.HasPrefix(k, contextVar.Name) {
contextVal[k] = v
delete(variables, k)
}
}
}
storeRules = append(storeRules, store.Rule{
Name: rule.Name,
Values: contextVal,
})
}
}
storePolicies = append(storePolicies, store.Policy{
Name: policy.GetName(),
Rules: storeRules,
})
}
store.SetPolicies(storePolicies...)
return variables
}
func CheckVariableForPolicy(valuesMap map[string]map[string]api.Resource, globalValMap map[string]string, policyName string, resourceName string, resourceKind string, variables map[string]string, kindOnwhichPolicyIsApplied map[string]struct{}, variable string) (map[string]interface{}, error) {
// get values from file for this policy resource combination
thisPolicyResourceValues := make(map[string]interface{})
if len(valuesMap[policyName]) != 0 && !datautils.DeepEqual(valuesMap[policyName][resourceName], api.Resource{}) {
thisPolicyResourceValues = valuesMap[policyName][resourceName].Values
}
for k, v := range variables {
thisPolicyResourceValues[k] = v
}
if thisPolicyResourceValues == nil && len(globalValMap) > 0 {
thisPolicyResourceValues = make(map[string]interface{})
}
for k, v := range globalValMap {
if _, ok := thisPolicyResourceValues[k]; !ok {
thisPolicyResourceValues[k] = v
}
}
// skipping the variable check for non matching kind
if _, ok := kindOnwhichPolicyIsApplied[resourceKind]; ok {
if len(variable) > 0 && len(thisPolicyResourceValues) == 0 && store.HasPolicies() {
return thisPolicyResourceValues, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policyName, resourceName), nil)
}
}
return thisPolicyResourceValues, nil
}

View file

@ -1,11 +1,11 @@
package values
import (
"encoding/json"
"io"
"os"
"github.com/go-git/go-billy/v5"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
"k8s.io/apimachinery/pkg/util/yaml"
)
@ -20,17 +20,13 @@ func readFile(f billy.Filesystem, filepath string) ([]byte, error) {
return os.ReadFile(filepath)
}
func Load(f billy.Filesystem, filepath string) (*Values, error) {
func Load(f billy.Filesystem, filepath string) (*api.Values, error) {
yamlBytes, err := readFile(f, filepath)
if err != nil {
return nil, err
}
jsonBytes, err := yaml.ToJSON(yamlBytes)
if err != nil {
return nil, err
}
vals := &Values{}
if err := json.Unmarshal(jsonBytes, vals); err != nil {
vals := &api.Values{}
if err := yaml.UnmarshalStrict(yamlBytes, vals); err != nil {
return nil, err
}
return vals, nil

View file

@ -1,39 +0,0 @@
package values
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type Policy struct {
Name string `json:"name"`
Resources []Resource `json:"resources"`
Rules []Rule `json:"rules"`
}
type Rule struct {
Name string `json:"name"`
Values map[string]interface{} `json:"values"`
ForeachValues map[string][]interface{} `json:"foreachValues"`
}
type Values struct {
Policies []Policy `json:"policies"`
GlobalValues map[string]string `json:"globalValues"`
NamespaceSelectors []NamespaceSelector `json:"namespaceSelector"`
Subresources []Subresource `json:"subresources"`
}
type Resource struct {
Name string `json:"name"`
Values map[string]interface{} `json:"values"`
}
type Subresource struct {
APIResource metav1.APIResource `json:"subresource"`
ParentResource metav1.APIResource `json:"parentResource"`
}
type NamespaceSelector struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`
}

View file

@ -3,7 +3,6 @@ policies:
- deny-exec-by-pod-label.yaml
resources:
- resource.yaml
variables: values.yaml
results:
- policy: deny-exec-by-pod-label
rule: deny-exec-by-label
@ -11,3 +10,21 @@ results:
namespace: default
kind: PodExecOptions
result: fail
values:
policies:
- name: deny-exec-by-pod-label
rules:
- name: deny-exec-by-label
values:
podexeclabel: "false"
globalValues:
request.operation: CONNECT
subresources:
- subresource:
name: "pods/exec"
kind: "PodExecOptions"
version: "v1"
parentResource:
name: "pods"
kind: "Pod"
version: "v1"

View file

@ -1,17 +0,0 @@
policies:
- name: deny-exec-by-pod-label
rules:
- name: deny-exec-by-label
values:
podexeclabel: "false"
globalValues:
request.operation: CONNECT
subresources:
- subresource:
name: "pods/exec"
kind: "PodExecOptions"
version: "v1"
parentResource:
name: "pods"
kind: "Pod"
version: "v1"