1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 10:28:36 +00:00

refactor: move util funcs in sub packages (#5754)

* refactor: move util func in sub packages

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>

* Update pkg/utils/kube/crd.go

Signed-off-by: shuting <shutting06@gmail.com>

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
Signed-off-by: shuting <shutting06@gmail.com>
Co-authored-by: shuting <shutting06@gmail.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-12-22 07:39:54 +01:00 committed by GitHub
parent 3c997d88a8
commit 94abfaf13e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 490 additions and 491 deletions

View file

@ -18,7 +18,7 @@ import (
"github.com/kyverno/kyverno/pkg/leaderelection"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/tls"
"github.com/kyverno/kyverno/pkg/utils"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"go.uber.org/multierr"
admissionv1 "k8s.io/api/admission/v1"
coordinationv1 "k8s.io/api/coordination/v1"
@ -53,7 +53,7 @@ func main() {
kyvernoClient := internal.CreateKyvernoClient(logger)
client := internal.CreateDClient(logger, ctx, dynamicClient, kubeClient, 15*time.Minute)
// Exit for unsupported version of kubernetes cluster
if !utils.HigherThanKubernetesVersion(kubeClient.Discovery(), logging.GlobalLogger(), 1, 16, 0) {
if !kubeutils.HigherThanKubernetesVersion(kubeClient.Discovery(), logging.GlobalLogger(), 1, 16, 0) {
os.Exit(1)
}
requests := []request{

View file

@ -45,7 +45,7 @@ import (
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/tls"
"github.com/kyverno/kyverno/pkg/toggle"
"github.com/kyverno/kyverno/pkg/utils"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
runtimeutils "github.com/kyverno/kyverno/pkg/utils/runtime"
"github.com/kyverno/kyverno/pkg/webhooks"
webhooksexception "github.com/kyverno/kyverno/pkg/webhooks/exception"
@ -99,7 +99,7 @@ func showWarnings(logger logr.Logger) {
}
func sanityChecks(dynamicClient dclient.Interface) error {
if !utils.CRDsInstalled(dynamicClient.Discovery()) {
if !kubeutils.CRDsInstalled(dynamicClient.Discovery()) {
return fmt.Errorf("CRDs not installed")
}
return nil

View file

@ -28,7 +28,7 @@ import (
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/registryclient"
kyvernoutils "github.com/kyverno/kyverno/pkg/utils"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"golang.org/x/exp/slices"
@ -595,7 +595,7 @@ func newGenResource(genAPIVersion, genKind, genNamespace, genName string) kyvern
}
func manageData(log logr.Logger, apiVersion, kind, namespace, name string, data interface{}, synchronize bool, ur kyvernov1beta1.UpdateRequest, client dclient.Interface) (map[string]interface{}, ResourceMode, error) {
resource, err := kyvernoutils.ToMap(data)
resource, err := datautils.ToMap(data)
if err != nil {
return nil, Skip, err
}

View file

@ -14,7 +14,7 @@ import (
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/tracing"
"github.com/kyverno/kyverno/pkg/utils"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
wildcard "github.com/kyverno/kyverno/pkg/utils/wildcard"
"github.com/pkg/errors"
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio"
@ -404,7 +404,7 @@ func decodePayload(payloadBase64 string) (map[string]interface{}, error) {
// - in_toto.PredicateLinkV1
// - in_toto.PredicateSPDX
// any other custom predicate
return utils.ToMap(statement)
return datautils.ToMap(statement)
}
return decodeCosignCustomProvenanceV01(statement)
@ -432,7 +432,7 @@ func decodeCosignCustomProvenanceV01(statement in_toto.Statement) (map[string]in
statement.Predicate = predicate
}
return utils.ToMap(statement)
return datautils.ToMap(statement)
}
func stringToJSONMap(i interface{}) (map[string]interface{}, error) {

View file

@ -5,7 +5,7 @@ import (
"strconv"
"strings"
"github.com/kyverno/kyverno/pkg/utils"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
"golang.org/x/exp/slices"
)
@ -71,13 +71,13 @@ func (t *Traversal) traverseJSON(element interface{}, path string) (interface{},
// traverse further
switch typed := element.(type) {
case map[string]interface{}:
return t.traverseObject(utils.CopyMap(typed), path)
return t.traverseObject(datautils.CopyMap(typed), path)
case []interface{}:
return t.traverseList(slices.Clone(typed), path)
case []map[string]interface{}:
return t.traverseList(utils.CopySliceOfMaps(typed), path)
return t.traverseList(datautils.CopySliceOfMaps(typed), path)
case Key:
return typed.Key, nil

View file

@ -10,7 +10,7 @@ import (
"github.com/kyverno/kyverno/pkg/engine/mutate/patch"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/utils"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -91,7 +91,7 @@ func ForEach(name string, foreach kyvernov1.ForEachMutation, ctx context.Interfa
}
func substituteAllInForEach(fe kyvernov1.ForEachMutation, ctx context.Interface, logger logr.Logger) (*kyvernov1.ForEachMutation, error) {
jsonObj, err := utils.ToMap(fe)
jsonObj, err := datautils.ToMap(fe)
if err != nil {
return nil, err
}

View file

@ -16,7 +16,7 @@ import (
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/engine/wildcards"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/utils"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"github.com/kyverno/kyverno/pkg/utils/wildcard"
"github.com/pkg/errors"
@ -210,14 +210,14 @@ func doesResourceMatchConditionBlock(subresourceGVKToAPIResource map[string]*met
keys := append(admissionInfo.AdmissionUserInfo.Groups, admissionInfo.AdmissionUserInfo.Username)
var userInfoErrors []error
if len(userInfo.Roles) > 0 && !utils.SliceContains(keys, dynamicConfig...) {
if !utils.SliceContains(userInfo.Roles, admissionInfo.Roles...) {
if len(userInfo.Roles) > 0 && !datautils.SliceContains(keys, dynamicConfig...) {
if !datautils.SliceContains(userInfo.Roles, admissionInfo.Roles...) {
userInfoErrors = append(userInfoErrors, fmt.Errorf("user info does not match roles for the given conditionBlock"))
}
}
if len(userInfo.ClusterRoles) > 0 && !utils.SliceContains(keys, dynamicConfig...) {
if !utils.SliceContains(userInfo.ClusterRoles, admissionInfo.ClusterRoles...) {
if len(userInfo.ClusterRoles) > 0 && !datautils.SliceContains(keys, dynamicConfig...) {
if !datautils.SliceContains(userInfo.ClusterRoles, admissionInfo.ClusterRoles...) {
userInfoErrors = append(userInfoErrors, fmt.Errorf("user info does not match clustersRoles for the given conditionBlock"))
}
}

View file

@ -22,8 +22,8 @@ import (
"github.com/kyverno/kyverno/pkg/pss"
"github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/tracing"
"github.com/kyverno/kyverno/pkg/utils"
"github.com/kyverno/kyverno/pkg/utils/api"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
matched "github.com/kyverno/kyverno/pkg/utils/match"
"github.com/pkg/errors"
"go.opentelemetry.io/otel/trace"
@ -216,7 +216,7 @@ func newValidator(log logr.Logger, rclient registryclient.Client, ctx *PolicyCon
func newForEachValidator(foreach kyvernov1.ForEachValidation, rclient registryclient.Client, nesting int, rule *kyvernov1.Rule, ctx *PolicyContext, log logr.Logger) (*validator, error) {
ruleCopy := rule.DeepCopy()
anyAllConditions, err := utils.ToMap(foreach.AnyAllConditions)
anyAllConditions, err := datautils.ToMap(foreach.AnyAllConditions)
if err != nil {
return nil, errors.Wrap(err, "failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions")
}

View file

@ -7,7 +7,6 @@ import (
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/utils"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"github.com/kyverno/kyverno/pkg/utils/wildcard"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -94,7 +93,7 @@ func excludeResources(included map[string]unstructured.Unstructured, exclude kyv
if len(exclude.Namespaces) == 0 {
return NotEvaluate
}
if utils.ContainsNamepace(exclude.Namespaces, namespace) {
if wildcard.CheckPatterns(exclude.Namespaces, namespace) {
return Skip
}
return Process

View file

@ -24,6 +24,7 @@ import (
"github.com/kyverno/kyverno/pkg/openapi"
"github.com/kyverno/kyverno/pkg/utils"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"github.com/kyverno/kyverno/pkg/utils/wildcard"
"github.com/pkg/errors"
"golang.org/x/exp/slices"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
@ -1268,20 +1269,20 @@ func validateKinds(kinds []string, mock, backgroundScanningEnabled, isValidation
}
func validateWildcardsWithNamespaces(enforce, audit, enforceW, auditW []string) error {
pat, ns, notOk := utils.CheckWildcardNamespaces(auditW, enforce)
pat, ns, notOk := wildcard.MatchPatterns(auditW, enforce...)
if notOk {
return fmt.Errorf("wildcard pattern '%s' matches with namespace '%s'", pat, ns)
}
pat, ns, notOk = utils.CheckWildcardNamespaces(enforceW, audit)
pat, ns, notOk = wildcard.MatchPatterns(enforceW, audit...)
if notOk {
return fmt.Errorf("wildcard pattern '%s' matches with namespace '%s'", pat, ns)
}
pat1, pat2, notOk := utils.CheckWildcardNamespaces(auditW, enforceW)
pat1, pat2, notOk := wildcard.MatchPatterns(auditW, enforceW...)
if notOk {
return fmt.Errorf("wildcard pattern '%s' conflicts with the pattern '%s'", pat1, pat2)
}
pat1, pat2, notOk = utils.CheckWildcardNamespaces(enforceW, auditW)
pat1, pat2, notOk = wildcard.MatchPatterns(enforceW, auditW...)
if notOk {
return fmt.Errorf("wildcard pattern '%s' conflicts with the pattern '%s'", pat1, pat2)
}
@ -1298,7 +1299,7 @@ func validateNamespaces(s *kyvernov1.Spec, path *field.Path) error {
}
for i, vfa := range s.ValidationFailureActionOverrides {
patternList, nsList := utils.SeperateWildcards(vfa.Namespaces)
patternList, nsList := wildcard.SeperateWildcards(vfa.Namespaces)
if vfa.Action.Audit() {
if action["enforce"].HasAny(nsList...) {

View file

@ -2,7 +2,7 @@ package policycache
import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernoutils "github.com/kyverno/kyverno/pkg/utils"
"github.com/kyverno/kyverno/pkg/utils/wildcard"
)
// Cache get method use for to get policy names and mostly use to test cache testcases
@ -81,7 +81,7 @@ func checkValidationFailureActionOverrides(enforce bool, ns string, policy kyver
return false
}
for _, action := range validationFailureActionOverrides {
if action.Action.Enforce() != enforce && kyvernoutils.ContainsNamepace(action.Namespaces, ns) {
if action.Action.Enforce() != enforce && wildcard.CheckPatterns(action.Namespaces, ns) {
return false
}
}

View file

@ -5,7 +5,7 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
pssutils "github.com/kyverno/kyverno/pkg/pss/utils"
"github.com/kyverno/kyverno/pkg/utils"
"github.com/kyverno/kyverno/pkg/utils/wildcard"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/pod-security-admission/api"
@ -126,18 +126,18 @@ func GetPodWithMatchingContainers(exclude kyvernov1.PodSecurityStandard, pod *co
},
}
for _, container := range pod.Spec.Containers {
if utils.ContainsWildcardPatterns(matchingImages, container.Image) {
if wildcard.CheckPatterns(matchingImages, container.Image) {
matching.Spec.Containers = append(matching.Spec.Containers, container)
}
}
for _, container := range pod.Spec.InitContainers {
if utils.ContainsWildcardPatterns(matchingImages, container.Image) {
if wildcard.CheckPatterns(matchingImages, container.Image) {
matching.Spec.InitContainers = append(matching.Spec.InitContainers, container)
}
}
for _, container := range pod.Spec.EphemeralContainers {
if utils.ContainsWildcardPatterns(matchingImages, container.Image) {
if wildcard.CheckPatterns(matchingImages, container.Image) {
matching.Spec.EphemeralContainers = append(matching.Spec.EphemeralContainers, container)
}
}

View file

@ -6,7 +6,7 @@ import (
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/utils"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
admissionv1 "k8s.io/api/admission/v1"
authenticationv1 "k8s.io/api/authentication/v1"
rbacv1 "k8s.io/api/rbac/v1"
@ -24,7 +24,7 @@ const (
// GetRoleRef gets the list of roles and cluster roles for the incoming api-request
func GetRoleRef(rbLister rbacv1listers.RoleBindingLister, crbLister rbacv1listers.ClusterRoleBindingLister, request *admissionv1.AdmissionRequest, dynamicConfig config.Configuration) ([]string, []string, error) {
keys := append(request.UserInfo.Groups, request.UserInfo.Username)
if utils.SliceContains(keys, dynamicConfig.GetExcludeGroupRole()...) {
if datautils.SliceContains(keys, dynamicConfig.GetExcludeGroupRole()...) {
return nil, nil, nil
}
// rolebindings

46
pkg/utils/data/data.go Normal file
View file

@ -0,0 +1,46 @@
package data
import (
"encoding/json"
"k8s.io/apimachinery/pkg/util/sets"
)
// CopyMap creates a full copy of the target map
func CopyMap(m map[string]interface{}) map[string]interface{} {
mapCopy := make(map[string]interface{})
for k, v := range m {
mapCopy[k] = v
}
return mapCopy
}
// CopySliceOfMaps creates a full copy of the target slice
func CopySliceOfMaps(s []map[string]interface{}) []interface{} {
sliceCopy := make([]interface{}, len(s))
for i, v := range s {
sliceCopy[i] = CopyMap(v)
}
return sliceCopy
}
func ToMap(data interface{}) (map[string]interface{}, error) {
if m, ok := data.(map[string]interface{}); ok {
return m, nil
}
b, err := json.Marshal(data)
if err != nil {
return nil, err
}
mapData := make(map[string]interface{})
err = json.Unmarshal(b, &mapData)
if err != nil {
return nil, err
}
return mapData, nil
}
// SliceContains checks whether values are contained in slice
func SliceContains(slice []string, values ...string) bool {
return sets.New(slice...).HasAny(values...)
}

View file

@ -0,0 +1,95 @@
package data
import (
"testing"
"gotest.tools/assert"
)
func TestOriginalMapMustNotBeChanged(t *testing.T) {
// no variables
originalMap := map[string]interface{}{
"rsc": 3711,
"r": 2138,
"gri": 1908,
"adg": 912,
}
mapCopy := CopyMap(originalMap)
mapCopy["r"] = 1
assert.Equal(t, originalMap["r"], 2138)
}
func TestSliceContains(t *testing.T) {
type args struct {
slice []string
values []string
}
tests := []struct {
name string
args args
want bool
}{{
name: "empty slice",
args: args{
slice: []string{},
values: []string{"ccc", "ddd"},
},
want: false,
}, {
name: "nil slice",
args: args{
slice: nil,
values: []string{"ccc", "ddd"},
},
want: false,
}, {
name: "empty values",
args: args{
slice: []string{"aaa", "bbb"},
values: []string{},
},
want: false,
}, {
name: "nil values",
args: args{
slice: []string{"aaa", "bbb"},
values: nil,
},
want: false,
}, {
name: "none match",
args: args{
slice: []string{"aaa", "bbb"},
values: []string{"ccc", "ddd"},
},
want: false,
}, {
name: "one match",
args: args{
slice: []string{"aaa", "bbb"},
values: []string{"aaa"},
},
want: true,
}, {
name: "one match, one doesn't match",
args: args{
slice: []string{"aaa", "bbb"},
values: []string{"aaa", "ddd"},
},
want: true,
}, {
name: "all match",
args: args{
slice: []string{"aaa", "bbb"},
values: []string{"aaa", "bbb"},
},
want: true,
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := SliceContains(tt.args.slice, tt.args.values...); got != tt.want {
t.Errorf("SliceContains() = %v, want %v", got, tt.want)
}
})
}
}

35
pkg/utils/kube/crd.go Normal file
View file

@ -0,0 +1,35 @@
package kube
import (
"fmt"
"github.com/kyverno/kyverno/pkg/logging"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type disco interface {
GetGVRFromKind(string) (schema.GroupVersionResource, error)
}
// CRDsInstalled checks if the Kyverno CRDs are installed or not
func CRDsInstalled(discovery disco) bool {
kyvernoCRDs := []string{"ClusterPolicy", "Policy", "ClusterPolicyReport", "PolicyReport", "AdmissionReport", "BackgroundScanReport", "ClusterAdmissionReport", "ClusterBackgroundScanReport"}
for _, crd := range kyvernoCRDs {
if !isCRDInstalled(discovery, crd) {
return false
}
}
return true
}
func isCRDInstalled(discovery disco, kind string) bool {
gvr, err := discovery.GetGVRFromKind(kind)
if gvr.Empty() {
if err == nil {
err = fmt.Errorf("not found")
}
logging.Error(err, "failed to retrieve CRD", "kind", kind)
return false
}
return true
}

53
pkg/utils/kube/version.go Normal file
View file

@ -0,0 +1,53 @@
package kube
import (
"fmt"
"regexp"
"strconv"
"github.com/go-logr/logr"
"k8s.io/client-go/discovery"
)
var regexVersion = regexp.MustCompile(`v(\d+).(\d+).(\d+)\.*`)
// HigherThanKubernetesVersion compare Kubernetes client version to user given version
func HigherThanKubernetesVersion(client discovery.ServerVersionInterface, log logr.Logger, major, minor, patch int) bool {
logger := log.WithName("CompareKubernetesVersion")
serverVersion, err := client.ServerVersion()
if err != nil {
logger.Error(err, "Failed to get kubernetes server version")
return false
}
b, err := isVersionHigher(serverVersion.String(), major, minor, patch)
if err != nil {
logger.Error(err, "serverVersion", serverVersion.String())
return false
}
return b
}
func isVersionHigher(version string, major int, minor int, patch int) (bool, error) {
groups := regexVersion.FindStringSubmatch(version)
if len(groups) != 4 {
return false, fmt.Errorf("invalid version %s. Expected {major}.{minor}.{patch}", version)
}
currentMajor, err := strconv.Atoi(groups[1])
if err != nil {
return false, fmt.Errorf("failed to extract major version from %s", version)
}
currentMinor, err := strconv.Atoi(groups[2])
if err != nil {
return false, fmt.Errorf("failed to extract minor version from %s", version)
}
currentPatch, err := strconv.Atoi(groups[3])
if err != nil {
return false, fmt.Errorf("failed to extract minor version from %s", version)
}
if currentMajor < major ||
(currentMajor == major && currentMinor < minor) ||
(currentMajor == major && currentMinor == minor && currentPatch <= patch) {
return false, nil
}
return true, nil
}

View file

@ -0,0 +1,42 @@
package kube
import (
"testing"
"gotest.tools/assert"
)
func Test_higherVersion(t *testing.T) {
v, err := isVersionHigher("invalid.version", 1, 1, 1)
assert.Assert(t, v == false && err != nil)
v, err = isVersionHigher("invalid-version", 0, 0, 0)
assert.Assert(t, v == false && err != nil)
v, err = isVersionHigher("v1.1.1", 1, 1, 1)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v1.0.0", 1, 1, 1)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v1.5.9", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9+distro", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9+distro", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9-rc2", 1, 5, 9)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v1.5.9", 2, 1, 0)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v2.1.0", 1, 5, 9)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9-x-v1.5.9.x", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
}

View file

@ -3,15 +3,11 @@ package utils
import (
"encoding/json"
"fmt"
"regexp"
"strconv"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/logging"
wildcard "github.com/kyverno/kyverno/pkg/utils/wildcard"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
"github.com/pkg/errors"
admissionv1 "k8s.io/api/admission/v1"
corev1 "k8s.io/api/core/v1"
@ -19,97 +15,8 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/discovery"
)
var regexVersion = regexp.MustCompile(`v(\d+).(\d+).(\d+)\.*`)
// CopyMap creates a full copy of the target map
func CopyMap(m map[string]interface{}) map[string]interface{} {
mapCopy := make(map[string]interface{})
for k, v := range m {
mapCopy[k] = v
}
return mapCopy
}
// CopySliceOfMaps creates a full copy of the target slice
func CopySliceOfMaps(s []map[string]interface{}) []interface{} {
sliceCopy := make([]interface{}, len(s))
for i, v := range s {
sliceCopy[i] = CopyMap(v)
}
return sliceCopy
}
func ToMap(data interface{}) (map[string]interface{}, error) {
if m, ok := data.(map[string]interface{}); ok {
return m, nil
}
b, err := json.Marshal(data)
if err != nil {
return nil, err
}
mapData := make(map[string]interface{})
err = json.Unmarshal(b, &mapData)
if err != nil {
return nil, err
}
return mapData, nil
}
// Contains checks if a string is contained in a list of string
func contains(list []string, element string, fn func(string, string) bool) bool {
for _, e := range list {
if fn(e, element) {
return true
}
}
return false
}
// ContainsNamepace check if namespace satisfies any list of pattern(regex)
func ContainsNamepace(patterns []string, ns string) bool {
return contains(patterns, ns, comparePatterns)
}
func ContainsWildcardPatterns(patterns []string, key string) bool {
return contains(patterns, key, comparePatterns)
}
func comparePatterns(pattern, ns string) bool {
return wildcard.Match(pattern, ns)
}
// CRDsInstalled checks if the Kyverno CRDs are installed or not
func CRDsInstalled(discovery dclient.IDiscovery) bool {
kyvernoCRDs := []string{"ClusterPolicy", "ClusterPolicyReport", "PolicyReport", "AdmissionReport", "BackgroundScanReport", "ClusterAdmissionReport", "ClusterBackgroundScanReport"}
for _, crd := range kyvernoCRDs {
if !isCRDInstalled(discovery, crd) {
return false
}
}
return true
}
func isCRDInstalled(discoveryClient dclient.IDiscovery, kind string) bool {
gvr, err := discoveryClient.GetGVRFromKind(kind)
if gvr.Empty() {
if err == nil {
err = fmt.Errorf("not found")
}
logging.Error(err, "failed to retrieve CRD", "kind", kind)
return false
}
return true
}
// ExtractResources extracts the new and old resource as unstructured
func ExtractResources(newRaw []byte, request *admissionv1.AdmissionRequest) (unstructured.Unstructured, unstructured.Unstructured, error) {
var emptyResource unstructured.Unstructured
@ -241,7 +148,7 @@ func RedactSecret(resource *unstructured.Unstructured) (unstructured.Unstructure
}
}
if secret.Annotations != nil {
metadata, err := ToMap(resource.Object["metadata"])
metadata, err := datautils.ToMap(resource.Object["metadata"])
if err != nil {
return *resource, errors.Wrap(err, "unable to convert metadata to map")
}
@ -257,68 +164,6 @@ func RedactSecret(resource *unstructured.Unstructured) (unstructured.Unstructure
return *resource, nil
}
// HigherThanKubernetesVersion compare Kubernetes client version to user given version
func HigherThanKubernetesVersion(client discovery.ServerVersionInterface, log logr.Logger, major, minor, patch int) bool {
logger := log.WithName("CompareKubernetesVersion")
serverVersion, err := client.ServerVersion()
if err != nil {
logger.Error(err, "Failed to get kubernetes server version")
return false
}
b, err := isVersionHigher(serverVersion.String(), major, minor, patch)
if err != nil {
logger.Error(err, "serverVersion", serverVersion.String())
return false
}
return b
}
func isVersionHigher(version string, major int, minor int, patch int) (bool, error) {
groups := regexVersion.FindStringSubmatch(version)
if len(groups) != 4 {
return false, fmt.Errorf("invalid version %s. Expected {major}.{minor}.{patch}", version)
}
currentMajor, err := strconv.Atoi(groups[1])
if err != nil {
return false, fmt.Errorf("failed to extract major version from %s", version)
}
currentMinor, err := strconv.Atoi(groups[2])
if err != nil {
return false, fmt.Errorf("failed to extract minor version from %s", version)
}
currentPatch, err := strconv.Atoi(groups[3])
if err != nil {
return false, fmt.Errorf("failed to extract minor version from %s", version)
}
if currentMajor < major ||
(currentMajor == major && currentMinor < minor) ||
(currentMajor == major && currentMinor == minor && currentPatch <= patch) {
return false, nil
}
return true, nil
}
// SliceContains checks whether values are contained in slice
func SliceContains(slice []string, values ...string) bool {
sliceElementsMap := make(map[string]bool, len(slice))
for _, sliceElement := range slice {
sliceElementsMap[sliceElement] = true
}
for _, value := range values {
if sliceElementsMap[value] {
return true
}
}
return false
}
// ApiextensionsJsonToKyvernoConditions takes in user-provided conditions in abstract apiextensions.JSON form
// and converts it into []kyverno.Condition or kyverno.AnyAllConditions according to its content.
// it also helps in validating the condtions as it returns an error when the conditions are provided wrongfully by the user.
@ -394,38 +239,3 @@ func OverrideRuntimeErrorHandler() {
}
}
}
func SeperateWildcards(l []string) (lw []string, rl []string) {
for _, val := range l {
if wildcard.ContainsWildcard(val) {
lw = append(lw, val)
} else {
rl = append(rl, val)
}
}
return lw, rl
}
func CheckWildcardNamespaces(patterns []string, ns []string) (string, string, bool) {
for _, n := range ns {
pat, element, boolval := containsNamespaceWithStringReturn(patterns, n)
if boolval {
return pat, element, true
}
}
return "", "", false
}
func containsWithStringReturn(list []string, element string, fn func(string, string) bool) (string, string, bool) {
for _, e := range list {
if fn(e, element) {
return e, element, true
}
}
return "", "", false
}
// containsNamespaceWithStringReturn check if namespace satisfies any list of pattern(regex)
func containsNamespaceWithStringReturn(patterns []string, ns string) (string, string, bool) {
return containsWithStringReturn(patterns, ns, comparePatterns)
}

View file

@ -6,84 +6,6 @@ import (
"gotest.tools/assert"
)
func Test_OriginalMapMustNotBeChanged(t *testing.T) {
// no variables
originalMap := map[string]interface{}{
"rsc": 3711,
"r": 2138,
"gri": 1908,
"adg": 912,
}
mapCopy := CopyMap(originalMap)
mapCopy["r"] = 1
assert.Equal(t, originalMap["r"], 2138)
}
func Test_containsNs(t *testing.T) {
var patterns []string
var res bool
patterns = []string{"*"}
res = ContainsNamepace(patterns, "default")
assert.Assert(t, res == true)
patterns = []string{"*", "default"}
res = ContainsNamepace(patterns, "default")
assert.Assert(t, res == true)
patterns = []string{"default2", "default"}
res = ContainsNamepace(patterns, "default1")
assert.Assert(t, res == false)
patterns = []string{"d*"}
res = ContainsNamepace(patterns, "default")
assert.Assert(t, res == true)
patterns = []string{"d*"}
res = ContainsNamepace(patterns, "test")
assert.Assert(t, res == false)
patterns = []string{}
res = ContainsNamepace(patterns, "test")
assert.Assert(t, res == false)
}
func Test_higherVersion(t *testing.T) {
v, err := isVersionHigher("invalid.version", 1, 1, 1)
assert.Assert(t, v == false && err != nil)
v, err = isVersionHigher("invalid-version", 0, 0, 0)
assert.Assert(t, v == false && err != nil)
v, err = isVersionHigher("v1.1.1", 1, 1, 1)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v1.0.0", 1, 1, 1)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v1.5.9", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9+distro", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9+distro", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9-rc2", 1, 5, 9)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v1.5.9", 2, 1, 0)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v2.1.0", 1, 5, 9)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9-x-v1.5.9.x", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
}
func Test_ConvertResource(t *testing.T) {
testCases := []struct {
name string
@ -155,181 +77,3 @@ func Test_ConvertResource(t *testing.T) {
break
}
}
func Test_SeperateWildcards(t *testing.T) {
testcases := []struct {
description string
inputList []string
expList1 []string
expList2 []string
}{
{
description: "tc1",
inputList: []string{"test*", "default", "default1", "hello"},
expList1: []string{"test*"},
expList2: []string{"default", "default1", "hello"},
},
{
description: "tc2",
inputList: []string{"test*", "default*", "default1?", "hello?"},
expList1: []string{"test*", "default*", "default1?", "hello?"},
expList2: nil,
},
{
description: "tc3",
inputList: []string{"test", "default", "default1", "hello"},
expList1: nil,
expList2: []string{"test", "default", "default1", "hello"},
},
{
description: "tc4",
inputList: nil,
expList1: nil,
expList2: nil,
},
}
for _, tc := range testcases {
t.Run(tc.description, func(t *testing.T) {
list1, list2 := SeperateWildcards(tc.inputList)
assert.DeepEqual(t, list1, tc.expList1)
assert.DeepEqual(t, list2, tc.expList2)
})
}
}
func Test_CheckWildcardNamespaces(t *testing.T) {
testcases := []struct {
description string
inputPatterns []string
inputNs []string
expString1 string
expString2 string
expBool bool
}{
{
description: "tc1",
inputPatterns: []string{"default*", "test*"},
inputNs: []string{"default", "default1"},
expString1: "default*",
expString2: "default",
expBool: true,
},
{
description: "tc2",
inputPatterns: []string{"test*"},
inputNs: []string{"default1", "test"},
expString1: "test*",
expString2: "test",
expBool: true,
},
{
description: "tc3",
inputPatterns: []string{"*"},
inputNs: []string{"default1", "test"},
expString1: "*",
expString2: "default1",
expBool: true,
},
{
description: "tc4",
inputPatterns: []string{"a*"},
inputNs: []string{"default1", "test"},
expString1: "",
expString2: "",
expBool: false,
},
{
description: "tc5",
inputPatterns: nil,
inputNs: []string{"default1", "test"},
expString1: "",
expString2: "",
expBool: false,
},
{
description: "tc6",
inputPatterns: []string{"*"},
inputNs: nil,
expString1: "",
expString2: "",
expBool: false,
},
{
description: "tc7",
inputPatterns: nil,
inputNs: nil,
expString1: "",
expString2: "",
expBool: false,
},
}
for _, tc := range testcases {
t.Run(tc.description, func(t *testing.T) {
str1, str2, actualBool := CheckWildcardNamespaces(tc.inputPatterns, tc.inputNs)
assert.Equal(t, str1, tc.expString1)
assert.Equal(t, str2, tc.expString2)
assert.Equal(t, actualBool, tc.expBool)
})
}
}
func Test_containsNamespaceWithStringReturn(t *testing.T) {
testcases := []struct {
description string
inputPattern []string
inputNs string
expStr1 string
expStr2 string
expBool bool
}{
{
description: "tc1",
inputPattern: []string{"default*"},
inputNs: "default",
expStr1: "default*",
expStr2: "default",
expBool: true,
},
{
description: "tc2",
inputPattern: []string{"*"},
inputNs: "default",
expStr1: "*",
expStr2: "default",
expBool: true,
},
{
description: "tc3",
inputPattern: []string{"*"},
inputNs: "default",
expStr1: "*",
expStr2: "default",
expBool: true,
},
{
description: "tc4",
inputPattern: nil,
inputNs: "default",
expStr1: "",
expStr2: "",
expBool: false,
},
{
description: "tc5",
inputPattern: nil,
inputNs: "",
expStr1: "",
expStr2: "",
expBool: false,
},
}
for _, tc := range testcases {
t.Run(tc.description, func(t *testing.T) {
str1, str2, actualBool := containsNamespaceWithStringReturn(tc.inputPattern, tc.inputNs)
assert.Equal(t, str1, tc.expStr1)
assert.Equal(t, str2, tc.expStr2)
assert.Equal(t, actualBool, tc.expBool)
})
}
}

View file

@ -5,3 +5,32 @@ import "strings"
func ContainsWildcard(v string) bool {
return strings.Contains(v, "*") || strings.Contains(v, "?")
}
// MatchPatterns check if any text satisfies any pattern
func MatchPatterns(patterns []string, names ...string) (string, string, bool) {
for _, name := range names {
for _, pattern := range patterns {
if Match(pattern, name) {
return pattern, name, true
}
}
}
return "", "", false
}
// CheckPatterns check if any text satisfies any pattern
func CheckPatterns(patterns []string, names ...string) bool {
_, _, match := MatchPatterns(patterns, names...)
return match
}
func SeperateWildcards(l []string) (lw []string, rl []string) {
for _, val := range l {
if ContainsWildcard(val) {
lw = append(lw, val)
} else {
rl = append(rl, val)
}
}
return lw, rl
}

View file

@ -87,3 +87,148 @@ func TestContainsWildcard(t *testing.T) {
})
}
}
func TestCheckPatterns(t *testing.T) {
var patterns []string
var res bool
patterns = []string{"*"}
res = CheckPatterns(patterns, "default")
assert.Equal(t, true, res)
patterns = []string{"*", "default"}
res = CheckPatterns(patterns, "default")
assert.Equal(t, true, res)
patterns = []string{"default2", "default"}
res = CheckPatterns(patterns, "default1")
assert.Equal(t, false, res)
patterns = []string{"d*"}
res = CheckPatterns(patterns, "default")
assert.Equal(t, true, res)
patterns = []string{"d*"}
res = CheckPatterns(patterns, "test")
assert.Equal(t, false, res)
patterns = []string{}
res = CheckPatterns(patterns, "test")
assert.Equal(t, false, res)
}
func Test_MatchPatterns(t *testing.T) {
testcases := []struct {
description string
inputPatterns []string
inputNs []string
expString1 string
expString2 string
expBool bool
}{
{
description: "tc1",
inputPatterns: []string{"default*", "test*"},
inputNs: []string{"default", "default1"},
expString1: "default*",
expString2: "default",
expBool: true,
},
{
description: "tc2",
inputPatterns: []string{"test*"},
inputNs: []string{"default1", "test"},
expString1: "test*",
expString2: "test",
expBool: true,
},
{
description: "tc3",
inputPatterns: []string{"*"},
inputNs: []string{"default1", "test"},
expString1: "*",
expString2: "default1",
expBool: true,
},
{
description: "tc4",
inputPatterns: []string{"a*"},
inputNs: []string{"default1", "test"},
expString1: "",
expString2: "",
expBool: false,
},
{
description: "tc5",
inputPatterns: nil,
inputNs: []string{"default1", "test"},
expString1: "",
expString2: "",
expBool: false,
},
{
description: "tc6",
inputPatterns: []string{"*"},
inputNs: nil,
expString1: "",
expString2: "",
expBool: false,
},
{
description: "tc7",
inputPatterns: nil,
inputNs: nil,
expString1: "",
expString2: "",
expBool: false,
},
}
for _, tc := range testcases {
t.Run(tc.description, func(t *testing.T) {
str1, str2, actualBool := MatchPatterns(tc.inputPatterns, tc.inputNs...)
assert.Equal(t, str1, tc.expString1)
assert.Equal(t, str2, tc.expString2)
assert.Equal(t, actualBool, tc.expBool)
})
}
}
func Test_SeperateWildcards(t *testing.T) {
testcases := []struct {
description string
inputList []string
expList1 []string
expList2 []string
}{
{
description: "tc1",
inputList: []string{"test*", "default", "default1", "hello"},
expList1: []string{"test*"},
expList2: []string{"default", "default1", "hello"},
},
{
description: "tc2",
inputList: []string{"test*", "default*", "default1?", "hello?"},
expList1: []string{"test*", "default*", "default1?", "hello?"},
expList2: nil,
},
{
description: "tc3",
inputList: []string{"test", "default", "default1", "hello"},
expList1: nil,
expList2: []string{"test", "default", "default1", "hello"},
},
{
description: "tc4",
inputList: nil,
expList1: nil,
expList2: nil,
},
}
for _, tc := range testcases {
t.Run(tc.description, func(t *testing.T) {
list1, list2 := SeperateWildcards(tc.inputList)
assert.Equal(t, tc.expList1, list1)
assert.Equal(t, tc.expList2, list2)
})
}
}

View file

@ -4,7 +4,7 @@ import (
"encoding/json"
"testing"
"github.com/kyverno/kyverno/pkg/utils"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
"gotest.tools/assert"
admissionv1 "k8s.io/api/admission/v1"
)
@ -144,28 +144,28 @@ func Test_RedactPayload(t *testing.T) {
payload, err := newAdmissionRequestPayload(req)
assert.NilError(t, err)
if payload.Object.Object != nil {
data, err := utils.ToMap(payload.Object.Object["data"])
data, err := datautils.ToMap(payload.Object.Object["data"])
assert.NilError(t, err)
for _, v := range data {
assert.Assert(t, v == "**REDACTED**")
}
metadata, err := utils.ToMap(payload.Object.Object["metadata"])
metadata, err := datautils.ToMap(payload.Object.Object["metadata"])
assert.NilError(t, err)
annotations, err := utils.ToMap(metadata["annotations"])
annotations, err := datautils.ToMap(metadata["annotations"])
assert.NilError(t, err)
for _, v := range annotations {
assert.Assert(t, v == "**REDACTED**")
}
}
if payload.OldObject.Object != nil {
data, err := utils.ToMap(payload.OldObject.Object["data"])
data, err := datautils.ToMap(payload.OldObject.Object["data"])
assert.NilError(t, err)
for _, v := range data {
assert.Assert(t, v == "**REDACTED**")
}
metadata, err := utils.ToMap(payload.OldObject.Object["metadata"])
metadata, err := datautils.ToMap(payload.OldObject.Object["metadata"])
assert.NilError(t, err)
annotations, err := utils.ToMap(metadata["annotations"])
annotations, err := datautils.ToMap(metadata["annotations"])
assert.NilError(t, err)
for _, v := range annotations {
assert.Assert(t, v == "**REDACTED**")