1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/engine/utils.go

375 lines
8.4 KiB
Go
Raw Normal View History

package engine
import (
"encoding/json"
2019-06-10 17:32:26 +03:00
"fmt"
"strconv"
2019-04-30 18:54:08 -07:00
"strings"
2019-08-19 18:57:19 -07:00
"time"
2019-07-23 23:34:03 -04:00
"github.com/golang/glog"
"github.com/minio/minio/pkg/wildcard"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
2019-10-21 14:22:31 -07:00
"github.com/nirmata/kyverno/pkg/engine/anchor"
2019-07-31 17:43:46 -07:00
"github.com/nirmata/kyverno/pkg/utils"
2019-06-25 22:53:18 -07:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)
2019-08-19 18:57:19 -07:00
//EngineStats stores in the statistics for a single application of resource
type EngineStats struct {
// average time required to process the policy rules on a resource
ExecutionTime time.Duration
// Count of rules that were applied succesfully
RulesAppliedCount int
}
2019-08-09 12:59:37 -07:00
//MatchesResourceDescription checks if the resource matches resource desription of the rule or not
func MatchesResourceDescription(resource unstructured.Unstructured, rule kyverno.Rule) bool {
2019-08-09 12:59:37 -07:00
matches := rule.MatchResources.ResourceDescription
exclude := rule.ExcludeResources.ResourceDescription
if !findKind(matches.Kinds, resource.GetKind()) {
2019-08-09 12:59:37 -07:00
return false
}
name := resource.GetName()
2019-08-09 12:59:37 -07:00
namespace := resource.GetNamespace()
if matches.Name != "" {
2019-08-09 12:59:37 -07:00
// Matches
if !wildcard.Match(matches.Name, name) {
2019-08-09 12:59:37 -07:00
return false
}
}
2019-08-09 12:59:37 -07:00
// Matches
// check if the resource namespace is defined in the list of namespace pattern
if len(matches.Namespaces) > 0 && !utils.ContainsNamepace(matches.Namespaces, namespace) {
2019-08-09 12:59:37 -07:00
return false
}
2019-08-09 12:59:37 -07:00
// Matches
if matches.Selector != nil {
selector, err := metav1.LabelSelectorAsSelector(matches.Selector)
if err != nil {
glog.Error(err)
return false
}
if !selector.Matches(labels.Set(resource.GetLabels())) {
return false
}
}
2019-09-03 19:31:42 -07:00
excludeName := func(name string) Condition {
if exclude.Name == "" {
return NotEvaluate
}
if wildcard.Match(exclude.Name, name) {
return Skip
}
return Process
}
excludeNamespace := func(namespace string) Condition {
if len(exclude.Namespaces) == 0 {
return NotEvaluate
}
if utils.ContainsNamepace(exclude.Namespaces, namespace) {
2019-09-03 19:31:42 -07:00
return Skip
}
return Process
}
excludeSelector := func(labelsMap map[string]string) Condition {
if exclude.Selector == nil {
return NotEvaluate
}
2019-08-09 12:59:37 -07:00
selector, err := metav1.LabelSelectorAsSelector(exclude.Selector)
// if the label selector is incorrect, should be fail or
if err != nil {
glog.Error(err)
2019-09-03 19:31:42 -07:00
return Skip
2019-08-09 12:59:37 -07:00
}
2019-09-03 19:31:42 -07:00
if selector.Matches(labels.Set(labelsMap)) {
return Skip
2019-08-09 12:59:37 -07:00
}
2019-09-03 19:31:42 -07:00
return Process
2019-08-09 12:59:37 -07:00
}
2019-09-03 19:31:42 -07:00
excludeKind := func(kind string) Condition {
if len(exclude.Kinds) == 0 {
return NotEvaluate
}
2019-09-03 19:31:42 -07:00
if findKind(exclude.Kinds, kind) {
return Skip
}
2019-09-03 19:31:42 -07:00
return Process
}
2019-09-03 19:31:42 -07:00
// 0 -> dont check
// 1 -> is not to be exclude
// 2 -> to be exclude
excludeEval := []Condition{}
2019-09-03 19:31:42 -07:00
if ret := excludeName(resource.GetName()); ret != NotEvaluate {
excludeEval = append(excludeEval, ret)
}
if ret := excludeNamespace(resource.GetNamespace()); ret != NotEvaluate {
excludeEval = append(excludeEval, ret)
}
if ret := excludeSelector(resource.GetLabels()); ret != NotEvaluate {
excludeEval = append(excludeEval, ret)
}
if ret := excludeKind(resource.GetKind()); ret != NotEvaluate {
excludeEval = append(excludeEval, ret)
}
// Filtered NotEvaluate
2019-09-04 09:56:44 -07:00
if len(excludeEval) == 0 {
// nothing to exclude
return true
}
return func() bool {
2019-09-03 19:31:42 -07:00
for _, ret := range excludeEval {
if ret == Process {
return true
}
}
return false
}()
}
type Condition int
const (
NotEvaluate Condition = 0
Process Condition = 1
Skip Condition = 2
)
2019-07-26 15:54:42 -07:00
// ParseResourceInfoFromObject get kind/namepace/name from resource
func ParseResourceInfoFromObject(rawResource []byte) string {
kind := ParseKindFromObject(rawResource)
namespace := ParseNamespaceFromObject(rawResource)
name := ParseNameFromObject(rawResource)
return strings.Join([]string{kind, namespace, name}, "/")
}
2019-06-26 12:19:11 -07:00
//ParseKindFromObject get kind from resource
func ParseKindFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
return objectJSON["kind"].(string)
}
2019-06-05 17:43:59 -07:00
//ParseNameFromObject extracts resource name from JSON obj
func ParseNameFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
2019-07-25 13:14:55 -04:00
meta, ok := objectJSON["metadata"]
if !ok {
return ""
}
2019-07-25 13:14:55 -04:00
metaMap, ok := meta.(map[string]interface{})
if !ok {
return ""
}
if name, ok := metaMap["name"].(string); ok {
return name
}
return ""
}
2019-06-05 17:43:59 -07:00
// ParseNamespaceFromObject extracts the namespace from the JSON obj
func ParseNamespaceFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
2019-07-25 13:14:55 -04:00
meta, ok := objectJSON["metadata"]
if !ok {
return ""
}
metaMap, ok := meta.(map[string]interface{})
if !ok {
return ""
}
2019-07-25 13:14:55 -04:00
if name, ok := metaMap["namespace"].(string); ok {
return name
}
2019-07-25 13:14:55 -04:00
return ""
}
2019-04-30 17:26:50 -07:00
// getAnchorsFromMap gets the conditional anchor map
2019-06-05 17:43:59 -07:00
func getAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
2019-05-21 18:27:56 +03:00
result := make(map[string]interface{})
for key, value := range anchorsMap {
if anchor.IsConditionAnchor(key) {
2019-05-21 18:27:56 +03:00
result[key] = value
}
}
return result
}
2019-10-31 20:38:24 -07:00
// getAnchorAndElementsFromMap gets the condition anchor map and resource map without anchor
func getAnchorAndElementsFromMap(anchorsMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
2019-07-26 12:01:09 -07:00
anchors := make(map[string]interface{})
elementsWithoutanchor := make(map[string]interface{})
for key, value := range anchorsMap {
2019-11-05 11:04:43 -08:00
if anchor.IsConditionAnchor(key) {
2019-07-26 12:01:09 -07:00
anchors[key] = value
2019-10-21 14:22:31 -07:00
} else if !anchor.IsAddingAnchor(key) {
2019-07-26 12:01:09 -07:00
elementsWithoutanchor[key] = value
}
}
return anchors, elementsWithoutanchor
}
2019-06-13 17:20:00 +03:00
func getAnchorFromMap(anchorsMap map[string]interface{}) (string, interface{}) {
for key, value := range anchorsMap {
2019-10-21 14:22:31 -07:00
if anchor.IsConditionAnchor(key) || anchor.IsExistanceAnchor(key) {
2019-06-13 17:20:00 +03:00
return key, value
}
}
return "", nil
}
func findKind(kinds []string, kindGVK string) bool {
for _, kind := range kinds {
if kind == kindGVK {
return true
}
}
return false
}
2019-10-21 14:22:31 -07:00
// func isConditionAnchor(str string) bool {
// if len(str) < 2 {
// return false
// }
2019-10-21 14:22:31 -07:00
// return (str[0] == '(' && str[len(str)-1] == ')')
// }
func getRawKeyIfWrappedWithAttributes(str string) string {
if len(str) < 2 {
return str
}
if str[0] == '(' && str[len(str)-1] == ')' {
return str[1 : len(str)-1]
2019-10-01 13:08:34 -07:00
} else if (str[0] == '$' || str[0] == '^' || str[0] == '+' || str[0] == '=') && (str[1] == '(' && str[len(str)-1] == ')') {
return str[2 : len(str)-1]
} else {
return str
}
}
func isStringIsReference(str string) bool {
if len(str) < len(referenceSign) {
return false
}
return str[0] == '$' && str[1] == '(' && str[len(str)-1] == ')'
}
// Checks if array object matches anchors. If not - skip - return true
func skipArrayObject(object, anchors map[string]interface{}) bool {
for key, pattern := range anchors {
key = key[1 : len(key)-1]
value, ok := object[key]
if !ok {
return true
}
if !ValidateValueWithPattern(value, pattern) {
return true
}
}
return false
}
2019-06-10 17:32:26 +03:00
// removeAnchor remove special characters around anchored key
func removeAnchor(key string) string {
2019-10-21 14:22:31 -07:00
if anchor.IsConditionAnchor(key) {
return key[1 : len(key)-1]
}
2019-10-21 14:22:31 -07:00
if anchor.IsExistanceAnchor(key) || anchor.IsAddingAnchor(key) || anchor.IsEqualityAnchor(key) || anchor.IsNegationAnchor(key) {
2019-06-13 17:20:00 +03:00
return key[2 : len(key)-1]
}
return key
}
2019-06-10 17:32:26 +03:00
// convertToFloat converts string and any other value to float64
func convertToFloat(value interface{}) (float64, error) {
switch typed := value.(type) {
case string:
var err error
floatValue, err := strconv.ParseFloat(typed, 64)
if err != nil {
return 0, err
}
return floatValue, nil
case float64:
return typed, nil
case int64:
return float64(typed), nil
case int:
return float64(typed), nil
default:
return 0, fmt.Errorf("Could not convert %T to float64", value)
}
}
2019-06-25 22:53:18 -07:00
type resourceInfo struct {
2019-07-23 23:34:03 -04:00
Resource unstructured.Unstructured
Gvk *metav1.GroupVersionKind
2019-06-25 22:53:18 -07:00
}
func ConvertToUnstructured(data []byte) (*unstructured.Unstructured, error) {
resource := &unstructured.Unstructured{}
err := resource.UnmarshalJSON(data)
if err != nil {
glog.V(4).Infof("failed to unmarshall resource: %v", err)
return nil, err
}
return resource, nil
}
2019-08-23 18:34:23 -07:00
type RuleType int
const (
Mutation RuleType = iota
Validation
Generation
All
)
func (ri RuleType) String() string {
return [...]string{
"Mutation",
"Validation",
"Generation",
"All",
}[ri]
}