Repository: kyverno/chainsaw
Pull request #362 (2023-11-03 15:04:26)

chore: improve action workflow
Improve action workflow.


Patch:
diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml
index a8aec825..73c002a3 100644
--- a/.github/workflows/action.yml
+++ b/.github/workflows/action.yml
@@ -208,7 +208,7 @@ jobs:
     needs:
     - default
     - verify
-    # - release
+    - release
     - custom
     - wrong_version
     - custom_dir
diff --git a/pkg/config/load.go b/pkg/config/load.go
index 1f81b3c2..74576254 100644
--- a/pkg/config/load.go
+++ b/pkg/config/load.go
@@ -11,6 +11,8 @@ import (
 	"github.com/kyverno/kyverno/ext/resource/convert"
 	"github.com/kyverno/kyverno/ext/resource/loader"
 	"github.com/kyverno/kyverno/ext/yaml"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/client-go/openapi"
 	"sigs.k8s.io/kubectl-validate/pkg/openapiclient"
 )
 
@@ -18,6 +20,12 @@ const (
 	DefaultFileName = ".chainsaw.yaml"
 )
 
+type (
+	splitter      = func([]byte) ([][]byte, error)
+	loaderFactory = func(openapi.Client) (loader.Loader, error)
+	converter     = func(unstructured.Unstructured) (*v1alpha1.Configuration, error)
+)
+
 var configuration_v1alpha1 = v1alpha1.SchemeGroupVersion.WithKind("Configuration")
 
 func Load(path string) (*v1alpha1.Configuration, error) {
@@ -47,13 +55,26 @@ func LoadBytes(content []byte) (*v1alpha1.Configuration, error) {
 }
 
 func Parse(content []byte) ([]*v1alpha1.Configuration, error) {
-	documents, err := yaml.SplitDocuments(content)
+	return parse(content, nil, nil, nil)
+}
+
+func parse(content []byte, splitter splitter, loaderFactory loaderFactory, converter converter) ([]*v1alpha1.Configuration, error) {
+	if splitter == nil {
+		splitter = yaml.SplitDocuments
+	}
+	if loaderFactory == nil {
+		loaderFactory = loader.New
+	}
+	if converter == nil {
+		converter = convert.To[v1alpha1.Configuration]
+	}
+	documents, err := splitter(content)
 	if err != nil {
 		return nil, err
 	}
 	var policies []*v1alpha1.Configuration
 	// TODO: no need to allocate a validator every time
-	loader, err := loader.New(openapiclient.NewLocalCRDFiles(data.Crds(), data.CrdsFolder))
+	loader, err := loaderFactory(openapiclient.NewLocalCRDFiles(data.Crds(), data.CrdsFolder))
 	if err != nil {
 		return nil, err
 	}
@@ -64,7 +85,7 @@ func Parse(content []byte) ([]*v1alpha1.Configuration, error) {
 		}
 		switch gvk {
 		case configuration_v1alpha1:
-			policy, err := convert.To[v1alpha1.Configuration](untyped)
+			policy, err := converter(untyped)
 			if err != nil {
 				return nil, err
 			}


Test patch:
diff --git a/pkg/config/load_test.go b/pkg/config/load_test.go
index e386cacd..a885e364 100644
--- a/pkg/config/load_test.go
+++ b/pkg/config/load_test.go
@@ -1,12 +1,19 @@
 package config
 
 import (
+	"errors"
+	"os"
 	"reflect"
 	"testing"
 	"time"
 
 	"github.com/kyverno/chainsaw/pkg/apis/v1alpha1"
+	"github.com/kyverno/kyverno/ext/resource/loader"
+	"github.com/stretchr/testify/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/client-go/openapi"
 	"k8s.io/utils/ptr"
 )
 
@@ -16,81 +23,74 @@ func TestLoad(t *testing.T) {
 		path    string
 		want    *v1alpha1.Configuration
 		wantErr bool
-	}{
-		{
-			name:    "confimap",
-			path:    "../../testdata/config/configmap.yaml",
-			wantErr: true,
-		},
-		{
-			name:    "not found",
-			path:    "../../testdata/config/not-found.yaml",
-			wantErr: true,
-		},
-		{
-			name:    "empty",
-			path:    "../../testdata/config/empty.yaml",
-			wantErr: true,
-		},
-		{
-			name: "default",
-			path: "../../testdata/config/default.yaml",
-			want: &v1alpha1.Configuration{
-				TypeMeta: metav1.TypeMeta{
-					APIVersion: "chainsaw.kyverno.io/v1alpha1",
-					Kind:       "Configuration",
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "default",
-				},
-				Spec: v1alpha1.ConfigurationSpec{
-					SkipDelete:       false,
-					FailFast:         false,
-					ReportFormat:     "",
-					ReportName:       "chainsaw-report",
-					FullName:         false,
-					IncludeTestRegex: "",
-					ExcludeTestRegex: "",
-				},
+	}{{
+		name:    "confimap",
+		path:    "../../testdata/config/configmap.yaml",
+		wantErr: true,
+	}, {
+		name:    "not found",
+		path:    "../../testdata/config/not-found.yaml",
+		wantErr: true,
+	}, {
+		name:    "empty",
+		path:    "../../testdata/config/empty.yaml",
+		wantErr: true,
+	}, {
+		name: "default",
+		path: "../../testdata/config/default.yaml",
+		want: &v1alpha1.Configuration{
+			TypeMeta: metav1.TypeMeta{
+				APIVersion: "chainsaw.kyverno.io/v1alpha1",
+				Kind:       "Configuration",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "default",
+			},
+			Spec: v1alpha1.ConfigurationSpec{
+				SkipDelete:       false,
+				FailFast:         false,
+				ReportFormat:     "",
+				ReportName:       "chainsaw-report",
+				FullName:         false,
+				IncludeTestRegex: "",
+				ExcludeTestRegex: "",
 			},
 		},
-		{
-			name: "custom-config",
-			path: "../../testdata/config/custom-config.yaml",
-			want: &v1alpha1.Configuration{
-				TypeMeta: metav1.TypeMeta{
-					APIVersion: "chainsaw.kyverno.io/v1alpha1",
-					Kind:       "Configuration",
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Name: "custom-config",
-				},
-				Spec: v1alpha1.ConfigurationSpec{
-					Timeouts: v1alpha1.Timeouts{
-						Apply:   &metav1.Duration{Duration: 5 * time.Second},
-						Assert:  &metav1.Duration{Duration: 10 * time.Second},
-						Error:   &metav1.Duration{Duration: 10 * time.Second},
-						Delete:  &metav1.Duration{Duration: 5 * time.Second},
-						Cleanup: &metav1.Duration{Duration: 5 * time.Second},
-						Exec:    &metav1.Duration{Duration: 10 * time.Second},
-					},
-					SkipDelete:       true,
-					FailFast:         true,
-					Parallel:         ptr.To(4),
-					ReportFormat:     "JSON",
-					ReportName:       "custom-report",
-					FullName:         true,
-					IncludeTestRegex: "include-*",
-					ExcludeTestRegex: "exclude-*",
+	}, {
+		name: "custom-config",
+		path: "../../testdata/config/custom-config.yaml",
+		want: &v1alpha1.Configuration{
+			TypeMeta: metav1.TypeMeta{
+				APIVersion: "chainsaw.kyverno.io/v1alpha1",
+				Kind:       "Configuration",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "custom-config",
+			},
+			Spec: v1alpha1.ConfigurationSpec{
+				Timeouts: v1alpha1.Timeouts{
+					Apply:   &metav1.Duration{Duration: 5 * time.Second},
+					Assert:  &metav1.Duration{Duration: 10 * time.Second},
+					Error:   &metav1.Duration{Duration: 10 * time.Second},
+					Delete:  &metav1.Duration{Duration: 5 * time.Second},
+					Cleanup: &metav1.Duration{Duration: 5 * time.Second},
+					Exec:    &metav1.Duration{Duration: 10 * time.Second},
 				},
+				SkipDelete:       true,
+				FailFast:         true,
+				Parallel:         ptr.To(4),
+				ReportFormat:     "JSON",
+				ReportName:       "custom-report",
+				FullName:         true,
+				IncludeTestRegex: "include-*",
+				ExcludeTestRegex: "exclude-*",
 			},
 		},
-		{
-			name:    "multiple",
-			path:    "../../testdata/config/multiple.yaml",
-			wantErr: true,
-		},
-	}
+	}, {
+		name:    "multiple",
+		path:    "../../testdata/config/multiple.yaml",
+		wantErr: true,
+	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			got, err := Load(tt.path)
@@ -104,3 +104,69 @@ func TestLoad(t *testing.T) {
 		})
 	}
 }
+
+type fakeLoader struct{}
+
+func (fakeLoader) Load([]byte) (schema.GroupVersionKind, unstructured.Unstructured, error) {
+	return schema.GroupVersionKind{Group: "v1", Kind: "Something"}, unstructured.Unstructured{}, nil
+}
+
+func Test_parse(t *testing.T) {
+	content, err := os.ReadFile("../../testdata/config/custom-config.yaml")
+	assert.NoError(t, err)
+	tests := []struct {
+		name          string
+		splitter      splitter
+		loaderFactory loaderFactory
+		converter     converter
+		wantErr       bool
+	}{{
+		name:          "default",
+		splitter:      nil,
+		loaderFactory: nil,
+		converter:     nil,
+		wantErr:       false,
+	}, {
+		name: "splitter error",
+		splitter: func([]byte) ([][]byte, error) {
+			return nil, errors.New("splitter")
+		},
+		loaderFactory: nil,
+		converter:     nil,
+		wantErr:       true,
+	}, {
+		name:     "loader factory error",
+		splitter: nil,
+		loaderFactory: func(openapi.Client) (loader.Loader, error) {
+			return nil, errors.New("loader factory")
+		},
+		converter: nil,
+		wantErr:   true,
+	}, {
+		name:     "loader error",
+		splitter: nil,
+		loaderFactory: func(openapi.Client) (loader.Loader, error) {
+			return fakeLoader{}, nil
+		},
+		converter: nil,
+		wantErr:   true,
+	}, {
+		name:          "converter error",
+		splitter:      nil,
+		loaderFactory: nil,
+		converter: func(unstructured.Unstructured) (*v1alpha1.Configuration, error) {
+			return nil, errors.New("converter")
+		},
+		wantErr: true,
+	}}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			_, err := parse(content, tt.splitter, tt.loaderFactory, tt.converter)
+			if tt.wantErr {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+			}
+		})
+	}
+}