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

744 resolve merge conflicts

This commit is contained in:
shravan 2020-04-13 20:31:40 +05:30
commit b6f01db0b1
5 changed files with 245 additions and 77 deletions

View file

@ -1,6 +1,7 @@
package engine
import (
"encoding/json"
"reflect"
"strings"
"time"
@ -101,18 +102,19 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
if reflect.DeepEqual(policyContext.AdmissionInfo, kyverno.RequestInfo{}) {
continue
}
}
if strings.Contains(PodControllers, resource.GetKind()) {
if strings.Contains(PodControllers, resource.GetKind()) {
if !patchedResourceHasPodControllerAnnotation(patchedResource) {
var ruleResponse response.RuleResponse
ruleResponse, patchedResource = mutate.ProcessOverlay(logger, rule.Name, podTemplateRule, patchedResource)
ruleResponse, patchedResource = mutate.ProcessOverlay(logger, "podControllerAnnotation", podTemplateRule.Mutation.Overlay, patchedResource)
if !ruleResponse.Success {
logger.Info("failed to insert annotation for podTemplate", "error", ruleResponse.Message)
continue
}
if ruleResponse.Success && ruleResponse.Patches != nil {
logger.V(2).Info("inserted annotation for podTemplate")
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
} else {
if ruleResponse.Success && ruleResponse.Patches != nil {
logger.V(2).Info("inserted annotation for podTemplate")
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
}
}
}
}
@ -120,6 +122,24 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
resp.PatchedResource = patchedResource
return resp
}
func patchedResourceHasPodControllerAnnotation(resource unstructured.Unstructured) bool {
var podController struct {
Spec struct {
Template struct {
Metadata struct {
Annotations map[string]interface{} `json:"annotations"`
} `json:"metadata"`
} `json:"template"`
} `json:"spec"`
}
resourceRaw, _ := json.Marshal(resource.Object)
_ = json.Unmarshal(resourceRaw, &podController)
_, ok := podController.Spec.Template.Metadata.Annotations[PodTemplateAnnotation]
return ok
}
func incrementAppliedRuleCount(resp *response.EngineResponse) {
resp.PolicyResponse.RulesAppliedCount++
}
@ -150,7 +170,7 @@ var podTemplateRule = kyverno.Rule{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"+(pod-policies.kyverno.io/autogen-applied)": "true",
"+(" + PodTemplateAnnotation + ")": "true",
},
},
},

View file

