1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-15 17:51:03 +00:00
kube-arangodb/internal/cr_validation_test.go

402 lines
11 KiB
Go
Raw Normal View History

//
// DISCLAIMER
//
// Copyright 2023-2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
package internal
import (
"fmt"
"go/token"
"os"
"path"
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/require"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/yaml"
analyticsApi "github.com/arangodb/kube-arangodb/pkg/apis/analytics/v1alpha1"
appsv1 "github.com/arangodb/kube-arangodb/pkg/apis/apps/v1"
backupv1 "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
deploymentv1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
deploymentv2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v2alpha1"
mlApiv1alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/ml/v1alpha1"
mlApi "github.com/arangodb/kube-arangodb/pkg/apis/ml/v1beta1"
2024-07-26 08:32:36 +00:00
networkingApi "github.com/arangodb/kube-arangodb/pkg/apis/networking/v1alpha1"
replicationv1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v1"
replicationv2alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/replication/v2alpha1"
schedulerApiv1alpha1 "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1"
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1beta1"
storagev1alpha "github.com/arangodb/kube-arangodb/pkg/apis/storage/v1alpha"
)
func (def DocDefinition) ApplyToSchema(s *apiextensions.JSONSchemaProps) {
for _, e := range def.Enum {
z := strings.Split(e, "|")
s.Enum = append(s.Enum, apiextensions.JSON{
Raw: []byte("\"" + z[0] + "\""),
})
}
s.Description = strings.Join(def.Docs, "\n")
}
// Test_GenerateCRValidationSchemas generates validation schema JSONs for each CRD referenced in `input` (see impl)
func Test_GenerateCRValidationSchemas(t *testing.T) {
root := os.Getenv("ROOT")
require.NotEmpty(t, root)
type genSpec struct {
objects map[string]interface{}
}
fset := token.NewFileSet()
sharedFields := parseSourceFiles(t, root, fset, fmt.Sprintf("%s/pkg/apis/shared/v1", root))
// CR file prefix -> packages to parse -> versions -> obj
input := map[string]map[string]map[string]genSpec{
"apps-job": {
fmt.Sprintf("%s/pkg/apis/apps/v1", root): {
"v1": {
objects: map[string]interface{}{
"spec": appsv1.ArangoJob{}.Spec,
},
},
},
},
"backups-backup": {
fmt.Sprintf("%s/pkg/apis/backup/v1", root): {
"v1": {
objects: map[string]interface{}{
"spec": backupv1.ArangoBackup{}.Spec,
},
},
"v1alpha": {
objects: map[string]interface{}{
"spec": backupv1.ArangoBackup{}.Spec,
},
},
},
},
"backups-backuppolicy": {
fmt.Sprintf("%s/pkg/apis/backup/v1", root): {
"v1": {
objects: map[string]interface{}{
"spec": backupv1.ArangoBackupPolicy{}.Spec,
},
},
"v1alpha": {
objects: map[string]interface{}{
"spec": backupv1.ArangoBackupPolicy{}.Spec,
},
},
},
},
"database-deployment": {
fmt.Sprintf("%s/pkg/apis/deployment/v1", root): {
"v1": {
objects: map[string]interface{}{
"spec": deploymentv1.ArangoDeployment{}.Spec,
},
},
"v1alpha": {
objects: map[string]interface{}{
"spec": deploymentv1.ArangoDeployment{}.Spec,
},
},
},
fmt.Sprintf("%s/pkg/apis/deployment/v2alpha1", root): {
"v2alpha1": {
objects: map[string]interface{}{
"spec": deploymentv2alpha1.ArangoDeployment{}.Spec,
},
},
},
},
"database-member": {
fmt.Sprintf("%s/pkg/apis/deployment/v1", root): {
"v1": {
objects: map[string]interface{}{
"spec": deploymentv1.ArangoMember{}.Spec,
},
},
},
fmt.Sprintf("%s/pkg/apis/deployment/v2alpha1", root): {
"v2alpha1": {
objects: map[string]interface{}{
"spec": deploymentv2alpha1.ArangoMember{}.Spec,
},
},
},
},
"database-clustersynchronization": {
fmt.Sprintf("%s/pkg/apis/deployment/v1", root): {
"v1": {
objects: map[string]interface{}{
"spec": deploymentv1.ArangoClusterSynchronization{}.Spec,
},
},
},
fmt.Sprintf("%s/pkg/apis/deployment/v2alpha1", root): {
"v2alpha1": {
objects: map[string]interface{}{
"spec": deploymentv2alpha1.ArangoClusterSynchronization{}.Spec,
},
},
},
},
"database-task": {
fmt.Sprintf("%s/pkg/apis/deployment/v1", root): {
"v1": {
objects: map[string]interface{}{
"spec": deploymentv1.ArangoTask{}.Spec,
},
},
"v1alpha": {
objects: map[string]interface{}{
"spec": deploymentv1.ArangoTask{}.Spec,
},
},
},
fmt.Sprintf("%s/pkg/apis/deployment/v2alpha1", root): {
"v2alpha1": {
objects: map[string]interface{}{
"spec": deploymentv2alpha1.ArangoTask{}.Spec,
},
},
},
},
"replication-deploymentreplication": {
fmt.Sprintf("%s/pkg/apis/replication/v1", root): {
"v1": {
objects: map[string]interface{}{
"spec": replicationv1.ArangoDeploymentReplication{}.Spec,
},
},
"v1alpha": {
objects: map[string]interface{}{
"spec": replicationv1.ArangoDeploymentReplication{}.Spec,
},
},
},
fmt.Sprintf("%s/pkg/apis/replication/v2alpha1", root): {
"v2alpha1": {
objects: map[string]interface{}{
"spec": replicationv2alpha1.ArangoDeploymentReplication{}.Spec,
},
},
},
},
"storage-localstorage": {
fmt.Sprintf("%s/pkg/apis/storage/v1alpha", root): {
"v1alpha": {
objects: map[string]interface{}{
"spec": storagev1alpha.ArangoLocalStorage{}.Spec,
},
},
},
},
"scheduler-profile": {
fmt.Sprintf("%s/pkg/apis/scheduler/v1alpha1", root): {
"v1alpha1": {
objects: map[string]interface{}{
"spec": schedulerApiv1alpha1.ArangoProfile{}.Spec,
},
},
"v1beta1": {
objects: map[string]interface{}{
"spec": schedulerApi.ArangoProfile{}.Spec,
},
},
},
},
2024-09-26 15:20:57 +00:00
"scheduler-pod": {
fmt.Sprintf("%s/pkg/apis/scheduler/v1alpha1", root): {
"v1beta1": {
objects: map[string]interface{}{
"spec": schedulerApi.ArangoSchedulerPod{}.Spec,
},
},
},
},
"scheduler-deployment": {
fmt.Sprintf("%s/pkg/apis/scheduler/v1alpha1", root): {
"v1beta1": {
objects: map[string]interface{}{
"spec": schedulerApi.ArangoSchedulerDeployment{}.Spec,
},
},
},
},
"scheduler-batchjob": {
fmt.Sprintf("%s/pkg/apis/scheduler/v1alpha1", root): {
"v1beta1": {
objects: map[string]interface{}{
"spec": schedulerApi.ArangoSchedulerBatchJob{}.Spec,
},
},
},
},
"scheduler-cronjob": {
fmt.Sprintf("%s/pkg/apis/scheduler/v1alpha1", root): {
"v1beta1": {
objects: map[string]interface{}{
"spec": schedulerApi.ArangoSchedulerCronJob{}.Spec,
},
},
},
},
"ml-extension": {
fmt.Sprintf("%s/pkg/apis/ml/v1alpha1", root): {
"v1alpha1": {
objects: map[string]interface{}{
"spec": mlApiv1alpha1.ArangoMLExtension{}.Spec,
},
},
"v1beta1": {
objects: map[string]interface{}{
"spec": mlApi.ArangoMLExtension{}.Spec,
},
},
},
},
"ml-storage": {
fmt.Sprintf("%s/pkg/apis/ml/v1alpha1", root): {
"v1alpha1": {
objects: map[string]interface{}{
"spec": mlApiv1alpha1.ArangoMLStorage{}.Spec,
},
},
"v1beta1": {
objects: map[string]interface{}{
"spec": mlApi.ArangoMLStorage{}.Spec,
},
},
},
},
"ml-job-cron": {
fmt.Sprintf("%s/pkg/apis/ml/v1alpha1", root): {
"v1alpha1": {
objects: map[string]interface{}{
"spec": mlApiv1alpha1.ArangoMLCronJob{}.Spec,
},
},
},
},
"ml-job-batch": {
fmt.Sprintf("%s/pkg/apis/ml/v1alpha1", root): {
"v1alpha1": {
objects: map[string]interface{}{
"spec": mlApiv1alpha1.ArangoMLBatchJob{}.Spec,
},
},
},
},
"analytics-graphanalyticsengine": {
fmt.Sprintf("%s/pkg/apis/analytics/v1alpha1", root): {
"v1alpha1": {
objects: map[string]interface{}{
"spec": analyticsApi.GraphAnalyticsEngine{}.Spec,
},
},
},
},
2024-07-26 08:32:36 +00:00
"networking-route": {
fmt.Sprintf("%s/pkg/apis/networking/v1alpha1", root): {
"v1alpha1": {
objects: map[string]interface{}{
"spec": networkingApi.ArangoRoute{}.Spec,
},
},
},
},
}
for filePrefix, packagesToVersion := range input {
t.Run(filePrefix, func(t *testing.T) {
// Preload Definition
data, err := os.ReadFile(fmt.Sprintf("%s/pkg/crd/crds/%s.yaml", root, filePrefix))
require.NoError(t, err)
var crd apiextensions.CustomResourceDefinition
require.NoError(t, yaml.Unmarshal(data, &crd))
validationPerVersion := make(map[string]apiextensions.CustomResourceValidation, len(packagesToVersion))
for apiDir, versionMap := range packagesToVersion {
fields := parseSourceFiles(t, root, fset, apiDir)
for n, f := range sharedFields {
require.NotContains(t, fields, n)
fields[n] = f
}
for version, generationSpec := range versionMap {
crdVersion := findCRDVersion(t, crd, version)
t.Log(crdVersion.Schema)
if _, ok := generationSpec.objects["status"]; !ok {
generationSpec.objects["status"] = allowAnyType{}
}
sb := newSchemaBuilder(root, fields, fset)
objects := map[string]apiextensions.JSONSchemaProps{}
for k, obj := range generationSpec.objects {
s := sb.TypeToSchema(t, reflect.TypeOf(obj), fmt.Sprintf(".%s", k))
require.NotNil(t, s, version)
objects[k] = *s
}
validationPerVersion[version] = apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: objects,
},
}
}
}
yamlRaw, err := yaml.Marshal(validationPerVersion)
require.NoError(t, err)
outPath := path.Join(root, "pkg/crd/crds", fmt.Sprintf("%s.schema.generated.yaml", filePrefix))
err = os.WriteFile(outPath, yamlRaw, 0644)
require.NoError(t, err)
})
}
}
func findCRDVersion(t *testing.T, crd apiextensions.CustomResourceDefinition, version string) apiextensions.CustomResourceDefinitionVersion {
for _, v := range crd.Spec.Versions {
if v.Name == version {
return v
}
}
require.FailNowf(t, "Unable to find version", "Trying to find %s/%s", crd.GetName(), version)
return apiextensions.CustomResourceDefinitionVersion{}
}