1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

refactor: move common utils (#3553)

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>

Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-04-05 15:02:43 +02:00 committed by GitHub
parent 3bc0e062f9
commit 29d7010e25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 121 additions and 131 deletions

View file

@ -9,25 +9,22 @@ import (
"fmt" "fmt"
"strings" "strings"
v1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/sigstore/cosign/cmd/cosign/cli/rekor"
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio"
"github.com/sigstore/cosign/pkg/oci/remote"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/name"
gcrremote "github.com/google/go-containerregistry/pkg/v1/remote" gcrremote "github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/in-toto/in-toto-golang/in_toto" "github.com/in-toto/in-toto-golang/in_toto"
wildcard "github.com/kyverno/go-wildcard" wildcard "github.com/kyverno/go-wildcard"
"github.com/kyverno/kyverno/pkg/engine/common" v1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/registryclient"
"github.com/kyverno/kyverno/pkg/utils"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio"
"github.com/sigstore/cosign/cmd/cosign/cli/options" "github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/cmd/cosign/cli/rekor"
"github.com/sigstore/cosign/pkg/cosign" "github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/attestation" "github.com/sigstore/cosign/pkg/cosign/attestation"
"github.com/sigstore/cosign/pkg/oci" "github.com/sigstore/cosign/pkg/oci"
"github.com/sigstore/cosign/pkg/oci/remote"
sigs "github.com/sigstore/cosign/pkg/signature" sigs "github.com/sigstore/cosign/pkg/signature"
"github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature"
@ -269,7 +266,7 @@ func decodeStatement(payloadBase64 string) (map[string]interface{}, error) {
// - in_toto.PredicateLinkV1 // - in_toto.PredicateLinkV1
// - in_toto.PredicateSPDX // - in_toto.PredicateSPDX
// any other custom predicate // any other custom predicate
return common.ToMap(statement) return utils.ToMap(statement)
} }
return decodeCosignCustomProvenanceV01(statement) return decodeCosignCustomProvenanceV01(statement)
@ -297,7 +294,7 @@ func decodeCosignCustomProvenanceV01(statement in_toto.Statement) (map[string]in
statement.Predicate = predicate statement.Predicate = predicate
} }
return common.ToMap(statement) return utils.ToMap(statement)
} }
func stringToJSONMap(i interface{}) (map[string]interface{}, error) { func stringToJSONMap(i interface{}) (map[string]interface{}, error) {

View file

@ -1,7 +1,6 @@
package common package common
import ( import (
"encoding/json"
"fmt" "fmt"
"reflect" "reflect"
@ -10,53 +9,6 @@ import (
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
) )
// 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
}
// CopySlice creates a full copy of the target slice
func CopySlice(s []interface{}) []interface{} {
sliceCopy := make([]interface{}, len(s))
copy(sliceCopy, s)
return sliceCopy
}
// 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
}
func GetRawKeyIfWrappedWithAttributes(str string) string { func GetRawKeyIfWrappedWithAttributes(str string) string {
if len(str) < 2 { if len(str) < 2 {
return str return str

View file

@ -1,37 +0,0 @@
package common
import (
"testing"
"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_OriginalSliceMustNotBeChanged(t *testing.T) {
// no variables
originalSlice := []interface{}{
3711,
2138,
1908,
912,
}
sliceCopy := CopySlice(originalSlice)
sliceCopy[0] = 1
assert.Equal(t, originalSlice[0], 3711)
}

View file

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/kyverno/kyverno/pkg/engine/common" "github.com/kyverno/kyverno/pkg/utils"
) )
// ActionData represents data available for action on current element // ActionData represents data available for action on current element
@ -69,13 +69,13 @@ func (t *Traversal) traverseJSON(element interface{}, path string) (interface{},
// traverse further // traverse further
switch typed := element.(type) { switch typed := element.(type) {
case map[string]interface{}: case map[string]interface{}:
return t.traverseObject(common.CopyMap(typed), path) return t.traverseObject(utils.CopyMap(typed), path)
case []interface{}: case []interface{}:
return t.traverseList(common.CopySlice(typed), path) return t.traverseList(utils.CopySlice(typed), path)
case []map[string]interface{}: case []map[string]interface{}:
return t.traverseList(common.CopySliceOfMaps(typed), path) return t.traverseList(utils.CopySliceOfMaps(typed), path)
case Key: case Key:
return typed.Key, nil return typed.Key, nil

View file

@ -8,11 +8,11 @@ import (
"github.com/go-logr/logr" "github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1" kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/common"
"github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/mutate/patch" "github.com/kyverno/kyverno/pkg/engine/mutate/patch"
"github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/utils"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
) )
@ -92,7 +92,7 @@ func ForEach(name string, foreach *kyverno.ForEachMutation, ctx *context.Context
} }
func substituteAllInForEach(fe *kyverno.ForEachMutation, ctx *context.Context, logger logr.Logger) (*kyverno.ForEachMutation, error) { func substituteAllInForEach(fe *kyverno.ForEachMutation, ctx *context.Context, logger logr.Logger) (*kyverno.ForEachMutation, error) {
jsonObj, err := common.ToMap(fe) jsonObj, err := utils.ToMap(fe)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -16,9 +16,10 @@ import (
"github.com/go-logr/logr" "github.com/go-logr/logr"
gojmespath "github.com/jmespath/go-jmespath" gojmespath "github.com/jmespath/go-jmespath"
"github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils" engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/engine/validate" "github.com/kyverno/kyverno/pkg/engine/validate"
"github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/utils"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log"
) )
@ -179,7 +180,7 @@ func newValidator(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) *vali
func newForeachValidator(foreach *kyverno.ForEachValidation, rule *kyverno.Rule, ctx *PolicyContext, log logr.Logger) *validator { func newForeachValidator(foreach *kyverno.ForEachValidation, rule *kyverno.Rule, ctx *PolicyContext, log logr.Logger) *validator {
ruleCopy := rule.DeepCopy() ruleCopy := rule.DeepCopy()
anyAllConditions, err := common.ToMap(foreach.AnyAllConditions) anyAllConditions, err := utils.ToMap(foreach.AnyAllConditions)
if err != nil { if err != nil {
log.Error(err, "failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions") log.Error(err, "failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions")
} }
@ -198,16 +199,16 @@ func newForeachValidator(foreach *kyverno.ForEachValidation, rule *kyverno.Rule,
func (v *validator) validate() *response.RuleResponse { func (v *validator) validate() *response.RuleResponse {
if err := v.loadContext(); err != nil { if err := v.loadContext(); err != nil {
return ruleError(v.rule, utils.Validation, "failed to load context", err) return ruleError(v.rule, engineutils.Validation, "failed to load context", err)
} }
preconditionsPassed, err := checkPreconditions(v.log, v.ctx, v.anyAllConditions) preconditionsPassed, err := checkPreconditions(v.log, v.ctx, v.anyAllConditions)
if err != nil { if err != nil {
return ruleError(v.rule, utils.Validation, "failed to evaluate preconditions", err) return ruleError(v.rule, engineutils.Validation, "failed to evaluate preconditions", err)
} }
if !preconditionsPassed && v.ctx.Policy.GetSpec().ValidationFailureAction != kyverno.Audit { if !preconditionsPassed && v.ctx.Policy.GetSpec().ValidationFailureAction != kyverno.Audit {
return ruleResponse(v.rule, utils.Validation, "preconditions not met", response.RuleStatusSkip) return ruleResponse(v.rule, engineutils.Validation, "preconditions not met", response.RuleStatusSkip)
} }
if v.deny != nil { if v.deny != nil {
@ -216,14 +217,14 @@ func (v *validator) validate() *response.RuleResponse {
if v.pattern != nil || v.anyPattern != nil { if v.pattern != nil || v.anyPattern != nil {
if err = v.substitutePatterns(); err != nil { if err = v.substitutePatterns(); err != nil {
return ruleError(v.rule, utils.Validation, "variable substitution failed", err) return ruleError(v.rule, engineutils.Validation, "variable substitution failed", err)
} }
ruleResponse := v.validateResourceWithRule() ruleResponse := v.validateResourceWithRule()
if isUpdateRequest(v.ctx) { if isUpdateRequest(v.ctx) {
priorResp, err := validateOldObject(v.log, v.ctx, v.rule) priorResp, err := validateOldObject(v.log, v.ctx, v.rule)
if err != nil { if err != nil {
return ruleError(v.rule, utils.Validation, "failed to validate old object", err) return ruleError(v.rule, engineutils.Validation, "failed to validate old object", err)
} }
if isSameRuleResponse(ruleResponse, priorResp) { if isSameRuleResponse(ruleResponse, priorResp) {
@ -241,14 +242,14 @@ func (v *validator) validate() *response.RuleResponse {
func (v *validator) validateForEach() *response.RuleResponse { func (v *validator) validateForEach() *response.RuleResponse {
if err := v.loadContext(); err != nil { if err := v.loadContext(); err != nil {
return ruleError(v.rule, utils.Validation, "failed to load context", err) return ruleError(v.rule, engineutils.Validation, "failed to load context", err)
} }
preconditionsPassed, err := checkPreconditions(v.log, v.ctx, v.anyAllConditions) preconditionsPassed, err := checkPreconditions(v.log, v.ctx, v.anyAllConditions)
if err != nil { if err != nil {
return ruleError(v.rule, utils.Validation, "failed to evaluate preconditions", err) return ruleError(v.rule, engineutils.Validation, "failed to evaluate preconditions", err)
} else if !preconditionsPassed && v.ctx.Policy.GetSpec().ValidationFailureAction != kyverno.Audit { } else if !preconditionsPassed && v.ctx.Policy.GetSpec().ValidationFailureAction != kyverno.Audit {
return ruleResponse(v.rule, utils.Validation, "preconditions not met", response.RuleStatusSkip) return ruleResponse(v.rule, engineutils.Validation, "preconditions not met", response.RuleStatusSkip)
} }
foreachList := v.rule.Validation.ForEachValidation foreachList := v.rule.Validation.ForEachValidation
@ -278,10 +279,10 @@ func (v *validator) validateForEach() *response.RuleResponse {
} }
if applyCount == 0 { if applyCount == 0 {
return ruleResponse(v.rule, utils.Validation, "rule skipped", response.RuleStatusSkip) return ruleResponse(v.rule, engineutils.Validation, "rule skipped", response.RuleStatusSkip)
} }
return ruleResponse(v.rule, utils.Validation, "rule passed", response.RuleStatusPass) return ruleResponse(v.rule, engineutils.Validation, "rule passed", response.RuleStatusPass)
} }
func (v *validator) validateElements(foreach *kyverno.ForEachValidation, elements []interface{}, elementScope bool) (*response.RuleResponse, int) { func (v *validator) validateElements(foreach *kyverno.ForEachValidation, elements []interface{}, elementScope bool) (*response.RuleResponse, int) {
@ -295,7 +296,7 @@ func (v *validator) validateElements(foreach *kyverno.ForEachValidation, element
ctx := v.ctx.Copy() ctx := v.ctx.Copy()
if err := addElementToContext(ctx, e, i, elementScope); err != nil { if err := addElementToContext(ctx, e, i, elementScope); err != nil {
v.log.Error(err, "failed to add element to context") v.log.Error(err, "failed to add element to context")
return ruleError(v.rule, utils.Validation, "failed to process foreach", err), applyCount return ruleError(v.rule, engineutils.Validation, "failed to process foreach", err), applyCount
} }
foreachValidator := newForeachValidator(foreach, v.rule, ctx, v.log) foreachValidator := newForeachValidator(foreach, v.rule, ctx, v.log)
@ -308,17 +309,17 @@ func (v *validator) validateElements(foreach *kyverno.ForEachValidation, element
continue continue
} else if r.Status != response.RuleStatusPass { } else if r.Status != response.RuleStatusPass {
msg := fmt.Sprintf("validation failure: %v", r.Message) msg := fmt.Sprintf("validation failure: %v", r.Message)
return ruleResponse(v.rule, utils.Validation, msg, r.Status), applyCount return ruleResponse(v.rule, engineutils.Validation, msg, r.Status), applyCount
} }
applyCount++ applyCount++
} }
return ruleResponse(v.rule, utils.Validation, "", response.RuleStatusPass), applyCount return ruleResponse(v.rule, engineutils.Validation, "", response.RuleStatusPass), applyCount
} }
func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, elementScope bool) error { func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, elementScope bool) error {
data, err := common.ToMap(e) data, err := utils.ToMap(e)
if err != nil { if err != nil {
return err return err
} }
@ -359,24 +360,24 @@ func (v *validator) validateDeny() *response.RuleResponse {
anyAllCond := v.deny.GetAnyAllConditions() anyAllCond := v.deny.GetAnyAllConditions()
anyAllCond, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, anyAllCond) anyAllCond, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, anyAllCond)
if err != nil { if err != nil {
return ruleError(v.rule, utils.Validation, "failed to substitute variables in deny conditions", err) return ruleError(v.rule, engineutils.Validation, "failed to substitute variables in deny conditions", err)
} }
if err = v.substituteDeny(); err != nil { if err = v.substituteDeny(); err != nil {
return ruleError(v.rule, utils.Validation, "failed to substitute variables in rule", err) return ruleError(v.rule, engineutils.Validation, "failed to substitute variables in rule", err)
} }
denyConditions, err := common.TransformConditions(anyAllCond) denyConditions, err := common.TransformConditions(anyAllCond)
if err != nil { if err != nil {
return ruleError(v.rule, utils.Validation, "invalid deny conditions", err) return ruleError(v.rule, engineutils.Validation, "invalid deny conditions", err)
} }
deny := variables.EvaluateConditions(v.log, v.ctx.JSONContext, denyConditions) deny := variables.EvaluateConditions(v.log, v.ctx.JSONContext, denyConditions)
if deny { if deny {
return ruleResponse(v.rule, utils.Validation, v.getDenyMessage(deny), response.RuleStatusFail) return ruleResponse(v.rule, engineutils.Validation, v.getDenyMessage(deny), response.RuleStatusFail)
} }
return ruleResponse(v.rule, utils.Validation, v.getDenyMessage(deny), response.RuleStatusPass) return ruleResponse(v.rule, engineutils.Validation, v.getDenyMessage(deny), response.RuleStatusPass)
} }
func (v *validator) getDenyMessage(deny bool) string { func (v *validator) getDenyMessage(deny bool) string {
@ -480,22 +481,22 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon
v.log.V(3).Info("validation error", "path", pe.Path, "error", err.Error()) v.log.V(3).Info("validation error", "path", pe.Path, "error", err.Error())
if pe.Skip { if pe.Skip {
return ruleResponse(v.rule, utils.Validation, pe.Error(), response.RuleStatusSkip) return ruleResponse(v.rule, engineutils.Validation, pe.Error(), response.RuleStatusSkip)
} }
if pe.Path == "" { if pe.Path == "" {
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, ""), response.RuleStatusError) return ruleResponse(v.rule, engineutils.Validation, v.buildErrorMessage(err, ""), response.RuleStatusError)
} }
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusFail) return ruleResponse(v.rule, engineutils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusFail)
} }
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusError) return ruleResponse(v.rule, engineutils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusError)
} }
v.log.V(4).Info("successfully processed rule") v.log.V(4).Info("successfully processed rule")
msg := fmt.Sprintf("validation rule '%s' passed.", v.rule.Name) msg := fmt.Sprintf("validation rule '%s' passed.", v.rule.Name)
return ruleResponse(v.rule, utils.Validation, msg, response.RuleStatusPass) return ruleResponse(v.rule, engineutils.Validation, msg, response.RuleStatusPass)
} }
if v.anyPattern != nil { if v.anyPattern != nil {
@ -505,14 +506,14 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon
anyPatterns, err := deserializeAnyPattern(v.anyPattern) anyPatterns, err := deserializeAnyPattern(v.anyPattern)
if err != nil { if err != nil {
msg := fmt.Sprintf("failed to deserialize anyPattern, expected type array: %v", err) msg := fmt.Sprintf("failed to deserialize anyPattern, expected type array: %v", err)
return ruleResponse(v.rule, utils.Validation, msg, response.RuleStatusError) return ruleResponse(v.rule, engineutils.Validation, msg, response.RuleStatusError)
} }
for idx, pattern := range anyPatterns { for idx, pattern := range anyPatterns {
err := validate.MatchPattern(v.log, resource.Object, pattern) err := validate.MatchPattern(v.log, resource.Object, pattern)
if err == nil { if err == nil {
msg := fmt.Sprintf("validation rule '%s' anyPattern[%d] passed.", v.rule.Name, idx) msg := fmt.Sprintf("validation rule '%s' anyPattern[%d] passed.", v.rule.Name, idx)
return ruleResponse(v.rule, utils.Validation, msg, response.RuleStatusPass) return ruleResponse(v.rule, engineutils.Validation, msg, response.RuleStatusPass)
} }
if pe, ok := err.(*validate.PatternError); ok { if pe, ok := err.(*validate.PatternError); ok {
@ -536,11 +537,11 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon
v.log.V(4).Info(fmt.Sprintf("Validation rule '%s' failed. %s", v.rule.Name, errorStr)) v.log.V(4).Info(fmt.Sprintf("Validation rule '%s' failed. %s", v.rule.Name, errorStr))
msg := buildAnyPatternErrorMessage(v.rule, errorStr) msg := buildAnyPatternErrorMessage(v.rule, errorStr)
return ruleResponse(v.rule, utils.Validation, msg, response.RuleStatusFail) return ruleResponse(v.rule, engineutils.Validation, msg, response.RuleStatusFail)
} }
} }
return ruleResponse(v.rule, utils.Validation, v.rule.Validation.Message, response.RuleStatusPass) return ruleResponse(v.rule, engineutils.Validation, v.rule.Validation.Message, response.RuleStatusPass)
} }
func deserializeAnyPattern(anyPattern apiextensions.JSON) ([]interface{}, error) { func deserializeAnyPattern(anyPattern apiextensions.JSON) ([]interface{}, error) {

View file

@ -25,6 +25,53 @@ import (
var regexVersion = regexp.MustCompile(`v(\d+).(\d+).(\d+)\.*`) 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
}
// CopySlice creates a full copy of the target slice
func CopySlice(s []interface{}) []interface{} {
sliceCopy := make([]interface{}, len(s))
copy(sliceCopy, s)
return sliceCopy
}
// 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 // Contains checks if a string is contained in a list of string
func contains(list []string, element string, fn func(string, string) bool) bool { func contains(list []string, element string, fn func(string, string) bool) bool {
for _, e := range list { for _, e := range list {

View file

@ -6,6 +6,36 @@ import (
"gotest.tools/assert" "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_OriginalSliceMustNotBeChanged(t *testing.T) {
// no variables
originalSlice := []interface{}{
3711,
2138,
1908,
912,
}
sliceCopy := CopySlice(originalSlice)
sliceCopy[0] = 1
assert.Equal(t, originalSlice[0], 3711)
}
func Test_allEmpty(t *testing.T) { func Test_allEmpty(t *testing.T) {
var list []string var list []string
var element string var element string