@ -7,6 +7,9 @@ import (
"os"
"path/filepath"
"regexp"
"time"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/utils"
@ -18,8 +21,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/apimachinery/pkg/util/yaml"
"github.com/nirmata/kyverno/pkg/engine"
@ -87,15 +88,19 @@ func Command() *cobra.Command {
}
}
var dClient discovery.CachedDiscoveryInterface
var dClient *client.Client
if cluster {
dClient, err = kubernetesConfig.ToDiscoveryClient()
restConfig, err := kubernetesConfig.ToRESTConfig()
if err != nil {
return sanitizedError.New(fmt.Errorf("Issues with kubernetes Config").Error())
return err
}
dClient, err = client.NewClient(restConfig, 10*time.Second, make(chan struct{}), log.Log)
if err != nil {
return err
}
}
resources, err := getResources(policies, resourcePaths, dClient, openAPIController)
resources, err := getResources(policies, resourcePaths, dClient)
if err != nil {
return sanitizedError.New(fmt.Errorf("Issues fetching resources").Error())
}
@ -123,7 +128,7 @@ func Command() *cobra.Command {
return cmd
}
func getResources(policies []*v1.ClusterPolicy, resourcePaths []string, dClient discovery.CachedDiscoveryInterface, openAPIController *openapi.Controller) ([]*unstructured.Unstructured, error) {
func getResources(policies []*v1.ClusterPolicy, resourcePaths []string, dClient *client.Client) ([]*unstructured.Unstructured, error) {
var resources []*unstructured.Unstructured
var err error
@ -142,7 +147,7 @@ func getResources(policies []*v1.ClusterPolicy, resourcePaths []string, dClient
resourceTypes = append(resourceTypes, kind)
}
resources, err = getResourcesOfTypeFromCluster(resourceTypes, dClient, openAPIController)
resources, err = getResourcesOfTypeFromCluster(resourceTypes, dClient)
if err != nil {
return nil, err
}
@ -160,27 +165,11 @@ func getResources(policies []*v1.ClusterPolicy, resourcePaths []string, dClient
return resources, nil
}
func getResourcesOfTypeFromCluster(resourceTypes []string, dClient discovery.CachedDiscoveryInterface, openAPIController *openapi.Controller) ([]*unstructured.Unstructured, error) {
func getResourcesOfTypeFromCluster(resourceTypes []string, dClient *client.Client) ([]*unstructured.Unstructured, error) {
var resources []*unstructured.Unstructured
for _, kind := range resourceTypes {
// TODO use lister interface
endpoint, err := getListEndpointForKind(kind, openAPIController)
if err != nil {
return nil, err
}
listObjectRaw, err := dClient.RESTClient().Get().RequestURI(endpoint).Do().Raw()
if err != nil {
return nil, err
}
listObject, err := engineutils.ConvertToUnstructured(listObjectRaw)
if err != nil {
return nil, err
}
resourceList, err := listObject.ToList()
resourceList, err := dClient.ListResource(kind, "", nil)
if err != nil {
return nil, err
}

View file

@ -1,37 +0,0 @@
package apply
import (
"fmt"
"strings"
"github.com/nirmata/kyverno/pkg/openapi"
)
func getListEndpointForKind(kind string, openAPIController *openapi.Controller) (string, error) {
definitionName := openAPIController.GetDefinitionNameFromKind(kind)
definitionNameWithoutPrefix := strings.Replace(definitionName, "io.k8s.", "", -1)
parts := strings.Split(definitionNameWithoutPrefix, ".")
definitionPrefix := strings.Join(parts[:len(parts)-1], ".")
defPrefixToApiPrefix := map[string]string{
"api.core.v1": "/api/v1",
"api.apps.v1": "/apis/apps/v1",
"api.batch.v1": "/apis/batch/v1",
"api.admissionregistration.v1": "/apis/admissionregistration.k8s.io/v1",
"kube-aggregator.pkg.apis.apiregistration.v1": "/apis/apiregistration.k8s.io/v1",
"apiextensions-apiserver.pkg.apis.apiextensions.v1": "/apis/apiextensions.k8s.io/v1",
"api.autoscaling.v1": "/apis/autoscaling/v1/",
"api.storage.v1": "/apis/storage.k8s.io/v1",
"api.coordination.v1": "/apis/coordination.k8s.io/v1",
"api.scheduling.v1": "/apis/scheduling.k8s.io/v1",
"api.rbac.v1": "/apis/rbac.authorization.k8s.io/v1",
}
if defPrefixToApiPrefix[definitionPrefix] == "" {
return "", fmt.Errorf("Unsupported resource type %v", kind)
}
return defPrefixToApiPrefix[definitionPrefix] + "/" + strings.ToLower(kind) + "s", nil
}

View file

@ -7,6 +7,8 @@ import (
"reflect"
"strings"
"github.com/minio/minio/pkg/wildcard"
"github.com/nirmata/kyverno/pkg/openapi"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
@ -42,15 +44,21 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro
}
for i, rule := range p.Spec.Rules {
// only one type of rule is allowed per rule
if err := validateRuleType(rule); err != nil {
return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
}
// validate resource description
if path, err := validateResources(rule); err != nil {
return fmt.Errorf("path: spec.rules[%d].%s: %v", i, path, err)
}
// validate rule types
// only one type of rule is allowed per rule
if err := validateRuleType(rule); err != nil {
// as there are more than 1 operation in rule, not need to evaluate it further
return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
}
if doesMatchAndExcludeConflict(rule) {
return fmt.Errorf("path: spec.rules[%v]: rule is matching an empty set", rule.Name)
}
// validate rule actions
// - Mutate
// - Validate
@ -82,6 +90,121 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro
return nil
}
// doesMatchAndExcludeConflict checks if the resultant
// of match and exclude block is not an empty set
func doesMatchAndExcludeConflict(rule kyverno.Rule) bool {
if reflect.DeepEqual(rule.MatchResources, kyverno.MatchResources{}) {
return true
}
if reflect.DeepEqual(rule.ExcludeResources, kyverno.ExcludeResources{}) {
return false
}
excludeRoles := make(map[string]bool)
for _, role := range rule.ExcludeResources.UserInfo.Roles {
excludeRoles[role] = true
}
excludeClusterRoles := make(map[string]bool)
for _, clusterRoles := range rule.ExcludeResources.UserInfo.ClusterRoles {
excludeClusterRoles[clusterRoles] = true
}
excludeSubjects := make(map[string]bool)
for _, subject := range rule.ExcludeResources.UserInfo.Subjects {
subjectRaw, _ := json.Marshal(subject)
excludeSubjects[string(subjectRaw)] = true
}
excludeKinds := make(map[string]bool)
for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds {
excludeKinds[kind] = true
}
excludeNamespaces := make(map[string]bool)
for _, namespace := range rule.ExcludeResources.ResourceDescription.Namespaces {
excludeNamespaces[namespace] = true
}
excludeMatchExpressions := make(map[string]bool)
if rule.ExcludeResources.ResourceDescription.Selector != nil {
for _, matchExpression := range rule.ExcludeResources.ResourceDescription.Selector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
excludeMatchExpressions[string(matchExpressionRaw)] = true
}
}
if len(excludeRoles) > 0 {
for _, role := range rule.MatchResources.UserInfo.Roles {
if !excludeRoles[role] {
return false
}
}
}
if len(excludeClusterRoles) > 0 {
for _, clusterRole := range rule.MatchResources.UserInfo.ClusterRoles {
if !excludeClusterRoles[clusterRole] {
return false
}
}
}
if len(excludeSubjects) > 0 {
for _, subject := range rule.MatchResources.UserInfo.Subjects {
subjectRaw, _ := json.Marshal(subject)
if !excludeSubjects[string(subjectRaw)] {
return false
}
}
}
if rule.ExcludeResources.ResourceDescription.Name != "" {
if !wildcard.Match(rule.ExcludeResources.ResourceDescription.Name, rule.MatchResources.ResourceDescription.Name) {
return false
}
}
if len(excludeNamespaces) > 0 {
for _, namespace := range rule.MatchResources.ResourceDescription.Namespaces {
if !excludeNamespaces[namespace] {
return false
}
}
}
if len(excludeKinds) > 0 {
for _, kind := range rule.MatchResources.ResourceDescription.Kinds {
if !excludeKinds[kind] {
return false
}
}
}
if rule.MatchResources.ResourceDescription.Selector != nil && rule.ExcludeResources.ResourceDescription.Selector != nil {
if len(excludeMatchExpressions) > 0 {
for _, matchExpression := range rule.MatchResources.ResourceDescription.Selector.MatchExpressions {
matchExpressionRaw, _ := json.Marshal(matchExpression)
if !excludeMatchExpressions[string(matchExpressionRaw)] {
return false
}
}
}
if len(rule.ExcludeResources.ResourceDescription.Selector.MatchLabels) > 0 {
for label, value := range rule.MatchResources.ResourceDescription.Selector.MatchLabels {
if rule.ExcludeResources.ResourceDescription.Selector.MatchLabels[label] != value {
return false
}
}
}
}
return true
}
func ruleOnlyDealsWithResourceMetaData(rule kyverno.Rule) bool {
overlayMap, _ := rule.Mutation.Overlay.(map[string]interface{})
for k := range overlayMap {

View file

@ -960,3 +960,76 @@ func Test_ruleOnlyDealsWithResourceMetaData(t *testing.T) {
}
}
}
func Test_doesMatchExcludeConflict(t *testing.T) {
testcases := []struct {
description string
rule []byte
expectedOutput bool
}{
{
description: "Same match and exclude",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: true,
},
{
description: "Failed to exclude kind",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude name",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something-*","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude namespace",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something3","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude labels",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"higha"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude expression",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["databases"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude subjects",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something2","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude clusterroles",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something3","something1"],"roles":["something","something1"]}}`),
expectedOutput: false,
},
{
description: "Failed to exclude roles",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something","something1"]},"exclude":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"],"selector":{"matchLabels":{"memory":"high"},"matchExpressions":[{"key":"tier","operator":"In","values":["database"]}]}},"subjects":[{"name":"something","kind":"something","Namespace":"something","apiGroup":"something"},{"name":"something1","kind":"something1","Namespace":"something1","apiGroup":"something1"}],"clusterroles":["something","something1"],"roles":["something3","something1"]}}`),
expectedOutput: false,
},
{
description: "simple",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"something","namespaces":["something","something1"]}},"exclude":{"resources":{"kinds":["Pod","Namespace","Job"],"name":"some*","namespaces":["something","something1","something2"]}}}`),
expectedOutput: true,
},
{
description: "simple - fail",
rule: []byte(`{"name":"set-image-pull-policy-2","match":{"resources":{"kinds":["Pod","Namespace"],"name":"somxething","namespaces":["something","something1"]}},"exclude":{"resources":{"kinds":["Pod","Namespace","Job"],"name":"some*","namespaces":["something","something1","something2"]}}}`),
expectedOutput: false,
},
}
for i, testcase := range testcases {
var rule kyverno.Rule
_ = json.Unmarshal(testcase.rule, &rule)
if doesMatchAndExcludeConflict(rule) != testcase.expectedOutput {
t.Errorf("Testcase [%d] failed - description - %v", i+1, testcase.description)
}
}
}