mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-15 20:20:22 +00:00
fix: return gvk when loading resource (#8501)
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
3c76cf5118
commit
bc6b6e17b9
5 changed files with 34 additions and 163 deletions
|
@ -14,27 +14,26 @@ import (
|
||||||
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
|
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/data"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/data"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/experimental"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/experimental"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/convert"
|
||||||
|
resourceloader "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/loader"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/source"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/source"
|
||||||
"github.com/kyverno/kyverno/pkg/utils/git"
|
"github.com/kyverno/kyverno/pkg/utils/git"
|
||||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||||
"k8s.io/api/admissionregistration/v1alpha1"
|
"k8s.io/api/admissionregistration/v1alpha1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"sigs.k8s.io/kubectl-validate/pkg/openapiclient"
|
"sigs.k8s.io/kubectl-validate/pkg/openapiclient"
|
||||||
"sigs.k8s.io/kubectl-validate/pkg/validator"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
factory, _ = validator.New(client)
|
factory, _ = resourceloader.New(openapiclient.NewComposite(
|
||||||
policyV1 = schema.GroupVersion(kyvernov1.GroupVersion).WithKind("Policy")
|
|
||||||
policyV2 = schema.GroupVersion(kyvernov2beta1.GroupVersion).WithKind("Policy")
|
|
||||||
clusterPolicyV1 = schema.GroupVersion(kyvernov1.GroupVersion).WithKind("ClusterPolicy")
|
|
||||||
clusterPolicyV2 = schema.GroupVersion(kyvernov2beta1.GroupVersion).WithKind("ClusterPolicy")
|
|
||||||
vapV1 = v1alpha1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicy")
|
|
||||||
client = openapiclient.NewComposite(
|
|
||||||
openapiclient.NewHardcodedBuiltins("1.28"),
|
openapiclient.NewHardcodedBuiltins("1.28"),
|
||||||
openapiclient.NewLocalCRDFiles(data.Crds(), data.CrdsFolder),
|
openapiclient.NewLocalCRDFiles(data.Crds(), data.CrdsFolder),
|
||||||
)
|
))
|
||||||
|
policyV1 = schema.GroupVersion(kyvernov1.GroupVersion).WithKind("Policy")
|
||||||
|
policyV2 = schema.GroupVersion(kyvernov2beta1.GroupVersion).WithKind("Policy")
|
||||||
|
clusterPolicyV1 = schema.GroupVersion(kyvernov1.GroupVersion).WithKind("ClusterPolicy")
|
||||||
|
clusterPolicyV2 = schema.GroupVersion(kyvernov2beta1.GroupVersion).WithKind("ClusterPolicy")
|
||||||
|
vapV1Alpha1 = v1alpha1.SchemeGroupVersion.WithKind("ValidatingAdmissionPolicy")
|
||||||
LegacyLoader = yamlutils.GetPolicy
|
LegacyLoader = yamlutils.GetPolicy
|
||||||
KubectlValidateLoader = kubectlValidateLoader
|
KubectlValidateLoader = kubectlValidateLoader
|
||||||
defaultLoader = func(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
defaultLoader = func(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
||||||
|
@ -92,46 +91,42 @@ func LoadWithLoader(loader loader, fs billy.Filesystem, resourcePath string, pat
|
||||||
return pols, vaps, nil
|
return pols, vaps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func kubectlValidateLoader(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
func kubectlValidateLoader(content []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
||||||
var policies []kyvernov1.PolicyInterface
|
documents, err := yamlutils.SplitDocuments(content)
|
||||||
var validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy
|
|
||||||
documents, err := yamlutils.SplitDocuments(bytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
var policies []kyvernov1.PolicyInterface
|
||||||
|
var vaps []v1alpha1.ValidatingAdmissionPolicy
|
||||||
for _, document := range documents {
|
for _, document := range documents {
|
||||||
gvk, untyped, err := factory.Parse(document)
|
gvk, untyped, err := factory.Load(document)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// TODO remove DeepCopy when fixed upstream
|
|
||||||
if err := factory.Validate(untyped.DeepCopy()); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
switch gvk {
|
switch gvk {
|
||||||
case policyV1, policyV2:
|
case policyV1, policyV2:
|
||||||
var policy kyvernov1.Policy
|
typed, err := convert.To[kyvernov1.Policy](untyped)
|
||||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(untyped.UnstructuredContent(), &policy, true); err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
policies = append(policies, &policy)
|
policies = append(policies, typed)
|
||||||
case clusterPolicyV1, clusterPolicyV2:
|
case clusterPolicyV1, clusterPolicyV2:
|
||||||
var policy kyvernov1.ClusterPolicy
|
typed, err := convert.To[kyvernov1.ClusterPolicy](untyped)
|
||||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(untyped.UnstructuredContent(), &policy, true); err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
policies = append(policies, &policy)
|
policies = append(policies, typed)
|
||||||
case vapV1:
|
case vapV1Alpha1:
|
||||||
var policy v1alpha1.ValidatingAdmissionPolicy
|
typed, err := convert.To[v1alpha1.ValidatingAdmissionPolicy](untyped)
|
||||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(untyped.UnstructuredContent(), &policy, true); err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
validatingAdmissionPolicies = append(validatingAdmissionPolicies, policy)
|
vaps = append(vaps, *typed)
|
||||||
default:
|
default:
|
||||||
return nil, nil, fmt.Errorf("policy type not supported %s", gvk)
|
return nil, nil, fmt.Errorf("policy type not supported %s", gvk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return policies, validatingAdmissionPolicies, nil
|
return policies, vaps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fsLoad(loader loader, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
func fsLoad(loader loader, path string) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
||||||
|
|
|
@ -3,12 +3,10 @@ package resource
|
||||||
import (
|
import (
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/convert"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/convert"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/loader"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/loader"
|
||||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Load[T any](l loader.Loader, content []byte) (*T, error) {
|
func Load[T any](l loader.Loader, content []byte) (*T, error) {
|
||||||
untyped, err := l.Load(content)
|
_, untyped, err := l.Load(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -18,19 +16,3 @@ func Load[T any](l loader.Loader, content []byte) (*T, error) {
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadResources(l loader.Loader, content []byte) ([]unstructured.Unstructured, error) {
|
|
||||||
documents, err := yamlutils.SplitDocuments(content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var resources []unstructured.Unstructured
|
|
||||||
for _, document := range documents {
|
|
||||||
untyped, err := l.Load(document)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resources = append(resources, untyped)
|
|
||||||
}
|
|
||||||
return resources, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
package resource
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/loader"
|
|
||||||
"sigs.k8s.io/kubectl-validate/pkg/openapiclient"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
singleResource string = `apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: prod-bus-app1
|
|
||||||
labels:
|
|
||||||
purpose: production`
|
|
||||||
|
|
||||||
multipleResources string = `
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Pod
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
run: nginx
|
|
||||||
name: nginx
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- image: nginx
|
|
||||||
name: nginx
|
|
||||||
resources: {}
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Pod
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
run: redis
|
|
||||||
name: redis
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- image: redis
|
|
||||||
name: redis
|
|
||||||
resources: {}`
|
|
||||||
|
|
||||||
resourceWithComment string = `
|
|
||||||
### POD ###
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Pod
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
run: nginx
|
|
||||||
name: nginx
|
|
||||||
namespace: default
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- image: nginx
|
|
||||||
name: nginx
|
|
||||||
resources: {}`
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_LoadResources(t *testing.T) {
|
|
||||||
l, err := loader.New(openapiclient.NewHardcodedBuiltins("1.27"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
resources string
|
|
||||||
wantLoaded int
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "load no resource with empy string",
|
|
||||||
resources: "",
|
|
||||||
wantLoaded: 0,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "load single resource",
|
|
||||||
resources: singleResource,
|
|
||||||
wantLoaded: 1,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "load multiple resources",
|
|
||||||
resources: multipleResources,
|
|
||||||
wantLoaded: 2,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "load resource with comment",
|
|
||||||
resources: resourceWithComment,
|
|
||||||
wantLoaded: 1,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if res, err := LoadResources(l, []byte(tt.resources)); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("loader.Resources() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
} else if len(res) != tt.wantLoaded {
|
|
||||||
t.Errorf("loader.Resources() loaded amount = %v, wantLoaded %v", len(res), tt.wantLoaded)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,12 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/openapi"
|
"k8s.io/client-go/openapi"
|
||||||
"sigs.k8s.io/kubectl-validate/pkg/validator"
|
"sigs.k8s.io/kubectl-validate/pkg/validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Loader interface {
|
type Loader interface {
|
||||||
Load([]byte) (unstructured.Unstructured, error)
|
Load([]byte) (schema.GroupVersionKind, unstructured.Unstructured, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type loader struct {
|
type loader struct {
|
||||||
|
@ -26,14 +27,14 @@ func New(client openapi.Client) (Loader, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *loader) Load(document []byte) (unstructured.Unstructured, error) {
|
func (l *loader) Load(document []byte) (schema.GroupVersionKind, unstructured.Unstructured, error) {
|
||||||
_, result, err := l.validator.Parse(document)
|
gvk, result, err := l.validator.Parse(document)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return unstructured.Unstructured{}, fmt.Errorf("failed to parse document (%w)", err)
|
return gvk, unstructured.Unstructured{}, fmt.Errorf("failed to parse document (%w)", err)
|
||||||
}
|
}
|
||||||
// TODO: remove DeepCopy when fixed upstream
|
// TODO: remove DeepCopy when fixed upstream
|
||||||
if err := l.validator.Validate(result.DeepCopy()); err != nil {
|
if err := l.validator.Validate(result.DeepCopy()); err != nil {
|
||||||
return unstructured.Unstructured{}, fmt.Errorf("failed to validate resource (%w)", err)
|
return gvk, unstructured.Unstructured{}, fmt.Errorf("failed to validate resource (%w)", err)
|
||||||
}
|
}
|
||||||
return *result, nil
|
return gvk, *result, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,7 +163,7 @@ func Test_loader_Load(t *testing.T) {
|
||||||
}}
|
}}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := tt.loader.Load(tt.document)
|
_, got, err := tt.loader.Load(tt.document)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("loader.Load() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("loader.Load() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
|
|
Loading…
Add table
Reference in a new issue