mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
merge main and fmt
Signed-off-by: Jim Bugwadia <jim@nirmata.com>
This commit is contained in:
commit
4019d6b8b2
81 changed files with 845 additions and 2527 deletions
10
.github/workflows/build.yaml
vendored
10
.github/workflows/build.yaml
vendored
|
@ -40,6 +40,16 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
|
||||
- name: goimports
|
||||
run: |
|
||||
if [ "$(goimports -l . | wc -l)" -ne 0 ]
|
||||
then
|
||||
echo "The following files were found to have import formatting issues:"
|
||||
goimports -l -l .
|
||||
echo "Please run 'make fmt' to go format the above files."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: golangci-lint
|
||||
uses: reviewdog/action-golangci-lint@v1
|
||||
|
||||
|
|
17
Makefile
17
Makefile
|
@ -68,7 +68,7 @@ docker-publish-sbom: docker-build-sbom docker-push-sbom
|
|||
docker-build-sbom:
|
||||
@docker buildx build --file $(PWD)/$(ALPINE_PATH)/Dockerfile --tag $(REPO)/$(SBOM_IMAGE):$(IMAGE_TAG) .
|
||||
|
||||
docker-push-signature:
|
||||
docker-push-sbom:
|
||||
@docker buildx build --file $(PWD)/$(ALPINE_PATH)/Dockerfile --push --tag $(REPO)/$(SBOM_IMAGE):$(IMAGE_TAG) .
|
||||
@docker buildx build --file $(PWD)/$(ALPINE_PATH)/Dockerfile --push --tag $(REPO)/$(SBOM_IMAGE):latest .
|
||||
|
||||
|
@ -308,9 +308,20 @@ endif
|
|||
deepcopy-autogen: controller-gen
|
||||
$(CONTROLLER_GEN) object:headerFile="scripts/boilerplate.go.txt" paths="./..."
|
||||
|
||||
goimports:
|
||||
ifeq (, $(shell which goimports))
|
||||
@{ \
|
||||
echo "goimports not found!";\
|
||||
echo "installing goimports...";\
|
||||
go get golang.org/x/tools/cmd/goimports;\
|
||||
}
|
||||
else
|
||||
GO_IMPORTS=$(shell which goimports)
|
||||
endif
|
||||
|
||||
# Run go fmt against code
|
||||
fmt:
|
||||
gofmt -s -w .
|
||||
fmt: goimports
|
||||
go fmt ./... && $(GO_IMPORTS) -w ./
|
||||
|
||||
vet:
|
||||
go vet ./...
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/kyverno/kyverno/pkg/kyverno"
|
||||
)
|
||||
import "github.com/kyverno/kyverno/pkg/kyverno"
|
||||
|
||||
func main() {
|
||||
kyverno.CLI()
|
||||
|
|
1
go.mod
1
go.mod
|
@ -37,6 +37,7 @@ require (
|
|||
github.com/spf13/cobra v1.2.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
golang.org/x/tools v0.1.7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
gotest.tools v2.2.0+incompatible
|
||||
|
|
5
go.sum
5
go.sum
|
@ -1721,6 +1721,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
|
@ -1990,6 +1991,7 @@ golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg=
|
||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
@ -2156,6 +2158,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210903071746-97244b99971b h1:3Dq0eVHn0uaQJmPO+/aYPI/fRMqdrVDbu7MQcku54gg=
|
||||
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -2278,6 +2281,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -50,6 +50,7 @@ type GenerateRequestContext struct {
|
|||
AdmissionRequestInfo AdmissionRequestInfoObject `json:"admissionRequestInfo,omitempty" yaml:"admissionRequestInfo,omitempty"`
|
||||
}
|
||||
|
||||
// AdmissionRequestInfoObject stores the admission request and operation details
|
||||
type AdmissionRequestInfoObject struct {
|
||||
// +optional
|
||||
AdmissionRequest string `json:"admissionRequest,omitempty" yaml:"admissionRequest,omitempty"`
|
||||
|
|
|
@ -136,7 +136,7 @@ const (
|
|||
Fail FailurePolicyType = "Fail"
|
||||
)
|
||||
|
||||
// AnyAllCondition consists of conditions wrapped denoting a logical criteria to be fulfilled.
|
||||
// AnyAllConditions consists of conditions wrapped denoting a logical criteria to be fulfilled.
|
||||
// AnyConditions get fulfilled when at least one of its sub-conditions passes.
|
||||
// AllConditions get fulfilled only when all of its sub-conditions pass.
|
||||
type AnyAllConditions struct {
|
||||
|
@ -311,9 +311,10 @@ type ExcludeResources struct {
|
|||
ResourceDescription `json:"resources,omitempty" yaml:"resources,omitempty"`
|
||||
}
|
||||
|
||||
// ResourceFilters is a slice of ResourceFilter
|
||||
type ResourceFilters []ResourceFilter
|
||||
|
||||
// ResourceFilters allow users to "AND" or "OR" between resources
|
||||
// ResourceFilter allow users to "AND" or "OR" between resources
|
||||
type ResourceFilter struct {
|
||||
// UserInfo contains information about the user performing the operation.
|
||||
// +optional
|
||||
|
@ -411,12 +412,12 @@ type Mutation struct {
|
|||
// +optional
|
||||
PatchesJSON6902 string `json:"patchesJson6902,omitempty" yaml:"patchesJson6902,omitempty"`
|
||||
|
||||
// ForEach applies policy rule changes to nested elements.
|
||||
// ForEachMutation applies policy rule changes to nested elements.
|
||||
// +optional
|
||||
ForEachMutation []*ForEachMutation `json:"foreach,omitempty" yaml:"foreach,omitempty"`
|
||||
}
|
||||
|
||||
// ForEach applies policy rule changes to nested elements.
|
||||
// ForEachMutation applies policy rule changes to nested elements.
|
||||
type ForEachMutation struct {
|
||||
|
||||
// List specifies a JMESPath expression that results in one or more elements
|
||||
|
@ -427,7 +428,7 @@ type ForEachMutation struct {
|
|||
// +optional
|
||||
Context []ContextEntry `json:"context,omitempty" yaml:"context,omitempty"`
|
||||
|
||||
// Preconditions are used to determine if a policy rule should be applied by evaluating a
|
||||
// AnyAllConditions are used to determine if a policy rule should be applied by evaluating a
|
||||
// set of conditions. The declaration can contain nested `any` or `all` statements.
|
||||
// See: https://kyverno.io/docs/writing-policies/preconditions/
|
||||
// +kubebuilder:validation:XPreserveUnknownFields
|
||||
|
@ -498,7 +499,7 @@ type Deny struct {
|
|||
AnyAllConditions apiextensions.JSON `json:"conditions,omitempty" yaml:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// ForEach applies policy rule checks to nested elements.
|
||||
// ForEachValidation applies policy rule checks to nested elements.
|
||||
type ForEachValidation struct {
|
||||
|
||||
// List specifies a JMESPath expression that results in one or more elements
|
||||
|
@ -509,7 +510,7 @@ type ForEachValidation struct {
|
|||
// +optional
|
||||
Context []ContextEntry `json:"context,omitempty" yaml:"context,omitempty"`
|
||||
|
||||
// Preconditions are used to determine if a policy rule should be applied by evaluating a
|
||||
// AnyAllConditions are used to determine if a policy rule should be applied by evaluating a
|
||||
// set of conditions. The declaration can contain nested `any` or `all` statements.
|
||||
// See: https://kyverno.io/docs/writing-policies/preconditions/
|
||||
// +kubebuilder:validation:XPreserveUnknownFields
|
||||
|
|
|
@ -102,6 +102,7 @@ func (r Rule) HasGenerate() bool {
|
|||
return !reflect.DeepEqual(r.Generation, Generation{})
|
||||
}
|
||||
|
||||
// MatchKinds returns a slice of all kinds to match
|
||||
func (r Rule) MatchKinds() []string {
|
||||
matchKinds := r.MatchResources.ResourceDescription.Kinds
|
||||
for _, value := range r.MatchResources.All {
|
||||
|
@ -114,6 +115,7 @@ func (r Rule) MatchKinds() []string {
|
|||
return matchKinds
|
||||
}
|
||||
|
||||
// ExcludeKinds returns a slice of all kinds to exclude
|
||||
func (r Rule) ExcludeKinds() []string {
|
||||
excludeKinds := r.ExcludeResources.ResourceDescription.Kinds
|
||||
for _, value := range r.ExcludeResources.All {
|
||||
|
@ -250,13 +252,13 @@ func (rs ResourceSpec) ToKey() string {
|
|||
|
||||
// ViolatedRule stores the information regarding the rule.
|
||||
type ViolatedRule struct {
|
||||
// Specifies violated rule name.
|
||||
// Name specifies violated rule name.
|
||||
Name string `json:"name" yaml:"name"`
|
||||
|
||||
// Specifies violated rule type.
|
||||
// Type specifies violated rule type.
|
||||
Type string `json:"type" yaml:"type"`
|
||||
|
||||
// Specifies violation message.
|
||||
// Message specifies violation message.
|
||||
// +optional
|
||||
Message string `json:"message" yaml:"message"`
|
||||
|
||||
|
|
|
@ -22,8 +22,10 @@ import (
|
|||
|
||||
// Policy Reporting Modes
|
||||
const (
|
||||
Enforce = "enforce" // blocks the request on failure
|
||||
Audit = "audit" // dont block the request on failure, but report failiures as policy violations
|
||||
// Enforce blocks the request on failure
|
||||
Enforce = "enforce"
|
||||
// Audit indicates not to block the request on failure, but report failiures as policy violations
|
||||
Audit = "audit"
|
||||
)
|
||||
|
||||
// Policy Reporting Types
|
||||
|
@ -118,6 +120,7 @@ func VariableToJSON(key, value string) []byte {
|
|||
return jsonData
|
||||
}
|
||||
|
||||
// RetryFunc allows retrying a function on error within a given timeout
|
||||
func RetryFunc(retryInterval, timeout time.Duration, run func() error, logger logr.Logger) func() error {
|
||||
return func() error {
|
||||
registerTimeout := time.After(timeout)
|
||||
|
|
|
@ -104,12 +104,14 @@ func (cd *ConfigData) FilterNamespaces(namespaces []string) []string {
|
|||
return results
|
||||
}
|
||||
|
||||
// GetWebhooks returns the webhook configs
|
||||
func (cd *ConfigData) GetWebhooks() []WebhookConfig {
|
||||
cd.mux.RLock()
|
||||
defer cd.mux.RUnlock()
|
||||
return cd.webhooks
|
||||
}
|
||||
|
||||
// GetInitConfigMapName returns the init configmap name
|
||||
func (cd *ConfigData) GetInitConfigMapName() string {
|
||||
return cd.cmName
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ type MetricsConfigData struct {
|
|||
log logr.Logger
|
||||
}
|
||||
|
||||
// MetricsConfig stores the config for metrics
|
||||
type MetricsConfig struct {
|
||||
namespaces namespacesConfig
|
||||
metricsRefreshInterval time.Duration
|
||||
|
@ -44,10 +45,12 @@ func (mcd *MetricsConfigData) GetIncludeNamespaces() []string {
|
|||
return mcd.metricsConfig.namespaces.IncludeNamespaces
|
||||
}
|
||||
|
||||
// GetMetricsRefreshInterval returns the refresh interval for the metrics
|
||||
func (mcd *MetricsConfigData) GetMetricsRefreshInterval() time.Duration {
|
||||
return mcd.metricsConfig.metricsRefreshInterval
|
||||
}
|
||||
|
||||
// GetMetricsConfigMapName returns the configmap name for the metric
|
||||
func (mcd *MetricsConfigData) GetMetricsConfigMapName() string {
|
||||
return mcd.cmName
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package cosign
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/sigstore/cosign/pkg/cosign"
|
||||
)
|
||||
|
|
|
@ -49,6 +49,7 @@ func Initialize(client kubernetes.Interface, namespace, serviceAccount string, i
|
|||
return nil
|
||||
}
|
||||
|
||||
// VerifySignature verifies that the image has the expected key
|
||||
func VerifySignature(imageRef string, key []byte, repository string, log logr.Logger) (digest string, err error) {
|
||||
pubKey, err := decodePEM(key)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package cosign
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/sigstore/cosign/pkg/cosign"
|
||||
"gotest.tools/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const cosignPayload = `{
|
||||
|
|
|
@ -3,6 +3,7 @@ package cosign
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/sigstore/cosign/pkg/cosign"
|
||||
)
|
||||
|
|
|
@ -26,7 +26,7 @@ func NewConditionalAnchorError(msg string) ValidateAnchorError {
|
|||
}
|
||||
}
|
||||
|
||||
// IsConditionAnchorError ...
|
||||
// IsConditionAnchorError checks if the error is a conditional anchor error
|
||||
func (e ValidateAnchorError) IsConditionAnchorError() bool {
|
||||
return e.Err == ConditionalAnchorErr
|
||||
}
|
||||
|
@ -39,16 +39,17 @@ func NewGlobalAnchorError(msg string) ValidateAnchorError {
|
|||
}
|
||||
}
|
||||
|
||||
// IsConditionAnchorError ...
|
||||
// IsGlobalAnchorError checks if the error is a global anchor error
|
||||
func (e ValidateAnchorError) IsGlobalAnchorError() bool {
|
||||
return e.Err == GlobalAnchorErr
|
||||
}
|
||||
|
||||
// IsNil ...
|
||||
// IsNil checks if the error isn't populated
|
||||
func (e ValidateAnchorError) IsNil() bool {
|
||||
return e == ValidateAnchorError{}
|
||||
}
|
||||
|
||||
// Error returns an error instance of the anchor error
|
||||
func (e ValidateAnchorError) Error() error {
|
||||
return errors.New(e.Message)
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ func (ctx *Context) AddJSON(dataRaw []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AddJSON merges json data
|
||||
// AddJSONObject merges json data
|
||||
func (ctx *Context) AddJSONObject(jsonData interface{}) error {
|
||||
jsonBytes, err := json.Marshal(jsonData)
|
||||
if err != nil {
|
||||
|
|
|
@ -2,13 +2,14 @@ package engine
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/cosign"
|
||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
"github.com/kyverno/kyverno/pkg/engine/utils"
|
||||
"gotest.tools/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var test_policy_good = `{
|
||||
|
|
|
@ -336,7 +336,14 @@ func jpfSplit(arguments []interface{}) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return strings.Split(str.String(), sep.String()), nil
|
||||
split := strings.Split(str.String(), sep.String())
|
||||
arr := make([]interface{}, len(split))
|
||||
|
||||
for i, v := range split {
|
||||
arr[i] = v
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
}
|
||||
|
||||
func jpRegexReplaceAll(arguments []interface{}) (interface{}, error) {
|
||||
|
|
|
@ -128,7 +128,7 @@ func TestJMESPathFunctions_Split(t *testing.T) {
|
|||
result, err := jp.Search("")
|
||||
assert.NilError(t, err)
|
||||
|
||||
split, ok := result.([]string)
|
||||
split, ok := result.([]interface{})
|
||||
assert.Assert(t, ok)
|
||||
assert.Equal(t, split[0], "Hello")
|
||||
assert.Equal(t, split[1], "Gophers")
|
||||
|
|
|
@ -3,8 +3,9 @@ package engine
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"gotest.tools/assert"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func Test_parseMultilineBlockBody(t *testing.T) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package json_utils
|
||||
package jsonutils
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package json_utils
|
||||
package jsonutils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -2,9 +2,10 @@ package mutate
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
"testing"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
assert "github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package mutate
|
||||
|
||||
import (
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
"testing"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
|
|
@ -99,7 +99,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
|||
if rule.Mutation.ForEachMutation != nil {
|
||||
ruleResp, patchedResource = mutateForEachResource(ruleCopy, policyContext, patchedResource, logger)
|
||||
} else {
|
||||
err, mutateResp := mutateResource(ruleCopy, policyContext.JSONContext, patchedResource, logger, 0)
|
||||
mutateResp, err := mutateResource(ruleCopy, policyContext.JSONContext, patchedResource, logger, 0)
|
||||
if err != nil {
|
||||
if mutateResp.skip {
|
||||
ruleResp = ruleResponse(&policy.Spec.Rules[i], utils.Mutation, err.Error(), response.RuleStatusSkip)
|
||||
|
@ -174,7 +174,7 @@ func mutateForEachResource(rule *kyverno.Rule, ctx *PolicyContext, resource unst
|
|||
}
|
||||
|
||||
var skip = false
|
||||
err, mutateResp := mutateResource(rule, ctx.JSONContext, patchedResource, logger, foreachIndex)
|
||||
mutateResp, err := mutateResource(rule, ctx.JSONContext, patchedResource, logger, foreachIndex)
|
||||
if err != nil && !skip {
|
||||
return ruleResponse(rule, utils.Mutation, err.Error(), response.RuleStatusError), resource
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ type mutateResponse struct {
|
|||
message string
|
||||
}
|
||||
|
||||
func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructured.Unstructured, logger logr.Logger, foreachIndex int) (error, *mutateResponse) {
|
||||
func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructured.Unstructured, logger logr.Logger, foreachIndex int) (*mutateResponse, error) {
|
||||
mutateResp := &mutateResponse{false, unstructured.Unstructured{}, nil, ""}
|
||||
|
||||
// Pre-conditions checks for the list of foreach rules should ideally be performed once.
|
||||
|
@ -212,22 +212,22 @@ func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructu
|
|||
// Also, the foreach index parameter should be removed and a set of patches should be passed in.
|
||||
anyAllConditions, err := variables.SubstituteAllInPreconditions(logger, ctx, rule.AnyAllConditions)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to substitute vars in preconditions"), mutateResp
|
||||
return mutateResp, errors.Wrapf(err, "failed to substitute vars in preconditions")
|
||||
}
|
||||
|
||||
copyConditions, err := transformConditions(anyAllConditions)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to load context"), mutateResp
|
||||
return mutateResp, errors.Wrapf(err, "failed to load context")
|
||||
}
|
||||
|
||||
if !variables.EvaluateConditions(logger, ctx, copyConditions) {
|
||||
mutateResp.skip = true
|
||||
return fmt.Errorf("preconditions mismatch"), mutateResp
|
||||
return mutateResp, fmt.Errorf("preconditions mismatch")
|
||||
}
|
||||
|
||||
updatedRule, err := variables.SubstituteAllInRule(logger, ctx, *rule)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "variable substitution failed"), mutateResp
|
||||
return mutateResp, errors.Wrapf(err, "variable substitution failed")
|
||||
}
|
||||
|
||||
mutation := updatedRule.Mutation.DeepCopy()
|
||||
|
@ -238,7 +238,7 @@ func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructu
|
|||
// - overlay pattern does not match the resource conditions
|
||||
if resp.Patches == nil {
|
||||
mutateResp.skip = true
|
||||
return fmt.Errorf("resource does not match pattern"), mutateResp
|
||||
return mutateResp, fmt.Errorf("resource does not match pattern")
|
||||
}
|
||||
|
||||
mutateResp.skip = false
|
||||
|
@ -252,7 +252,7 @@ func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructu
|
|||
logger.Error(err, "failed to update resource in the JSON context")
|
||||
}
|
||||
|
||||
return nil, mutateResp
|
||||
return mutateResp, nil
|
||||
}
|
||||
|
||||
func startMutateResultResponse(resp *response.EngineResponse, policy kyverno.ClusterPolicy, resource unstructured.Unstructured) {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package operator
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Operator is string alias that represents selection operators enum
|
||||
type Operator string
|
||||
|
||||
|
@ -16,6 +20,10 @@ const (
|
|||
More Operator = ">"
|
||||
// Less stands for <
|
||||
Less Operator = "<"
|
||||
// InRange stands for -
|
||||
InRange Operator = "-"
|
||||
// NotInRange stands for !-
|
||||
NotInRange Operator = "!-"
|
||||
)
|
||||
|
||||
//ReferenceSign defines the operator for anchor reference
|
||||
|
@ -47,5 +55,13 @@ func GetOperatorFromStringPattern(pattern string) Operator {
|
|||
return NotEqual
|
||||
}
|
||||
|
||||
if match, _ := regexp.Match(`^(\d+(\.\d+)?)([^-]*)!-(\d+(\.\d+)?)([^-]*)$`, []byte(pattern)); match {
|
||||
return NotInRange
|
||||
}
|
||||
|
||||
if match, _ := regexp.Match(`^(\d+(\.\d+)?)([^-]*)-(\d+(\.\d+)?)([^-]*)$`, []byte(pattern)); match {
|
||||
return InRange
|
||||
}
|
||||
|
||||
return Equal
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package operator
|
||||
|
||||
import (
|
||||
"gotest.tools/assert"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestGetOperatorFromStringPattern_OneChar(t *testing.T) {
|
||||
|
@ -16,3 +17,19 @@ func TestGetOperatorFromStringPattern_EmptyString(t *testing.T) {
|
|||
func TestGetOperatorFromStringPattern_OnlyOperator(t *testing.T) {
|
||||
assert.Equal(t, GetOperatorFromStringPattern(">="), MoreEqual)
|
||||
}
|
||||
|
||||
func TestGetOperatorFromStringPattern_RangeOperator(t *testing.T) {
|
||||
assert.Equal(t, GetOperatorFromStringPattern("0-1"), InRange)
|
||||
assert.Equal(t, GetOperatorFromStringPattern("0Mi-1024Mi"), InRange)
|
||||
|
||||
assert.Equal(t, GetOperatorFromStringPattern("0!-1"), NotInRange)
|
||||
assert.Equal(t, GetOperatorFromStringPattern("0Mi!-1024Mi"), NotInRange)
|
||||
|
||||
assert.Equal(t, GetOperatorFromStringPattern("text1024Mi-2048Mi"), Equal)
|
||||
assert.Equal(t, GetOperatorFromStringPattern("test-value"), Equal)
|
||||
assert.Equal(t, GetOperatorFromStringPattern("value-*"), Equal)
|
||||
|
||||
assert.Equal(t, GetOperatorFromStringPattern("text1024Mi!-2048Mi"), Equal)
|
||||
assert.Equal(t, GetOperatorFromStringPattern("test!-value"), Equal)
|
||||
assert.Equal(t, GetOperatorFromStringPattern("value!-*"), Equal)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package response
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"gotest.tools/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var sourceYAML = `
|
||||
|
|
|
@ -81,17 +81,17 @@ func getRuleStatus(s string) (*RuleStatus, error) {
|
|||
return nil, fmt.Errorf("invalid status: %s", s)
|
||||
}
|
||||
|
||||
func (v *RuleStatus) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var s string
|
||||
if err := unmarshal(&s); err != nil {
|
||||
func (s *RuleStatus) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var str string
|
||||
if err := unmarshal(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
statusVal, err := getRuleStatus(s)
|
||||
statusVal, err := getRuleStatus(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = *statusVal
|
||||
*s = *statusVal
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,12 +2,13 @@ package utils
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||
commonAnchor "github.com/kyverno/kyverno/pkg/engine/anchor/common"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//RuleType defines the type for rule
|
||||
|
|
|
@ -177,17 +177,45 @@ func checkForAndConditionsAndValidate(log logr.Logger, value interface{}, patter
|
|||
// Handler for single pattern value during validation process
|
||||
// Detects if pattern has a number
|
||||
func validateValueWithStringPattern(log logr.Logger, value interface{}, pattern string) bool {
|
||||
operatorVariable := operator.GetOperatorFromStringPattern(pattern)
|
||||
|
||||
operator := operator.GetOperatorFromStringPattern(pattern)
|
||||
pattern = pattern[len(operator):]
|
||||
// Upon encountering InRange operator split the string by `-` and basically
|
||||
// verify the result of (x >= leftEndpoint & x <= rightEndpoint)
|
||||
if operatorVariable == operator.InRange {
|
||||
endpoints := strings.Split(pattern, "-")
|
||||
leftEndpoint, rightEndpoint := endpoints[0], endpoints[1]
|
||||
|
||||
gt := validateValueWithStringPattern(log, value, fmt.Sprintf(">=%s", leftEndpoint))
|
||||
if !gt {
|
||||
return false
|
||||
}
|
||||
pattern = fmt.Sprintf("<=%s", rightEndpoint)
|
||||
operatorVariable = operator.LessEqual
|
||||
}
|
||||
|
||||
// Upon encountering NotInRange operator split the string by `!-` and basically
|
||||
// verify the result of (x < leftEndpoint | x > rightEndpoint)
|
||||
if operatorVariable == operator.NotInRange {
|
||||
endpoints := strings.Split(pattern, "!-")
|
||||
leftEndpoint, rightEndpoint := endpoints[0], endpoints[1]
|
||||
|
||||
lt := validateValueWithStringPattern(log, value, fmt.Sprintf("<%s", leftEndpoint))
|
||||
if lt {
|
||||
return true
|
||||
}
|
||||
pattern = fmt.Sprintf(">%s", rightEndpoint)
|
||||
operatorVariable = operator.More
|
||||
}
|
||||
|
||||
pattern = pattern[len(operatorVariable):]
|
||||
pattern = strings.TrimSpace(pattern)
|
||||
number, str := getNumberAndStringPartsFromPattern(pattern)
|
||||
|
||||
if number == "" {
|
||||
return validateString(log, value, str, operator)
|
||||
return validateString(log, value, str, operatorVariable)
|
||||
}
|
||||
|
||||
return validateNumberWithStr(log, value, pattern, operator)
|
||||
return validateNumberWithStr(log, value, pattern, operatorVariable)
|
||||
}
|
||||
|
||||
// Handler for string values
|
||||
|
|
|
@ -310,6 +310,42 @@ func TestValidateValueWithStringPattern_WithSpace(t *testing.T) {
|
|||
assert.Assert(t, validateValueWithStringPattern(log.Log, 4, ">= 3"))
|
||||
}
|
||||
|
||||
func TestValidateValueWithStringPattern_Ranges(t *testing.T) {
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 0, "0-2"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 1, "0-2"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 2, "0-2"))
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, 3, "0-2"))
|
||||
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 0, "10!-20"))
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, 15, "10!-20"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 25, "10!-20"))
|
||||
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, 0, "0.00001-2.00001"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 1, "0.00001-2.00001"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 2, "0.00001-2.00001"))
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, 2.0001, "0.00001-2.00001"))
|
||||
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 0, "0.00001!-2.00001"))
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, 1, "0.00001!-2.00001"))
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, 2, "0.00001!-2.00001"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 2.0001, "0.00001!-2.00001"))
|
||||
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 2, "2-2"))
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, 2, "2!-2"))
|
||||
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 2.99999, "2.99998-3"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 2.99997, "2.99998!-3"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, 3.00001, "2.99998!-3"))
|
||||
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, "256Mi", "128Mi-512Mi"))
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, "1024Mi", "128Mi-512Mi"))
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, "64Mi", "128Mi-512Mi"))
|
||||
|
||||
assert.Assert(t, !validateValueWithStringPattern(log.Log, "256Mi", "128Mi!-512Mi"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, "1024Mi", "128Mi!-512Mi"))
|
||||
assert.Assert(t, validateValueWithStringPattern(log.Log, "64Mi", "128Mi!-512Mi"))
|
||||
}
|
||||
|
||||
func TestValidateNumberWithStr_LessFloatAndInt(t *testing.T) {
|
||||
assert.Assert(t, validateNumberWithStr(log.Log, 7.00001, "7.000001", operator.More))
|
||||
assert.Assert(t, validateNumberWithStr(log.Log, 7.00001, "7", operator.NotEqual))
|
||||
|
|
|
@ -425,7 +425,8 @@ func isSameRuleResponse(r1 *response.RuleResponse, r2 *response.RuleResponse) bo
|
|||
func (v *validator) validatePatterns(resource unstructured.Unstructured) *response.RuleResponse {
|
||||
if v.pattern != nil {
|
||||
if err := validate.MatchPattern(v.log, resource.Object, v.pattern); err != nil {
|
||||
if pe, ok := err.(*validate.PatternError); ok {
|
||||
pe, ok := err.(*validate.PatternError)
|
||||
if ok {
|
||||
v.log.V(3).Info("validation error", "path", pe.Path, "error", err.Error())
|
||||
|
||||
if pe.Skip {
|
||||
|
@ -437,9 +438,9 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon
|
|||
}
|
||||
|
||||
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusFail)
|
||||
} else {
|
||||
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusError)
|
||||
}
|
||||
|
||||
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusError)
|
||||
}
|
||||
|
||||
v.log.V(4).Info("successfully processed rule")
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -31,7 +31,7 @@ func (allin AllInHandler) Evaluate(key, value interface{}) bool {
|
|||
case []interface{}:
|
||||
var stringSlice []string
|
||||
for _, v := range typedKey {
|
||||
stringSlice = append(stringSlice, v.(string))
|
||||
stringSlice = append(stringSlice, fmt.Sprint(v))
|
||||
}
|
||||
return allin.validateValueWithStringSetPattern(stringSlice, value)
|
||||
default:
|
||||
|
@ -70,11 +70,7 @@ func allSetExistsInArray(key []string, value interface{}, log logr.Logger, allNo
|
|||
case []interface{}:
|
||||
var valueSlice []string
|
||||
for _, val := range valuesAvailable {
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
return true, false
|
||||
}
|
||||
valueSlice = append(valueSlice, v)
|
||||
valueSlice = append(valueSlice, fmt.Sprint(val))
|
||||
}
|
||||
if allNotIn {
|
||||
return false, isAllNotIn(key, valueSlice)
|
||||
|
@ -114,12 +110,7 @@ func isAllIn(key []string, value []string) bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
if found == len(key) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
return found == len(key)
|
||||
}
|
||||
|
||||
// isAllNotIn checks if all the values in S1 are not in S2
|
||||
|
|
|
@ -26,10 +26,12 @@ func (allnin AllNotInHandler) Evaluate(key, value interface{}) bool {
|
|||
switch typedKey := key.(type) {
|
||||
case string:
|
||||
return allnin.validateValueWithStringPattern(typedKey, value)
|
||||
case int, int32, int64, float32, float64:
|
||||
return allnin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
|
||||
case []interface{}:
|
||||
var stringSlice []string
|
||||
for _, v := range typedKey {
|
||||
stringSlice = append(stringSlice, v.(string))
|
||||
stringSlice = append(stringSlice, fmt.Sprint(v))
|
||||
}
|
||||
return allnin.validateValueWithStringSetPattern(stringSlice, value)
|
||||
default:
|
||||
|
|
|
@ -28,10 +28,12 @@ func (anyin AnyInHandler) Evaluate(key, value interface{}) bool {
|
|||
switch typedKey := key.(type) {
|
||||
case string:
|
||||
return anyin.validateValueWithStringPattern(typedKey, value)
|
||||
case int, int32, int64, float32, float64:
|
||||
return anyin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
|
||||
case []interface{}:
|
||||
var stringSlice []string
|
||||
for _, v := range typedKey {
|
||||
stringSlice = append(stringSlice, v.(string))
|
||||
stringSlice = append(stringSlice, fmt.Sprint(v))
|
||||
}
|
||||
return anyin.validateValueWithStringSetPattern(stringSlice, value)
|
||||
default:
|
||||
|
@ -70,11 +72,7 @@ func anySetExistsInArray(key []string, value interface{}, log logr.Logger, anyNo
|
|||
case []interface{}:
|
||||
var valueSlice []string
|
||||
for _, val := range valuesAvailable {
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
return true, false
|
||||
}
|
||||
valueSlice = append(valueSlice, v)
|
||||
valueSlice = append(valueSlice, fmt.Sprint(val))
|
||||
}
|
||||
if anyNotIn {
|
||||
return false, isAnyNotIn(key, valueSlice)
|
||||
|
@ -126,11 +124,7 @@ func isAnyNotIn(key []string, value []string) bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
if found < len(key) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return found < len(key)
|
||||
}
|
||||
|
||||
func (anyin AnyInHandler) validateValueWithBoolPattern(_ bool, _ interface{}) bool {
|
||||
|
|
|
@ -26,10 +26,12 @@ func (anynin AnyNotInHandler) Evaluate(key, value interface{}) bool {
|
|||
switch typedKey := key.(type) {
|
||||
case string:
|
||||
return anynin.validateValueWithStringPattern(typedKey, value)
|
||||
case int, int32, int64, float32, float64:
|
||||
return anynin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
|
||||
case []interface{}:
|
||||
var stringSlice []string
|
||||
for _, v := range typedKey {
|
||||
stringSlice = append(stringSlice, v.(string))
|
||||
stringSlice = append(stringSlice, fmt.Sprint(v))
|
||||
}
|
||||
return anynin.validateValueWithStringSetPattern(stringSlice, value)
|
||||
default:
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/minio/pkg/wildcard"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
|
@ -67,6 +68,26 @@ func (eh EqualHandler) validateValueWithMapPattern(key map[string]interface{}, v
|
|||
}
|
||||
|
||||
func (eh EqualHandler) validateValueWithStringPattern(key string, value interface{}) bool {
|
||||
// We need to check duration first as it's the only type that can be compared to a different type.
|
||||
durationKey, durationValue, err := parseDuration(key, value)
|
||||
if err == nil {
|
||||
return durationKey.Seconds() == durationValue.Seconds()
|
||||
}
|
||||
|
||||
// Attempt to extract resource quantity from string.
|
||||
resourceKey, err := resource.ParseQuantity(key)
|
||||
if err == nil {
|
||||
switch typedValue := value.(type) {
|
||||
case string:
|
||||
resourceValue, err := resource.ParseQuantity(typedValue)
|
||||
if err != nil {
|
||||
eh.log.Error(fmt.Errorf("parse error: "), "Failed to parse value type doesn't match key type")
|
||||
return false
|
||||
}
|
||||
return resourceKey.Equal(resourceValue)
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := value.(string); ok {
|
||||
return wildcard.Match(val, key)
|
||||
}
|
||||
|
|
|
@ -10,8 +10,9 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
)
|
||||
|
||||
// deprecated
|
||||
// NewInHandler returns handler to manage In operations
|
||||
//
|
||||
// Deprecated: Use `NewAllInHandler` or `NewAnyInHandler` instead
|
||||
func NewInHandler(log logr.Logger, ctx context.EvalInterface) OperatorHandler {
|
||||
return InHandler{
|
||||
ctx: ctx,
|
||||
|
@ -30,6 +31,8 @@ func (in InHandler) Evaluate(key, value interface{}) bool {
|
|||
switch typedKey := key.(type) {
|
||||
case string:
|
||||
return in.validateValueWithStringPattern(typedKey, value)
|
||||
case int, int32, int64, float32, float64:
|
||||
return in.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
|
||||
case []interface{}:
|
||||
var stringSlice []string
|
||||
for _, v := range typedKey {
|
||||
|
@ -60,18 +63,12 @@ func keyExistsInArray(key string, value interface{}, log logr.Logger) (invalidTy
|
|||
|
||||
case []interface{}:
|
||||
for _, val := range valuesAvailable {
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
return true, false
|
||||
}
|
||||
|
||||
if ok && wildcard.Match(key, v) {
|
||||
if wildcard.Match(key, fmt.Sprint(val)) {
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
|
||||
case string:
|
||||
|
||||
if wildcard.Match(valuesAvailable, key) {
|
||||
return false, true
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/minio/pkg/wildcard"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
|
@ -55,7 +56,7 @@ func (neh NotEqualHandler) validateValueWithSlicePattern(key []interface{}, valu
|
|||
return !reflect.DeepEqual(key, val)
|
||||
}
|
||||
neh.log.Info("Expected type []interface{}", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (neh NotEqualHandler) validateValueWithMapPattern(key map[string]interface{}, value interface{}) bool {
|
||||
|
@ -63,16 +64,36 @@ func (neh NotEqualHandler) validateValueWithMapPattern(key map[string]interface{
|
|||
return !reflect.DeepEqual(key, val)
|
||||
}
|
||||
neh.log.Info("Expected type map[string]interface{}", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (neh NotEqualHandler) validateValueWithStringPattern(key string, value interface{}) bool {
|
||||
// We need to check duration first as it's the only type that can be compared to a different type.
|
||||
durationKey, durationValue, err := parseDuration(key, value)
|
||||
if err == nil {
|
||||
return durationKey.Seconds() != durationValue.Seconds()
|
||||
}
|
||||
|
||||
// Attempt to extract resource quantity from string.
|
||||
resourceKey, err := resource.ParseQuantity(key)
|
||||
if err == nil {
|
||||
switch typedValue := value.(type) {
|
||||
case string:
|
||||
resourceValue, err := resource.ParseQuantity(typedValue)
|
||||
if err != nil {
|
||||
neh.log.Error(fmt.Errorf("parse error: "), "Failed to parse value type doesn't match key type")
|
||||
return false
|
||||
}
|
||||
return !resourceKey.Equal(resourceValue)
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := value.(string); ok {
|
||||
return !wildcard.Match(val, key)
|
||||
}
|
||||
|
||||
neh.log.Info("Expected type string", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (neh NotEqualHandler) validateValueWithFloatPattern(key float64, value interface{}) bool {
|
||||
|
@ -96,21 +117,21 @@ func (neh NotEqualHandler) validateValueWithFloatPattern(key float64, value inte
|
|||
float64Num, err := strconv.ParseFloat(typedValue, 64)
|
||||
if err != nil {
|
||||
neh.log.Error(err, "Failed to parse float64 from string")
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return float64Num != key
|
||||
default:
|
||||
neh.log.Info("Expected type float", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (neh NotEqualHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {
|
||||
typedValue, ok := value.(bool)
|
||||
if !ok {
|
||||
neh.log.Info("Expected type bool", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return key != typedValue
|
||||
}
|
||||
|
@ -133,11 +154,11 @@ func (neh NotEqualHandler) validateValueWithIntPattern(key int64, value interfac
|
|||
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
|
||||
if err != nil {
|
||||
neh.log.Error(err, "Failed to parse int64 from string")
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return int64Num != key
|
||||
default:
|
||||
neh.log.Info("Expected type int", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
)
|
||||
|
||||
// deprecated
|
||||
//NewNotInHandler returns handler to manage NotIn operations
|
||||
//
|
||||
// Deprecated: Use `NewAllNotInHandler` or `NewAnyNotInHandler` instead
|
||||
func NewNotInHandler(log logr.Logger, ctx context.EvalInterface) OperatorHandler {
|
||||
return NotInHandler{
|
||||
ctx: ctx,
|
||||
|
@ -27,6 +28,8 @@ func (nin NotInHandler) Evaluate(key, value interface{}) bool {
|
|||
switch typedKey := key.(type) {
|
||||
case string:
|
||||
return nin.validateValueWithStringPattern(typedKey, value)
|
||||
case int, int32, int64, float32, float64:
|
||||
return nin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
|
||||
case []interface{}:
|
||||
var stringSlice []string
|
||||
for _, v := range typedKey {
|
||||
|
|
|
@ -3,7 +3,6 @@ package operator
|
|||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
|
@ -38,8 +37,16 @@ func compareByCondition(key float64, value float64, op kyverno.ConditionOperator
|
|||
return key <= value
|
||||
case kyverno.LessThan:
|
||||
return key < value
|
||||
case kyverno.Equals:
|
||||
return key == value
|
||||
case kyverno.Equal:
|
||||
return key == value
|
||||
case kyverno.NotEquals:
|
||||
return key != value
|
||||
case kyverno.NotEqual:
|
||||
return key != value
|
||||
default:
|
||||
(*log).Info(fmt.Sprintf("Expected operator, one of [GreaterThanOrEquals, GreaterThan, LessThanOrEquals, LessThan], found %s", op))
|
||||
(*log).Info(fmt.Sprintf("Expected operator, one of [GreaterThanOrEquals, GreaterThan, LessThanOrEquals, LessThan, Equals, NotEquals], found %s", op))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +76,10 @@ func (noh NumericOperatorHandler) validateValueWithIntPattern(key int64, value i
|
|||
case float64:
|
||||
return compareByCondition(float64(key), typedValue, noh.condition, &noh.log)
|
||||
case string:
|
||||
durationKey, durationValue, err := parseDuration(key, value)
|
||||
if err == nil {
|
||||
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
|
||||
}
|
||||
// extract float64 and (if that fails) then, int64 from the string
|
||||
float64val, err := strconv.ParseFloat(typedValue, 64)
|
||||
if err == nil {
|
||||
|
@ -95,6 +106,10 @@ func (noh NumericOperatorHandler) validateValueWithFloatPattern(key float64, val
|
|||
case float64:
|
||||
return compareByCondition(key, typedValue, noh.condition, &noh.log)
|
||||
case string:
|
||||
durationKey, durationValue, err := parseDuration(key, value)
|
||||
if err == nil {
|
||||
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
|
||||
}
|
||||
float64val, err := strconv.ParseFloat(typedValue, 64)
|
||||
if err == nil {
|
||||
return compareByCondition(key, float64val, noh.condition, &noh.log)
|
||||
|
@ -128,7 +143,7 @@ func (noh NumericOperatorHandler) validateValueWithResourcePattern(key resource.
|
|||
|
||||
func (noh NumericOperatorHandler) validateValueWithStringPattern(key string, value interface{}) bool {
|
||||
// We need to check duration first as it's the only type that can be compared to a different type
|
||||
durationKey, durationValue, err := noh.parseDuration(key, value)
|
||||
durationKey, durationValue, err := parseDuration(key, value)
|
||||
if err == nil {
|
||||
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
|
||||
}
|
||||
|
@ -152,67 +167,6 @@ func (noh NumericOperatorHandler) validateValueWithStringPattern(key string, val
|
|||
return false
|
||||
}
|
||||
|
||||
func (noh NumericOperatorHandler) parseDuration(key, value interface{}) (*time.Duration, *time.Duration, error) {
|
||||
var keyDuration *time.Duration
|
||||
var valueDuration *time.Duration
|
||||
var err error
|
||||
|
||||
// We need to first ensure at least one of the values is actually a duration string
|
||||
switch typedKey := key.(type) {
|
||||
case string:
|
||||
duration, err := time.ParseDuration(typedKey)
|
||||
if err == nil && key != "0" {
|
||||
keyDuration = &duration
|
||||
}
|
||||
}
|
||||
switch typedValue := value.(type) {
|
||||
case string:
|
||||
duration, err := time.ParseDuration(typedValue)
|
||||
if err == nil && value != "0" {
|
||||
valueDuration = &duration
|
||||
}
|
||||
}
|
||||
if keyDuration == nil && valueDuration == nil {
|
||||
return keyDuration, valueDuration, fmt.Errorf("neither value is a duration")
|
||||
}
|
||||
|
||||
if keyDuration == nil {
|
||||
var duration time.Duration
|
||||
|
||||
switch typedKey := key.(type) {
|
||||
case int:
|
||||
duration = time.Duration(typedKey) * time.Second
|
||||
case int64:
|
||||
duration = time.Duration(typedKey) * time.Second
|
||||
case float64:
|
||||
duration = time.Duration(typedKey) * time.Second
|
||||
default:
|
||||
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
|
||||
}
|
||||
|
||||
keyDuration = &duration
|
||||
}
|
||||
|
||||
if valueDuration == nil {
|
||||
var duration time.Duration
|
||||
|
||||
switch typedValue := value.(type) {
|
||||
case int:
|
||||
duration = time.Duration(typedValue) * time.Second
|
||||
case int64:
|
||||
duration = time.Duration(typedValue) * time.Second
|
||||
case float64:
|
||||
duration = time.Duration(typedValue) * time.Second
|
||||
default:
|
||||
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
|
||||
}
|
||||
|
||||
valueDuration = &duration
|
||||
}
|
||||
|
||||
return keyDuration, valueDuration, err
|
||||
}
|
||||
|
||||
// the following functions are unreachable because the key is strictly supposed to be numeric
|
||||
// still the following functions are just created to make NumericOperatorHandler struct implement OperatorHandler interface
|
||||
func (noh NumericOperatorHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package operator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
|
@ -74,3 +76,64 @@ func CreateOperatorHandler(log logr.Logger, ctx context.EvalInterface, op kyvern
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseDuration(key, value interface{}) (*time.Duration, *time.Duration, error) {
|
||||
var keyDuration *time.Duration
|
||||
var valueDuration *time.Duration
|
||||
var err error
|
||||
|
||||
// We need to first ensure at least one of the values is actually a duration string.
|
||||
switch typedKey := key.(type) {
|
||||
case string:
|
||||
duration, err := time.ParseDuration(typedKey)
|
||||
if err == nil && key != "0" {
|
||||
keyDuration = &duration
|
||||
}
|
||||
}
|
||||
switch typedValue := value.(type) {
|
||||
case string:
|
||||
duration, err := time.ParseDuration(typedValue)
|
||||
if err == nil && value != "0" {
|
||||
valueDuration = &duration
|
||||
}
|
||||
}
|
||||
if keyDuration == nil && valueDuration == nil {
|
||||
return keyDuration, valueDuration, fmt.Errorf("neither value is a duration")
|
||||
}
|
||||
|
||||
if keyDuration == nil {
|
||||
var duration time.Duration
|
||||
|
||||
switch typedKey := key.(type) {
|
||||
case int:
|
||||
duration = time.Duration(typedKey) * time.Second
|
||||
case int64:
|
||||
duration = time.Duration(typedKey) * time.Second
|
||||
case float64:
|
||||
duration = time.Duration(typedKey) * time.Second
|
||||
default:
|
||||
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
|
||||
}
|
||||
|
||||
keyDuration = &duration
|
||||
}
|
||||
|
||||
if valueDuration == nil {
|
||||
var duration time.Duration
|
||||
|
||||
switch typedValue := value.(type) {
|
||||
case int:
|
||||
duration = time.Duration(typedValue) * time.Second
|
||||
case int64:
|
||||
duration = time.Duration(typedValue) * time.Second
|
||||
case float64:
|
||||
duration = time.Duration(typedValue) * time.Second
|
||||
default:
|
||||
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
|
||||
}
|
||||
|
||||
valueDuration = &duration
|
||||
}
|
||||
|
||||
return keyDuration, valueDuration, err
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/engine/anchor/common"
|
||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
jsonUtils "github.com/kyverno/kyverno/pkg/engine/json-utils"
|
||||
jsonUtils "github.com/kyverno/kyverno/pkg/engine/jsonutils"
|
||||
"github.com/kyverno/kyverno/pkg/engine/operator"
|
||||
)
|
||||
|
||||
|
@ -21,10 +21,10 @@ var RegexVariables = regexp.MustCompile(`^\{\{[^{}]*\}\}|[^\\]\{\{[^{}]*\}\}`)
|
|||
|
||||
var RegexEscpVariables = regexp.MustCompile(`\\\{\{[^{}]*\}\}`)
|
||||
|
||||
// Regex for '$(...)' at the beginning of the string, and 'x$(...)' where 'x' is not '\'
|
||||
// RegexReferences is the Regex for '$(...)' at the beginning of the string, and 'x$(...)' where 'x' is not '\'
|
||||
var RegexReferences = regexp.MustCompile(`^\$\(.[^\ ]*\)|[^\\]\$\(.[^\ ]*\)`)
|
||||
|
||||
// Regex for '\$(...)'
|
||||
// RegexEscpReferences is the Regex for '\$(...)'
|
||||
var RegexEscpReferences = regexp.MustCompile(`\\\$\(.[^\ ]*\)`)
|
||||
|
||||
var regexVariableInit = regexp.MustCompile(`^\{\{[^{}]*\}\}`)
|
||||
|
@ -298,7 +298,7 @@ func substituteReferencesIfAny(log logr.Logger) jsonUtils.Action {
|
|||
|
||||
for _, v := range RegexReferences.FindAllString(value, -1) {
|
||||
initial := v[:2] == `$(`
|
||||
v_old := v
|
||||
old := v
|
||||
|
||||
if !initial {
|
||||
v = v[1:]
|
||||
|
@ -321,15 +321,15 @@ func substituteReferencesIfAny(log logr.Logger) jsonUtils.Action {
|
|||
log.V(3).Info("reference resolved", "reference", v, "value", resolvedReference, "path", data.Path)
|
||||
|
||||
if val, ok := resolvedReference.(string); ok {
|
||||
replace_with := ""
|
||||
replacement := ""
|
||||
|
||||
if !initial {
|
||||
replace_with = string(v_old[0])
|
||||
replacement = string(old[0])
|
||||
}
|
||||
|
||||
replace_with += val
|
||||
replacement += val
|
||||
|
||||
value = strings.Replace(value, v_old, replace_with, 1)
|
||||
value = strings.Replace(value, old, replacement, 1)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,7 @@ func substituteVariablesIfAny(log logr.Logger, ctx context.EvalInterface, vr Var
|
|||
|
||||
for _, v := range vars {
|
||||
initial := len(regexVariableInit.FindAllString(v, -1)) > 0
|
||||
v_old := v
|
||||
old := v
|
||||
|
||||
if !initial {
|
||||
v = v[1:]
|
||||
|
@ -406,7 +406,7 @@ func substituteVariablesIfAny(log logr.Logger, ctx context.EvalInterface, vr Var
|
|||
prefix := ""
|
||||
|
||||
if !initial {
|
||||
prefix = string(v_old[0])
|
||||
prefix = string(old[0])
|
||||
}
|
||||
|
||||
if value, err = substituteVarInPattern(prefix, originalPattern, v, substitutedVar); err != nil {
|
||||
|
@ -524,7 +524,7 @@ func valFromReferenceToString(value interface{}, operator string) (string, error
|
|||
func FindAndShiftReferences(log logr.Logger, value, shift, pivot string) string {
|
||||
for _, reference := range RegexReferences.FindAllString(value, -1) {
|
||||
initial := reference[:2] == `$(`
|
||||
reference_old := reference
|
||||
oldReference := reference
|
||||
|
||||
if !initial {
|
||||
reference = reference[1:]
|
||||
|
@ -542,15 +542,15 @@ func FindAndShiftReferences(log logr.Logger, value, shift, pivot string) string
|
|||
}
|
||||
|
||||
shiftedReference := strings.Replace(reference, pivot, pivot+"/"+shift, -1)
|
||||
replace_with := ""
|
||||
replacement := ""
|
||||
|
||||
if !initial {
|
||||
replace_with = string(reference_old[0])
|
||||
replacement = string(oldReference[0])
|
||||
}
|
||||
|
||||
replace_with += shiftedReference
|
||||
replacement += shiftedReference
|
||||
|
||||
value = strings.Replace(value, reference_old, replace_with, 1)
|
||||
value = strings.Replace(value, oldReference, replacement, 1)
|
||||
}
|
||||
|
||||
return value
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
ju "github.com/kyverno/kyverno/pkg/engine/json-utils"
|
||||
ju "github.com/kyverno/kyverno/pkg/engine/jsonutils"
|
||||
"gotest.tools/assert"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
|
|
@ -435,7 +435,15 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
|
|||
label["policy.kyverno.io/synchronize"] = "enable"
|
||||
newResource.SetLabels(label)
|
||||
|
||||
if _, err := ValidateResourceWithPattern(logger, generatedObj.Object, rdata); err != nil {
|
||||
if genAPIVersion == "" {
|
||||
generatedResourceAPIVersion := generatedObj.GetAPIVersion()
|
||||
newResource.SetAPIVersion(generatedResourceAPIVersion)
|
||||
}
|
||||
if genNamespace == "" {
|
||||
newResource.SetNamespace("default")
|
||||
}
|
||||
|
||||
if _, err := ValidateResourceWithPattern(logger, generatedObj.Object, newResource.Object); err != nil {
|
||||
_, err = client.UpdateResource(genAPIVersion, genKind, genNamespace, newResource, false)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to update resource")
|
||||
|
|
|
@ -336,30 +336,37 @@ func checkMutateLogPath(mutateLogPath string) (mutateLogPathIsDir bool, err erro
|
|||
|
||||
// printReportOrViolation - printing policy report/violations
|
||||
func printReportOrViolation(policyReport bool, rc *common.ResultCounts, resourcePaths []string, resourcesLen int, skipInvalidPolicies SkippedInvalidPolicies, stdin bool, pvInfos []policyreport.Info) {
|
||||
divider := "----------------------------------------------------------------------"
|
||||
|
||||
if len(skipInvalidPolicies.skipped) > 0 {
|
||||
fmt.Println("----------------------------------------------------------------------\nPolicies Skipped(as required variables are not provided by the users):")
|
||||
fmt.Println(divider)
|
||||
fmt.Println("Policies Skipped (as required variables are not provided by the user):")
|
||||
for i, policyName := range skipInvalidPolicies.skipped {
|
||||
fmt.Println(i+1, ". ", policyName)
|
||||
fmt.Printf("%d. %s\n", i+1, policyName)
|
||||
}
|
||||
fmt.Println("----------------------------------------------------------------------")
|
||||
fmt.Println(divider)
|
||||
}
|
||||
if len(skipInvalidPolicies.invalid) > 0 {
|
||||
fmt.Println("----------------------------------------------------------------------\nInvalid Policies:")
|
||||
fmt.Println(divider)
|
||||
fmt.Println("Invalid Policies:")
|
||||
for i, policyName := range skipInvalidPolicies.invalid {
|
||||
fmt.Println(i+1, ". ", policyName)
|
||||
fmt.Printf("%d. %s\n", i+1, policyName)
|
||||
}
|
||||
fmt.Println("----------------------------------------------------------------------")
|
||||
fmt.Println(divider)
|
||||
}
|
||||
|
||||
if policyReport {
|
||||
resps := buildPolicyReports(pvInfos)
|
||||
if len(resps) > 0 || resourcesLen == 0 {
|
||||
fmt.Println("\n----------------------------------------------------------------------\nPOLICY REPORT:\n----------------------------------------------------------------------")
|
||||
fmt.Println(divider)
|
||||
fmt.Println("POLICY REPORT:")
|
||||
fmt.Println(divider)
|
||||
report, _ := generateCLIRaw(resps)
|
||||
yamlReport, _ := yaml1.Marshal(report)
|
||||
fmt.Println(string(yamlReport))
|
||||
} else {
|
||||
fmt.Println("----------------------------------------------------------------------\nPOLICY REPORT: skip generating policy report (no validate policy found/resource skipped)")
|
||||
fmt.Println(divider)
|
||||
fmt.Println("POLICY REPORT: skip generating policy report (no validate policy found/resource skipped)")
|
||||
}
|
||||
} else {
|
||||
if !stdin {
|
||||
|
|
|
@ -81,10 +81,10 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error)
|
|||
err error
|
||||
)
|
||||
|
||||
isHttpPath := IsHttpRegex.MatchString(path)
|
||||
isHTTPPath := IsHTTPRegex.MatchString(path)
|
||||
|
||||
// path clean and retrieving file info can be possible if it's not an HTTP URL
|
||||
if !isHttpPath {
|
||||
if !isHTTPPath {
|
||||
path = filepath.Clean(path)
|
||||
fileDesc, err = os.Stat(path)
|
||||
if err != nil {
|
||||
|
@ -95,7 +95,7 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error)
|
|||
}
|
||||
|
||||
// apply file from a directory is possible only if the path is not HTTP URL
|
||||
if !isHttpPath && fileDesc.IsDir() {
|
||||
if !isHTTPPath && fileDesc.IsDir() {
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("failed to process %v: %v", path, err.Error())
|
||||
|
@ -117,7 +117,7 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error)
|
|||
|
||||
} else {
|
||||
var fileBytes []byte
|
||||
if isHttpPath {
|
||||
if isHTTPPath {
|
||||
// We accept here that a random URL might be called based on user provided input.
|
||||
resp, err := http.Get(path) // #nosec
|
||||
if err != nil {
|
||||
|
@ -258,7 +258,7 @@ func PolicyHasNonAllowedVariables(policy v1.ClusterPolicy) error {
|
|||
matchesAll := RegexVariables.FindAllStringSubmatch(string(ruleJSON), -1)
|
||||
matchesAllowed := AllowedVariables.FindAllStringSubmatch(string(ruleJSON), -1)
|
||||
if (len(matchesAll) > len(matchesAllowed)) && len(rule.Context) == 0 {
|
||||
allowed := "{{request.*}}, {{element.*}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}}, and context variables"
|
||||
allowed := "{{request.*}}, {{element.*}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}}, {{images.*}} and context variables"
|
||||
return fmt.Errorf("rule \"%s\" has forbidden variables. Allowed variables are: %s", rule.Name, allowed)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,7 +220,7 @@ func getFileBytes(path string) ([]byte, error) {
|
|||
err error
|
||||
)
|
||||
|
||||
if IsHttpRegex.MatchString(path) {
|
||||
if IsHTTPRegex.MatchString(path) {
|
||||
// We accept here that a random URL might be called based on user provided input.
|
||||
resp, err := http.Get(path) // #nosec
|
||||
if err != nil {
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
// RegexVariables represents regex for '{{}}'
|
||||
var RegexVariables = regexp.MustCompile(`\{\{[^{}]*\}\}`)
|
||||
|
||||
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}} and functions e.g. {{divide(<num>,<num>))}}
|
||||
var AllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace|element\.|@|([a-z_0-9]+\())[^{}]*\}\}`)
|
||||
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}}, {{element.}}, {{images.}}
|
||||
var AllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace|element\.|@|images\.|([a-z_0-9]+\())[^{}]*\}\}`)
|
||||
|
||||
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}
|
||||
// WildCardAllowedVariables represents regex for the allowed fields in wildcards
|
||||
var WildCardAllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace)[^{}]*\}\}`)
|
||||
|
||||
// IsHttpRegex represents regex for starts with http:// or https://
|
||||
var IsHttpRegex = regexp.MustCompile("^(http|https)://")
|
||||
// IsHTTPRegex represents regex for starts with http:// or https://
|
||||
var IsHTTPRegex = regexp.MustCompile("^(http|https)://")
|
||||
|
|
|
@ -521,9 +521,9 @@ func isNamespacedPolicy(policyNames string) (bool, error) {
|
|||
|
||||
func getUserDefinedPolicyNameAndNamespace(policyName string) (string, string) {
|
||||
if strings.Contains(policyName, "/") {
|
||||
policy_n_ns := strings.Split(policyName, "/")
|
||||
namespace := policy_n_ns[0]
|
||||
policy := policy_n_ns[1]
|
||||
parts := strings.Split(policyName, "/")
|
||||
namespace := parts[0]
|
||||
policy := parts[1]
|
||||
return namespace, policy
|
||||
}
|
||||
return "", policyName
|
||||
|
|
|
@ -197,11 +197,11 @@ func addCacheHelper(rmr kyverno.ResourceFilter, m *pMap, rule kyverno.Rule, muta
|
|||
}
|
||||
}
|
||||
|
||||
func (pc *pMap) get(key PolicyType, gvk, namespace string) (names []string) {
|
||||
pc.RLock()
|
||||
defer pc.RUnlock()
|
||||
func (m *pMap) get(key PolicyType, gvk, namespace string) (names []string) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
_, kind := common.GetKindFromGVK(gvk)
|
||||
for _, policyName := range pc.kindDataMap[kind][key] {
|
||||
for _, policyName := range m.kindDataMap[kind][key] {
|
||||
ns, key, isNamespacedPolicy := policy2.ParseNamespacedPolicy(policyName)
|
||||
if !isNamespacedPolicy && namespace == "" {
|
||||
names = append(names, key)
|
||||
|
@ -262,19 +262,19 @@ func removeCacheHelper(rmr kyverno.ResourceFilter, m *pMap, pName string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (m *policyCache) getPolicyObject(key PolicyType, gvk string, nspace string) (policyObject []*kyverno.ClusterPolicy) {
|
||||
func (pc *policyCache) getPolicyObject(key PolicyType, gvk string, nspace string) (policyObject []*kyverno.ClusterPolicy) {
|
||||
_, kind := common.GetKindFromGVK(gvk)
|
||||
policyNames := m.pMap.get(key, kind, nspace)
|
||||
wildcardPolicies := m.pMap.get(key, "*", nspace)
|
||||
policyNames := pc.pMap.get(key, kind, nspace)
|
||||
wildcardPolicies := pc.pMap.get(key, "*", nspace)
|
||||
policyNames = append(policyNames, wildcardPolicies...)
|
||||
for _, policyName := range policyNames {
|
||||
var policy *kyverno.ClusterPolicy
|
||||
ns, key, isNamespacedPolicy := policy2.ParseNamespacedPolicy(policyName)
|
||||
if !isNamespacedPolicy {
|
||||
policy, _ = m.pLister.Get(key)
|
||||
policy, _ = pc.pLister.Get(key)
|
||||
} else {
|
||||
if ns == nspace {
|
||||
nspolicy, _ := m.npLister.Policies(ns).Get(key)
|
||||
nspolicy, _ := pc.npLister.Policies(ns).Get(key)
|
||||
policy = policy2.ConvertPolicyToClusterPolicy(nspolicy)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ const (
|
|||
deletedAnnotationResourceName string = "kyverno.io/delete.resource.name"
|
||||
deletedAnnotationResourceKind string = "kyverno.io/delete.resource.kind"
|
||||
|
||||
// static value for PolicyReportResult.Source
|
||||
// SourceValue is the static value for PolicyReportResult.Source
|
||||
SourceValue = "Kyverno"
|
||||
)
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ type Scenario struct {
|
|||
TestCases []TestCase
|
||||
}
|
||||
|
||||
//CaseT defines input and output for a case
|
||||
// TestCase defines input and output for a case
|
||||
type TestCase struct {
|
||||
Input Input `yaml:"input"`
|
||||
Expected Expected `yaml:"expected"`
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package testrunner
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
"gopkg.in/yaml.v3"
|
||||
"gotest.tools/assert"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var sourceYAML = `
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
var regexVersion = regexp.MustCompile(`v(\d+).(\d+).(\d+)\.*`)
|
||||
|
||||
//Contains Check if strint 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 {
|
||||
for _, e := range list {
|
||||
if fn(e, element) {
|
||||
|
@ -49,7 +49,7 @@ func ContainsNamepace(patterns []string, ns string) bool {
|
|||
return contains(patterns, ns, compareNamespaces)
|
||||
}
|
||||
|
||||
//ContainsString check if the string is contains in a list
|
||||
// ContainsString checks if the string is contained in the list
|
||||
func ContainsString(list []string, element string) bool {
|
||||
return contains(list, element, compareString)
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ func HigherThanKubernetesVersion(client *client.Client, log logr.Logger, major,
|
|||
|
||||
b, err := isVersionHigher(serverVersion.String(), major, minor, patch)
|
||||
if err != nil {
|
||||
logger.Error(err, "serverVersion", serverVersion)
|
||||
logger.Error(err, "serverVersion", serverVersion.String())
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -168,27 +168,29 @@ func HigherThanKubernetesVersion(client *client.Client, log logr.Logger, major,
|
|||
}
|
||||
|
||||
func isVersionHigher(version string, major int, minor int, patch int) (bool, error) {
|
||||
groups := regexVersion.FindAllStringSubmatch(version, -1)
|
||||
if len(groups) != 1 || len(groups[0]) != 4 {
|
||||
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[0][1])
|
||||
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[0][2])
|
||||
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[0][3])
|
||||
currentPatch, err := strconv.Atoi(groups[3])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to extract minor version from %s", version)
|
||||
}
|
||||
|
||||
if currentMajor <= major && currentMinor <= minor && currentPatch <= patch {
|
||||
if currentMajor < major ||
|
||||
(currentMajor == major && currentMinor < minor) ||
|
||||
(currentMajor == major && currentMinor == minor && currentPatch <= patch) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
@ -212,7 +214,7 @@ func SliceContains(slice []string, values ...string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// ApiextensionsJsonTOKyvernoConditions takes in user-provided conditions in abstract apiextensions.JSON form
|
||||
// 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.
|
||||
func ApiextensionsJsonToKyvernoConditions(original apiextensions.JSON) (interface{}, error) {
|
||||
|
|
|
@ -91,6 +91,15 @@ func Test_higherVersion(t *testing.T) {
|
|||
|
||||
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) {
|
||||
|
|
|
@ -175,11 +175,11 @@ func (wrc *Register) Remove(cleanUp chan<- struct{}) {
|
|||
|
||||
}
|
||||
|
||||
// +deprecated
|
||||
// UpdateWebhookConfigurations updates resource webhook configurations dynamically
|
||||
// base on the UPDATEs of Kyverno init-config ConfigMap
|
||||
//
|
||||
// it currently updates namespaceSelector only, can be extend to update other fields
|
||||
// +deprecated
|
||||
func (wrc *Register) UpdateWebhookConfigurations(configHandler config.Interface) {
|
||||
logger := wrc.log.WithName("UpdateWebhookConfigurations")
|
||||
for {
|
||||
|
@ -622,12 +622,12 @@ func (wrc *Register) checkEndpoint() error {
|
|||
}
|
||||
|
||||
kyverno := pods.Items[0]
|
||||
podIp, _, err := unstructured.NestedString(kyverno.UnstructuredContent(), "status", "podIP")
|
||||
podIP, _, err := unstructured.NestedString(kyverno.UnstructuredContent(), "status", "podIP")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract pod IP: %v", err)
|
||||
}
|
||||
|
||||
if podIp == "" {
|
||||
if podIP == "" {
|
||||
return fmt.Errorf("pod is not assigned to any node yet")
|
||||
}
|
||||
|
||||
|
@ -637,7 +637,7 @@ func (wrc *Register) checkEndpoint() error {
|
|||
}
|
||||
|
||||
for _, addr := range subset.Addresses {
|
||||
if addr.IP == podIp {
|
||||
if addr.IP == podIP {
|
||||
wrc.log.Info("Endpoint ready", "ns", config.KyvernoNamespace, "name", config.KyvernoServiceName)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -300,20 +300,14 @@ func (ws *WebhookServer) resourceMutation(request *v1beta1.AdmissionRequest) *v1
|
|||
requestTime := time.Now().Unix()
|
||||
kind := request.Kind.Kind
|
||||
mutatePolicies := ws.pCache.GetPolicies(policycache.Mutate, kind, request.Namespace)
|
||||
generatePolicies := ws.pCache.GetPolicies(policycache.Generate, kind, request.Namespace)
|
||||
verifyImagesPolicies := ws.pCache.GetPolicies(policycache.VerifyImages, kind, request.Namespace)
|
||||
|
||||
if len(mutatePolicies) == 0 && len(generatePolicies) == 0 && len(verifyImagesPolicies) == 0 {
|
||||
if len(mutatePolicies) == 0 && len(verifyImagesPolicies) == 0 {
|
||||
logger.V(4).Info("no policies matched admission request")
|
||||
if request.Operation == v1beta1.Update {
|
||||
// handle generate source resource updates
|
||||
go ws.handleUpdatesForGenerateRules(request, []*v1.ClusterPolicy{})
|
||||
}
|
||||
|
||||
return successResponse(nil)
|
||||
}
|
||||
|
||||
addRoles := containsRBACInfo(mutatePolicies, generatePolicies)
|
||||
addRoles := containsRBACInfo(mutatePolicies)
|
||||
policyContext, err := ws.buildPolicyContext(request, addRoles)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to build policy context")
|
||||
|
@ -334,9 +328,6 @@ func (ws *WebhookServer) resourceMutation(request *v1beta1.AdmissionRequest) *v1
|
|||
return failureResponse(err.Error())
|
||||
}
|
||||
|
||||
newRequest = patchRequest(imagePatches, newRequest, logger)
|
||||
ws.applyGeneratePolicies(newRequest, policyContext, generatePolicies, requestTime, logger)
|
||||
|
||||
var patches = append(mutatePatches, imagePatches...)
|
||||
return successResponse(patches)
|
||||
}
|
||||
|
@ -355,11 +346,10 @@ func (ws *WebhookServer) buildPolicyContext(request *v1beta1.AdmissionRequest, a
|
|||
}
|
||||
|
||||
if addRoles {
|
||||
if roles, clusterRoles, err := userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request, ws.configHandler); err != nil {
|
||||
var err error
|
||||
userRequestInfo.Roles, userRequestInfo.ClusterRoles, err = userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request, ws.configHandler)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to fetch RBAC information for request")
|
||||
} else {
|
||||
userRequestInfo.Roles = roles
|
||||
userRequestInfo.ClusterRoles = clusterRoles
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -488,6 +478,7 @@ func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *
|
|||
if request.Operation == v1beta1.Delete {
|
||||
ws.handleDelete(request)
|
||||
}
|
||||
|
||||
if excludeKyvernoResources(request.Kind.Kind) {
|
||||
return successResponse(nil)
|
||||
}
|
||||
|
@ -500,9 +491,15 @@ func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *
|
|||
// Get namespace policies from the cache for the requested resource namespace
|
||||
nsPolicies := ws.pCache.GetPolicies(policycache.ValidateEnforce, kind, request.Namespace)
|
||||
policies = append(policies, nsPolicies...)
|
||||
generatePolicies := ws.pCache.GetPolicies(policycache.Generate, kind, request.Namespace)
|
||||
|
||||
if len(generatePolicies) == 0 && request.Operation == v1beta1.Update {
|
||||
// handle generate source resource updates
|
||||
go ws.handleUpdatesForGenerateRules(request, []*v1.ClusterPolicy{})
|
||||
}
|
||||
|
||||
var roles, clusterRoles []string
|
||||
if containsRBACInfo(policies) {
|
||||
if containsRBACInfo(policies, generatePolicies) {
|
||||
var err error
|
||||
roles, clusterRoles, err = userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request, ws.configHandler)
|
||||
if err != nil {
|
||||
|
@ -561,6 +558,9 @@ func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *
|
|||
// push admission request to audit handler, this won't block the admission request
|
||||
ws.auditHandler.Add(request.DeepCopy())
|
||||
|
||||
// process generate policies
|
||||
ws.applyGeneratePolicies(request, policyContext, generatePolicies, admissionRequestTimestamp, logger)
|
||||
|
||||
return successResponse(nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
"github.com/kyverno/kyverno/pkg/utils"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
"github.com/kyverno/kyverno/pkg/utils"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/common"
|
||||
|
|
|
@ -2,6 +2,7 @@ package webhooks
|
|||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/engine"
|
||||
|
|
Loading…
Reference in a new issue