mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-15 17:51:20 +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:
parent
3bc0e062f9
commit
29d7010e25
8 changed files with 121 additions and 131 deletions
|
@ -9,25 +9,22 @@ import (
|
|||
"fmt"
|
||||
"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/google/go-containerregistry/pkg/name"
|
||||
gcrremote "github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/in-toto/in-toto-golang/in_toto"
|
||||
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/utils"
|
||||
"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/rekor"
|
||||
"github.com/sigstore/cosign/pkg/cosign"
|
||||
"github.com/sigstore/cosign/pkg/cosign/attestation"
|
||||
"github.com/sigstore/cosign/pkg/oci"
|
||||
"github.com/sigstore/cosign/pkg/oci/remote"
|
||||
sigs "github.com/sigstore/cosign/pkg/signature"
|
||||
"github.com/sigstore/sigstore/pkg/cryptoutils"
|
||||
"github.com/sigstore/sigstore/pkg/signature"
|
||||
|
@ -269,7 +266,7 @@ func decodeStatement(payloadBase64 string) (map[string]interface{}, error) {
|
|||
// - in_toto.PredicateLinkV1
|
||||
// - in_toto.PredicateSPDX
|
||||
// any other custom predicate
|
||||
return common.ToMap(statement)
|
||||
return utils.ToMap(statement)
|
||||
}
|
||||
|
||||
return decodeCosignCustomProvenanceV01(statement)
|
||||
|
@ -297,7 +294,7 @@ func decodeCosignCustomProvenanceV01(statement in_toto.Statement) (map[string]in
|
|||
statement.Predicate = predicate
|
||||
}
|
||||
|
||||
return common.ToMap(statement)
|
||||
return utils.ToMap(statement)
|
||||
}
|
||||
|
||||
func stringToJSONMap(i interface{}) (map[string]interface{}, error) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
|
@ -10,53 +9,6 @@ import (
|
|||
"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 {
|
||||
if len(str) < 2 {
|
||||
return str
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/engine/common"
|
||||
"github.com/kyverno/kyverno/pkg/utils"
|
||||
)
|
||||
|
||||
// ActionData represents data available for action on current element
|
||||
|
@ -69,13 +69,13 @@ func (t *Traversal) traverseJSON(element interface{}, path string) (interface{},
|
|||
// traverse further
|
||||
switch typed := element.(type) {
|
||||
case map[string]interface{}:
|
||||
return t.traverseObject(common.CopyMap(typed), path)
|
||||
return t.traverseObject(utils.CopyMap(typed), path)
|
||||
|
||||
case []interface{}:
|
||||
return t.traverseList(common.CopySlice(typed), path)
|
||||
return t.traverseList(utils.CopySlice(typed), path)
|
||||
|
||||
case []map[string]interface{}:
|
||||
return t.traverseList(common.CopySliceOfMaps(typed), path)
|
||||
return t.traverseList(utils.CopySliceOfMaps(typed), path)
|
||||
|
||||
case Key:
|
||||
return typed.Key, nil
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
|
||||
"github.com/go-logr/logr"
|
||||
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/mutate/patch"
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
"github.com/kyverno/kyverno/pkg/engine/variables"
|
||||
"github.com/kyverno/kyverno/pkg/utils"
|
||||
"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) {
|
||||
jsonObj, err := common.ToMap(fe)
|
||||
jsonObj, err := utils.ToMap(fe)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@ import (
|
|||
"github.com/go-logr/logr"
|
||||
gojmespath "github.com/jmespath/go-jmespath"
|
||||
"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/variables"
|
||||
"github.com/kyverno/kyverno/pkg/utils"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"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 {
|
||||
ruleCopy := rule.DeepCopy()
|
||||
anyAllConditions, err := common.ToMap(foreach.AnyAllConditions)
|
||||
anyAllConditions, err := utils.ToMap(foreach.AnyAllConditions)
|
||||
if err != nil {
|
||||
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 {
|
||||
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)
|
||||
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 {
|
||||
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 {
|
||||
|
@ -216,14 +217,14 @@ func (v *validator) validate() *response.RuleResponse {
|
|||
|
||||
if v.pattern != nil || v.anyPattern != 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()
|
||||
if isUpdateRequest(v.ctx) {
|
||||
priorResp, err := validateOldObject(v.log, v.ctx, v.rule)
|
||||
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) {
|
||||
|
@ -241,14 +242,14 @@ func (v *validator) validate() *response.RuleResponse {
|
|||
|
||||
func (v *validator) validateForEach() *response.RuleResponse {
|
||||
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)
|
||||
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 {
|
||||
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
|
||||
|
@ -278,10 +279,10 @@ func (v *validator) validateForEach() *response.RuleResponse {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -295,7 +296,7 @@ func (v *validator) validateElements(foreach *kyverno.ForEachValidation, element
|
|||
ctx := v.ctx.Copy()
|
||||
if err := addElementToContext(ctx, e, i, elementScope); err != nil {
|
||||
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)
|
||||
|
@ -308,17 +309,17 @@ func (v *validator) validateElements(foreach *kyverno.ForEachValidation, element
|
|||
continue
|
||||
} else if r.Status != response.RuleStatusPass {
|
||||
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++
|
||||
}
|
||||
|
||||
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 {
|
||||
data, err := common.ToMap(e)
|
||||
data, err := utils.ToMap(e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -359,24 +360,24 @@ func (v *validator) validateDeny() *response.RuleResponse {
|
|||
anyAllCond := v.deny.GetAnyAllConditions()
|
||||
anyAllCond, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, anyAllCond)
|
||||
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 {
|
||||
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)
|
||||
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)
|
||||
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 {
|
||||
|
@ -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())
|
||||
|
||||
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 == "" {
|
||||
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")
|
||||
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 {
|
||||
|
@ -505,14 +506,14 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon
|
|||
anyPatterns, err := deserializeAnyPattern(v.anyPattern)
|
||||
if err != nil {
|
||||
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 {
|
||||
err := validate.MatchPattern(v.log, resource.Object, pattern)
|
||||
if err == nil {
|
||||
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 {
|
||||
|
@ -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))
|
||||
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) {
|
||||
|
|
|
@ -25,6 +25,53 @@ import (
|
|||
|
||||
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
|
||||
func contains(list []string, element string, fn func(string, string) bool) bool {
|
||||
for _, e := range list {
|
||||
|
|
|
@ -6,6 +6,36 @@ 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_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) {
|
||||
var list []string
|
||||
var element string
|
||||
|
|
Loading…
Reference in a new issue