mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
Feature/1515 - handle configmap and api variable cli (#1789)
* added store package Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com> * added changes to handle api and configmap variables in cli Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com> * removed comments Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com> * refactoring code Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com> * added test case for mutation Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com> * added test case for validation Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com> * code improvement Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com>
This commit is contained in:
parent
caa6a90b27
commit
1e4c950104
11 changed files with 379 additions and 63 deletions
|
@ -2,6 +2,7 @@ package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
|
@ -77,3 +78,33 @@ func GetKindFromGVK(str string) (apiVersion string, kind string) {
|
||||||
}
|
}
|
||||||
return splitString[0] + "/" + splitString[1], splitString[2]
|
return splitString[0] + "/" + splitString[1], splitString[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func VariableToJSON(key, value string) []byte {
|
||||||
|
var subString string
|
||||||
|
splitBySlash := strings.Split(key, "\"")
|
||||||
|
if len(splitBySlash) > 1 {
|
||||||
|
subString = splitBySlash[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
startString := ""
|
||||||
|
endString := ""
|
||||||
|
lenOfVariableString := 0
|
||||||
|
addedSlashString := false
|
||||||
|
for _, k := range strings.Split(splitBySlash[0], ".") {
|
||||||
|
if k != "" {
|
||||||
|
startString += fmt.Sprintf(`{"%s":`, k)
|
||||||
|
endString += `}`
|
||||||
|
lenOfVariableString = lenOfVariableString + len(k) + 1
|
||||||
|
if lenOfVariableString >= len(splitBySlash[0]) && len(splitBySlash) > 1 && !addedSlashString {
|
||||||
|
startString += fmt.Sprintf(`{"%s":`, subString)
|
||||||
|
endString += `}`
|
||||||
|
addedSlashString = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
midString := fmt.Sprintf(`"%s"`, value)
|
||||||
|
finalString := startString + midString + endString
|
||||||
|
var jsonData = []byte(finalString)
|
||||||
|
return jsonData
|
||||||
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ func filterRule(rule kyverno.Rule, policyContext *PolicyContext) *response.RuleR
|
||||||
policyContext.JSONContext.Checkpoint()
|
policyContext.JSONContext.Checkpoint()
|
||||||
defer policyContext.JSONContext.Restore()
|
defer policyContext.JSONContext.Restore()
|
||||||
|
|
||||||
if err := LoadContext(logger, rule.Context, resCache, policyContext); err != nil {
|
if err := LoadContext(logger, rule.Context, resCache, policyContext, rule.Name); err != nil {
|
||||||
logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
|
logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,40 +9,55 @@ import (
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||||
|
pkgcommon "github.com/kyverno/kyverno/pkg/common"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||||
jmespath "github.com/kyverno/kyverno/pkg/engine/jmespath"
|
jmespath "github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/variables"
|
"github.com/kyverno/kyverno/pkg/engine/variables"
|
||||||
|
"github.com/kyverno/kyverno/pkg/kyverno/store"
|
||||||
"github.com/kyverno/kyverno/pkg/resourcecache"
|
"github.com/kyverno/kyverno/pkg/resourcecache"
|
||||||
"k8s.io/client-go/dynamic/dynamiclister"
|
"k8s.io/client-go/dynamic/dynamiclister"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoadContext - Fetches and adds external data to the Context.
|
// LoadContext - Fetches and adds external data to the Context.
|
||||||
func LoadContext(logger logr.Logger, contextEntries []kyverno.ContextEntry, resCache resourcecache.ResourceCache, ctx *PolicyContext) error {
|
func LoadContext(logger logr.Logger, contextEntries []kyverno.ContextEntry, resCache resourcecache.ResourceCache, ctx *PolicyContext, ruleName string) error {
|
||||||
if len(contextEntries) == 0 {
|
if len(contextEntries) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// get GVR Cache for "configmaps"
|
policyName := ctx.Policy.Name
|
||||||
// can get cache for other resources if the informers are enabled in resource cache
|
if store.GetMock() {
|
||||||
gvrC, ok := resCache.GetGVRCache("ConfigMap")
|
rule := store.GetPolicyRuleFromContext(policyName, ruleName)
|
||||||
if !ok {
|
variables := rule.Values
|
||||||
return errors.New("configmaps GVR Cache not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
lister := gvrC.Lister()
|
for key, value := range variables {
|
||||||
|
jsonData := pkgcommon.VariableToJSON(key, value)
|
||||||
for _, entry := range contextEntries {
|
if err := ctx.JSONContext.AddJSON(jsonData); err != nil {
|
||||||
if entry.ConfigMap != nil {
|
|
||||||
if err := loadConfigMap(logger, entry, lister, ctx.JSONContext); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if entry.APICall != nil {
|
|
||||||
if err := loadAPIData(logger, entry, ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// get GVR Cache for "configmaps"
|
||||||
|
// can get cache for other resources if the informers are enabled in resource cache
|
||||||
|
gvrC, ok := resCache.GetGVRCache("ConfigMap")
|
||||||
|
if !ok {
|
||||||
|
return errors.New("configmaps GVR Cache not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
lister := gvrC.Lister()
|
||||||
|
|
||||||
|
for _, entry := range contextEntries {
|
||||||
|
if entry.ConfigMap != nil {
|
||||||
|
if err := loadConfigMap(logger, entry, lister, ctx.JSONContext); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if entry.APICall != nil {
|
||||||
|
if err := loadAPIData(logger, entry, ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
||||||
logger.V(3).Info("matched mutate rule")
|
logger.V(3).Info("matched mutate rule")
|
||||||
|
|
||||||
policyContext.JSONContext.Restore()
|
policyContext.JSONContext.Restore()
|
||||||
if err := LoadContext(logger, rule.Context, resCache, policyContext); err != nil {
|
if err := LoadContext(logger, rule.Context, resCache, policyContext, rule.Name); err != nil {
|
||||||
logger.Error(err, "failed to load context")
|
logger.Error(err, "failed to load context")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/utils"
|
"github.com/kyverno/kyverno/pkg/engine/utils"
|
||||||
|
"github.com/kyverno/kyverno/pkg/kyverno/store"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -164,3 +165,104 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
|
||||||
t.Log(er.PolicyResponse.Rules[0].Message)
|
t.Log(er.PolicyResponse.Rules[0].Message)
|
||||||
assert.Equal(t, er.PolicyResponse.Rules[0].Message, expectedErrorStr)
|
assert.Equal(t, er.PolicyResponse.Rules[0].Message, expectedErrorStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_variableSubstitutionCLI(t *testing.T) {
|
||||||
|
resourceRaw := []byte(`{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"metadata": {
|
||||||
|
"name": "nginx-config-test"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"image": "nginx:latest",
|
||||||
|
"name": "test-nginx"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
policyraw := []byte(`{
|
||||||
|
"apiVersion": "kyverno.io/v1",
|
||||||
|
"kind": "ClusterPolicy",
|
||||||
|
"metadata": {
|
||||||
|
"name": "cm-variable-example"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"name": "example-configmap-lookup",
|
||||||
|
"context": [
|
||||||
|
{
|
||||||
|
"name": "dictionary",
|
||||||
|
"configMap": {
|
||||||
|
"name": "mycmap",
|
||||||
|
"namespace": "default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": {
|
||||||
|
"resources": {
|
||||||
|
"kinds": [
|
||||||
|
"Pod"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mutate": {
|
||||||
|
"patchStrategicMerge": {
|
||||||
|
"metadata": {
|
||||||
|
"labels": {
|
||||||
|
"my-environment-name": "{{dictionary.data.env}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
configMapVariableContext := store.Context{
|
||||||
|
Policies: []store.Policy{
|
||||||
|
{
|
||||||
|
Name: "cm-variable-example",
|
||||||
|
Rules: []store.Rule{
|
||||||
|
{
|
||||||
|
Name: "example-configmap-lookup",
|
||||||
|
Values: map[string]string{
|
||||||
|
"dictionary.data.env": "dev1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedPatch := []byte(`{"op":"add","path":"/metadata/labels","value":{"my-environment-name":"dev1"}}`)
|
||||||
|
|
||||||
|
store.SetContext(configMapVariableContext)
|
||||||
|
store.SetMock(true)
|
||||||
|
var policy kyverno.ClusterPolicy
|
||||||
|
err := json.Unmarshal(policyraw, &policy)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
ctx := context.NewContext()
|
||||||
|
err = ctx.AddResource(resourceRaw)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
policyContext := &PolicyContext{
|
||||||
|
Policy: policy,
|
||||||
|
JSONContext: ctx,
|
||||||
|
NewResource: *resourceUnstructured,
|
||||||
|
}
|
||||||
|
|
||||||
|
er := Mutate(policyContext)
|
||||||
|
t.Log(string(expectedPatch))
|
||||||
|
t.Log(string(er.PolicyResponse.Rules[0].Patches[0]))
|
||||||
|
if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) {
|
||||||
|
t.Error("patches dont match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSONContext.Restore()
|
ctx.JSONContext.Restore()
|
||||||
if err := LoadContext(log, rule.Context, ctx.ResourceCache, ctx); err != nil {
|
if err := LoadContext(log, rule.Context, ctx.ResourceCache, ctx, rule.Name); err != nil {
|
||||||
log.Error(err, "failed to load context")
|
log.Error(err, "failed to load context")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/utils"
|
"github.com/kyverno/kyverno/pkg/engine/utils"
|
||||||
|
"github.com/kyverno/kyverno/pkg/kyverno/store"
|
||||||
utils2 "github.com/kyverno/kyverno/pkg/utils"
|
utils2 "github.com/kyverno/kyverno/pkg/utils"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"k8s.io/api/admission/v1beta1"
|
"k8s.io/api/admission/v1beta1"
|
||||||
|
@ -2141,3 +2142,103 @@ func executeTest(t *testing.T, err error, test testCase) {
|
||||||
t.Errorf("Testcase has failed, policy: %v", policy.Name)
|
t.Errorf("Testcase has failed, policy: %v", policy.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidate_context_variable_substitution_CLI(t *testing.T) {
|
||||||
|
rawPolicy := []byte(`{
|
||||||
|
"apiVersion": "kyverno.io/v1",
|
||||||
|
"kind": "ClusterPolicy",
|
||||||
|
"metadata": {
|
||||||
|
"name": "restrict-pod-count"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"validationFailureAction": "enforce",
|
||||||
|
"background": false,
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"name": "restrict-pod-count",
|
||||||
|
"match": {
|
||||||
|
"resources": {
|
||||||
|
"kinds": [
|
||||||
|
"Pod"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context": [
|
||||||
|
{
|
||||||
|
"name": "podcounts",
|
||||||
|
"apiCall": {
|
||||||
|
"urlPath": "/api/v1/pods",
|
||||||
|
"jmesPath": "items[?spec.nodeName=='minikube'] | length(@)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validate": {
|
||||||
|
"message": "restrict pod counts to be no more than 10 on node minikube",
|
||||||
|
"deny": {
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "{{ podcounts }}",
|
||||||
|
"operator": "GreaterThanOrEquals",
|
||||||
|
"value": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
rawResource := []byte(`
|
||||||
|
{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Pod",
|
||||||
|
"metadata": {
|
||||||
|
"name": "nginx-config-test"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"image": "nginx:latest",
|
||||||
|
"name": "test-nginx"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
configMapVariableContext := store.Context{
|
||||||
|
Policies: []store.Policy{
|
||||||
|
{
|
||||||
|
Name: "restrict-pod-count",
|
||||||
|
Rules: []store.Rule{
|
||||||
|
{
|
||||||
|
Name: "restrict-pod-count",
|
||||||
|
Values: map[string]string{
|
||||||
|
"podcounts": "12",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
store.SetContext(configMapVariableContext)
|
||||||
|
store.SetMock(true)
|
||||||
|
|
||||||
|
var policy kyverno.ClusterPolicy
|
||||||
|
err := json.Unmarshal(rawPolicy, &policy)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
resourceUnstructured, err := utils.ConvertToUnstructured(rawResource)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
msgs := []string{
|
||||||
|
"restrict pod counts to be no more than 10 on node minikube",
|
||||||
|
}
|
||||||
|
er := Validate(&PolicyContext{Policy: policy, NewResource: *resourceUnstructured, JSONContext: context.NewContext()})
|
||||||
|
for index, r := range er.PolicyResponse.Rules {
|
||||||
|
assert.Equal(t, r.Message, msgs[index])
|
||||||
|
}
|
||||||
|
assert.Assert(t, !er.IsSuccessful())
|
||||||
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext *engine.
|
||||||
}
|
}
|
||||||
|
|
||||||
// add configmap json data to context
|
// add configmap json data to context
|
||||||
if err := engine.LoadContext(log, rule.Context, resCache, policyContext); err != nil {
|
if err := engine.LoadContext(log, rule.Context, resCache, policyContext, rule.Name); err != nil {
|
||||||
log.Error(err, "cannot add configmaps to context")
|
log.Error(err, "cannot add configmaps to context")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||||
"github.com/kyverno/kyverno/pkg/kyverno/common"
|
"github.com/kyverno/kyverno/pkg/kyverno/common"
|
||||||
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
|
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
|
||||||
|
"github.com/kyverno/kyverno/pkg/kyverno/store"
|
||||||
"github.com/kyverno/kyverno/pkg/openapi"
|
"github.com/kyverno/kyverno/pkg/openapi"
|
||||||
policy2 "github.com/kyverno/kyverno/pkg/policy"
|
policy2 "github.com/kyverno/kyverno/pkg/policy"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -148,6 +149,7 @@ func Command() *cobra.Command {
|
||||||
func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool, mutateLogPath string,
|
func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool, mutateLogPath string,
|
||||||
variablesString string, valuesFile string, namespace string, policyPaths []string, stdin bool) (validateEngineResponses []*response.EngineResponse, rc *resultCounts, resources []*unstructured.Unstructured, skippedPolicies []SkippedPolicy, err error) {
|
variablesString string, valuesFile string, namespace string, policyPaths []string, stdin bool) (validateEngineResponses []*response.EngineResponse, rc *resultCounts, resources []*unstructured.Unstructured, skippedPolicies []SkippedPolicy, err error) {
|
||||||
|
|
||||||
|
store.SetMock(true)
|
||||||
kubernetesConfig := genericclioptions.NewConfigFlags(true)
|
kubernetesConfig := genericclioptions.NewConfigFlags(true)
|
||||||
fs := memfs.New()
|
fs := memfs.New()
|
||||||
|
|
||||||
|
@ -286,7 +288,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
|
||||||
thisPolicyResourceValues[k] = v
|
thisPolicyResourceValues[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(common.PolicyHasVariables(*policy)) > 0 && len(thisPolicyResourceValues) == 0 {
|
if len(common.PolicyHasVariables(*policy)) > 0 && len(thisPolicyResourceValues) == 0 && len(store.GetContext().Policies) == 0 {
|
||||||
return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError(fmt.Sprintf("policy %s have variables. pass the values for the variables using set/values_file flag", policy.Name), err)
|
return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError(fmt.Sprintf("policy %s have variables. pass the values for the variables using set/values_file flag", policy.Name), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,13 @@ import (
|
||||||
"github.com/go-git/go-billy/v5"
|
"github.com/go-git/go-billy/v5"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||||
|
pkgcommon "github.com/kyverno/kyverno/pkg/common"
|
||||||
client "github.com/kyverno/kyverno/pkg/dclient"
|
client "github.com/kyverno/kyverno/pkg/dclient"
|
||||||
"github.com/kyverno/kyverno/pkg/engine"
|
"github.com/kyverno/kyverno/pkg/engine"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||||
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
|
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
|
||||||
|
"github.com/kyverno/kyverno/pkg/kyverno/store"
|
||||||
"github.com/kyverno/kyverno/pkg/policymutation"
|
"github.com/kyverno/kyverno/pkg/policymutation"
|
||||||
"github.com/kyverno/kyverno/pkg/utils"
|
"github.com/kyverno/kyverno/pkg/utils"
|
||||||
ut "github.com/kyverno/kyverno/pkg/utils"
|
ut "github.com/kyverno/kyverno/pkg/utils"
|
||||||
|
@ -32,15 +34,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetPolicies - Extracting the policies from multiple YAML
|
// GetPolicies - Extracting the policies from multiple YAML
|
||||||
|
|
||||||
type Resource struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Values map[string]string `json:"values"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Policy struct {
|
type Policy struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Resources []Resource `json:"resources"`
|
Resources []Resource `json:"resources"`
|
||||||
|
Rules []Rule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rule struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Values map[string]string `json:"values"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Values struct {
|
type Values struct {
|
||||||
|
@ -48,6 +50,11 @@ type Values struct {
|
||||||
NamespaceSelectors []NamespaceSelector `json:"namespaceSelector"`
|
NamespaceSelectors []NamespaceSelector `json:"namespaceSelector"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Resource struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Values map[string]string `json:"values"`
|
||||||
|
}
|
||||||
|
|
||||||
type NamespaceSelector struct {
|
type NamespaceSelector struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Labels map[string]string `json:"labels"`
|
Labels map[string]string `json:"labels"`
|
||||||
|
@ -305,9 +312,9 @@ func RemoveDuplicateVariables(matches [][]string) string {
|
||||||
return variableStr
|
return variableStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVariable - get the variables from console/file
|
|
||||||
func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit bool, policyresoucePath string) (map[string]string, map[string]map[string]Resource, map[string]map[string]string, error) {
|
func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit bool, policyresoucePath string) (map[string]string, map[string]map[string]Resource, map[string]map[string]string, error) {
|
||||||
valuesMap := make(map[string]map[string]Resource)
|
valuesMapResource := make(map[string]map[string]Resource)
|
||||||
|
valuesMapRule := make(map[string]map[string]Rule)
|
||||||
namespaceSelectorMap := make(map[string]map[string]string)
|
namespaceSelectorMap := make(map[string]map[string]string)
|
||||||
variables := make(map[string]string)
|
variables := make(map[string]string)
|
||||||
var yamlFile []byte
|
var yamlFile []byte
|
||||||
|
@ -331,25 +338,33 @@ func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return variables, valuesMap, namespaceSelectorMap, sanitizederror.NewWithError("unable to read yaml", err)
|
return variables, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("unable to read yaml", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
valuesBytes, err := yaml.ToJSON(yamlFile)
|
valuesBytes, err := yaml.ToJSON(yamlFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return variables, valuesMap, namespaceSelectorMap, sanitizederror.NewWithError("failed to convert json", err)
|
return variables, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("failed to convert json", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
values := &Values{}
|
values := &Values{}
|
||||||
if err := json.Unmarshal(valuesBytes, values); err != nil {
|
if err := json.Unmarshal(valuesBytes, values); err != nil {
|
||||||
return variables, valuesMap, namespaceSelectorMap, sanitizederror.NewWithError("failed to decode yaml", err)
|
return variables, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("failed to decode yaml", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range values.Policies {
|
for _, p := range values.Policies {
|
||||||
pmap := make(map[string]Resource)
|
resourceMap := make(map[string]Resource)
|
||||||
for _, r := range p.Resources {
|
for _, r := range p.Resources {
|
||||||
pmap[r.Name] = r
|
resourceMap[r.Name] = r
|
||||||
|
}
|
||||||
|
valuesMapResource[p.Name] = resourceMap
|
||||||
|
|
||||||
|
if p.Rules != nil {
|
||||||
|
ruleMap := make(map[string]Rule)
|
||||||
|
for _, r := range p.Rules {
|
||||||
|
ruleMap[r.Name] = r
|
||||||
|
}
|
||||||
|
valuesMapRule[p.Name] = ruleMap
|
||||||
}
|
}
|
||||||
valuesMap[p.Name] = pmap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range values.NamespaceSelectors {
|
for _, n := range values.NamespaceSelectors {
|
||||||
|
@ -357,7 +372,26 @@ func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return variables, valuesMap, namespaceSelectorMap, nil
|
storePolices := 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
storePolices = append(storePolices, store.Policy{
|
||||||
|
Name: policyName,
|
||||||
|
Rules: storeRules,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
store.SetContext(store.Context{
|
||||||
|
Policies: storePolices,
|
||||||
|
})
|
||||||
|
|
||||||
|
return variables, valuesMapResource, namespaceSelectorMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MutatePolices - function to apply mutation on policies
|
// MutatePolices - function to apply mutation on policies
|
||||||
|
@ -409,32 +443,7 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
|
||||||
|
|
||||||
ctx := context.NewContext()
|
ctx := context.NewContext()
|
||||||
for key, value := range variables {
|
for key, value := range variables {
|
||||||
var subString string
|
jsonData := pkgcommon.VariableToJSON(key, value)
|
||||||
splitBySlash := strings.Split(key, "\"")
|
|
||||||
if len(splitBySlash) > 1 {
|
|
||||||
subString = splitBySlash[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
startString := ""
|
|
||||||
endString := ""
|
|
||||||
lenOfVariableString := 0
|
|
||||||
addedSlashString := false
|
|
||||||
for _, k := range strings.Split(splitBySlash[0], ".") {
|
|
||||||
if k != "" {
|
|
||||||
startString += fmt.Sprintf(`{"%s":`, k)
|
|
||||||
endString += `}`
|
|
||||||
lenOfVariableString = lenOfVariableString + len(k) + 1
|
|
||||||
if lenOfVariableString >= len(splitBySlash[0]) && len(splitBySlash) > 1 && addedSlashString == false {
|
|
||||||
startString += fmt.Sprintf(`{"%s":`, subString)
|
|
||||||
endString += `}`
|
|
||||||
addedSlashString = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
midString := fmt.Sprintf(`"%s"`, value)
|
|
||||||
finalString := startString + midString + endString
|
|
||||||
var jsonData = []byte(finalString)
|
|
||||||
ctx.AddJSON(jsonData)
|
ctx.AddJSON(jsonData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
56
pkg/kyverno/store/store.go
Normal file
56
pkg/kyverno/store/store.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package store
|
||||||
|
|
||||||
|
var Mock bool
|
||||||
|
var ContextVar Context
|
||||||
|
|
||||||
|
func SetMock(mock bool) {
|
||||||
|
Mock = mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMock() bool {
|
||||||
|
return Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetContext(context Context) {
|
||||||
|
ContextVar = context
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetContext() Context {
|
||||||
|
return ContextVar
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPolicyFromContext(policyName string) *Policy {
|
||||||
|
for _, policy := range ContextVar.Policies {
|
||||||
|
if policy.Name == policyName {
|
||||||
|
return &policy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPolicyRuleFromContext(policyName string, ruleName string) *Rule {
|
||||||
|
for _, policy := range ContextVar.Policies {
|
||||||
|
if policy.Name == policyName {
|
||||||
|
for _, rule := range policy.Rules {
|
||||||
|
if rule.Name == ruleName {
|
||||||
|
return &rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
Policies []Policy `json:"policies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Policy struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Rules []Rule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rule struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Values map[string]string `json:"values"`
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue