mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +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"
|
||||
"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/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/pkg/utils/git"
|
||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/kubectl-validate/pkg/openapiclient"
|
||||
"sigs.k8s.io/kubectl-validate/pkg/validator"
|
||||
)
|
||||
|
||||
var (
|
||||
factory, _ = validator.New(client)
|
||||
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(
|
||||
factory, _ = resourceloader.New(openapiclient.NewComposite(
|
||||
openapiclient.NewHardcodedBuiltins("1.28"),
|
||||
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
|
||||
KubectlValidateLoader = kubectlValidateLoader
|
||||
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
|
||||
}
|
||||
|
||||
func kubectlValidateLoader(bytes []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
||||
var policies []kyvernov1.PolicyInterface
|
||||
var validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy
|
||||
documents, err := yamlutils.SplitDocuments(bytes)
|
||||
func kubectlValidateLoader(content []byte) ([]kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, error) {
|
||||
documents, err := yamlutils.SplitDocuments(content)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var policies []kyvernov1.PolicyInterface
|
||||
var vaps []v1alpha1.ValidatingAdmissionPolicy
|
||||
for _, document := range documents {
|
||||
gvk, untyped, err := factory.Parse(document)
|
||||
gvk, untyped, err := factory.Load(document)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// TODO remove DeepCopy when fixed upstream
|
||||
if err := factory.Validate(untyped.DeepCopy()); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
switch gvk {
|
||||
case policyV1, policyV2:
|
||||
var policy kyvernov1.Policy
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(untyped.UnstructuredContent(), &policy, true); err != nil {
|
||||
typed, err := convert.To[kyvernov1.Policy](untyped)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
policies = append(policies, &policy)
|
||||
policies = append(policies, typed)
|
||||
case clusterPolicyV1, clusterPolicyV2:
|
||||
var policy kyvernov1.ClusterPolicy
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(untyped.UnstructuredContent(), &policy, true); err != nil {
|
||||
typed, err := convert.To[kyvernov1.ClusterPolicy](untyped)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
policies = append(policies, &policy)
|
||||
case vapV1:
|
||||
var policy v1alpha1.ValidatingAdmissionPolicy
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(untyped.UnstructuredContent(), &policy, true); err != nil {
|
||||
policies = append(policies, typed)
|
||||
case vapV1Alpha1:
|
||||
typed, err := convert.To[v1alpha1.ValidatingAdmissionPolicy](untyped)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
validatingAdmissionPolicies = append(validatingAdmissionPolicies, policy)
|
||||
vaps = append(vaps, *typed)
|
||||
default:
|
||||
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) {
|
||||
|
|
|
@ -3,12 +3,10 @@ package resource
|
|||
import (
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource/convert"
|
||||
"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) {
|
||||
untyped, err := l.Load(content)
|
||||
_, untyped, err := l.Load(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -18,19 +16,3 @@ func Load[T any](l loader.Loader, content []byte) (*T, error) {
|
|||
}
|
||||
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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/openapi"
|
||||
"sigs.k8s.io/kubectl-validate/pkg/validator"
|
||||
)
|
||||
|
||||
type Loader interface {
|
||||
Load([]byte) (unstructured.Unstructured, error)
|
||||
Load([]byte) (schema.GroupVersionKind, unstructured.Unstructured, error)
|
||||
}
|
||||
|
||||
type loader struct {
|
||||
|
@ -26,14 +27,14 @@ func New(client openapi.Client) (Loader, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (l *loader) Load(document []byte) (unstructured.Unstructured, error) {
|
||||
_, result, err := l.validator.Parse(document)
|
||||
func (l *loader) Load(document []byte) (schema.GroupVersionKind, unstructured.Unstructured, error) {
|
||||
gvk, result, err := l.validator.Parse(document)
|
||||
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
|
||||
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 {
|
||||
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 {
|
||||
t.Errorf("loader.Load() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
|
Loading…
Reference in a new issue