Skip to content

Commit

Permalink
Validate parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
feloy committed Sep 15, 2021
1 parent 44c35d5 commit 3ca2cd7
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 488 deletions.
52 changes: 52 additions & 0 deletions pkg/kclient/operators.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/go-openapi/spec"
"github.com/openshift/odo/pkg/log"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -210,3 +211,54 @@ loopDefinitions:
}
return nil, nil
}

// GetCRDSpec returns the specs of a resource in an openAPIv2 format
func (c *Client) GetCRDSpec(cr *olm.CRDDescription, resourceType string, resourceName string) (*spec.Schema, error) {

crd, err := c.GetResourceSpecDefinition(cr.Name, cr.Version, resourceName)

if err != nil {
log.Warning("Unable to get CRD specifications:", err)
}

if crd == nil {
crd = toOpenAPISpec(cr)
}

return crd, nil
}

// toOpenAPISpec transforms Spec descriptors from a CRD description to an OpenAPI schema
func toOpenAPISpec(repr *olm.CRDDescription) *spec.Schema {
if len(repr.SpecDescriptors) == 0 {
return nil
}
schema := new(spec.Schema).Typed("object", "")
schema.AdditionalProperties = &spec.SchemaOrBool{
Allows: false,
}
for _, param := range repr.SpecDescriptors {
addParam(schema, param)
}
return schema
}

// addParam adds a Spec Descriptor parameter to an OpenAPI schema
func addParam(schema *spec.Schema, param olm.SpecDescriptor) {
parts := strings.SplitN(param.Path, ".", 2)
if len(parts) == 1 {
child := spec.StringProperty().WithTitle(param.DisplayName).WithDescription(param.Description)
schema.SetProperty(parts[0], *child)
} else {
var child *spec.Schema
if _, ok := schema.Properties[parts[0]]; ok {
c := schema.Properties[parts[0]]
child = &c
} else {
child = new(spec.Schema).Typed("object", "")
}
param.Path = parts[1]
addParam(child, param)
schema.SetProperty(parts[0], *child)
}
}
116 changes: 116 additions & 0 deletions pkg/kclient/operators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/go-openapi/spec"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
)

func TestGetResourceSpecDefinitionFromSwagger(t *testing.T) {
Expand Down Expand Up @@ -93,3 +94,118 @@ func TestGetResourceSpecDefinitionFromSwagger(t *testing.T) {
})
}
}

func TestToOpenAPISpec(t *testing.T) {
tests := []struct {
name string
repr olm.CRDDescription
want spec.Schema
}{
{
name: "one-level property",
repr: olm.CRDDescription{
SpecDescriptors: []olm.SpecDescriptor{
{
Path: "path1",
DisplayName: "name to display 1",
Description: "description 1",
},
},
},
want: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"path1": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "description 1",
Title: "name to display 1",
},
},
},
AdditionalProperties: &spec.SchemaOrBool{
Allows: false,
},
},
},
},

{
name: "multiple-levels property",
repr: olm.CRDDescription{
SpecDescriptors: []olm.SpecDescriptor{
{
Path: "subpath1.path1",
DisplayName: "name to display 1.1",
Description: "description 1.1",
},
{
Path: "subpath1.path2",
DisplayName: "name to display 1.2",
Description: "description 1.2",
},
{
Path: "subpath2.path1",
DisplayName: "name to display 2.1",
Description: "description 2.1",
},
},
},
want: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"subpath1": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"path1": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "description 1.1",
Title: "name to display 1.1",
},
},
"path2": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "description 1.2",
Title: "name to display 1.2",
},
},
},
},
},
"subpath2": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"path1": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "description 2.1",
Title: "name to display 2.1",
},
},
},
},
},
},
AdditionalProperties: &spec.SchemaOrBool{
Allows: false,
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := toOpenAPISpec(&tt.repr)
if !reflect.DeepEqual(*result, tt.want) {
t.Errorf("Failed %s:\n\ngot: %+v\n\nwant: %+v", t.Name(), result, tt.want)
}
})
}
}
58 changes: 4 additions & 54 deletions pkg/odo/cli/catalog/describe/operator_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,35 +65,17 @@ func (ohb *operatorBackend) ValidateDescribeService(dso *DescribeServiceOptions)
return err
}

// Get the specific CR that matches "kind"
crs := dso.KClient.GetCustomResourcesFromCSV(&ohb.CSV)

var cr *olm.CRDDescription
hasCR := false
for _, custRes := range *crs {
c := custRes
if c.Kind == ohb.CustomResource {
cr = &c
hasCR = true
break
}
}
var hasCR bool
hasCR, ohb.CR = dso.KClient.CheckCustomResourceInCSV(ohb.CustomResource, &ohb.CSV)
if !hasCR {
return fmt.Errorf("the %q resource doesn't exist in specified %q operator", ohb.CustomResource, ohb.OperatorType)
}

ohb.CR = cr

crd, err := dso.KClient.GetResourceSpecDefinition(ohb.CR.Name, ohb.CR.Version, ohb.CustomResource)
ohb.CRDSpec, err = dso.KClient.GetCRDSpec(ohb.CR, ohb.OperatorType, ohb.CustomResource)
if err != nil {
log.Warning("Unable to get CRD specifications:", err)
return nil
return err
}
ohb.CRDSpec = crd

if crd == nil {
ohb.CRDSpec = toOpenAPISpec(cr)
}
return nil

}
Expand Down Expand Up @@ -198,35 +180,3 @@ func getTypeString(property spec.Schema) string {
}
return tpe
}

// toOpenAPISpec transforms Spec descriptors from a CRD description to an OpenAPI schema
func toOpenAPISpec(repr *olm.CRDDescription) *spec.Schema {
if len(repr.SpecDescriptors) == 0 {
return nil
}
schema := new(spec.Schema).Typed("object", "")
for _, param := range repr.SpecDescriptors {
addParam(schema, param)
}
return schema
}

// addParam adds a Spec Descriptor parameter to an OpenAPI schema
func addParam(schema *spec.Schema, param olm.SpecDescriptor) {
parts := strings.SplitN(param.Path, ".", 2)
if len(parts) == 1 {
child := spec.StringProperty().WithTitle(param.DisplayName).WithDescription(param.Description)
schema.SetProperty(parts[0], *child)
} else {
var child *spec.Schema
if _, ok := schema.Properties[parts[0]]; ok {
c := schema.Properties[parts[0]]
child = &c
} else {
child = new(spec.Schema).Typed("object", "")
}
param.Path = parts[1]
addParam(child, param)
schema.SetProperty(parts[0], *child)
}
}
111 changes: 0 additions & 111 deletions pkg/odo/cli/catalog/describe/operator_backend_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package describe

import (
"reflect"
"testing"

"github.com/go-openapi/spec"
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
)

func TestGetTypeString(t *testing.T) {
Expand Down Expand Up @@ -50,112 +48,3 @@ func TestGetTypeString(t *testing.T) {
})
}
}

func TestToOpenAPISpec(t *testing.T) {
tests := []struct {
name string
repr olm.CRDDescription
want spec.Schema
}{
{
name: "one-level property",
repr: olm.CRDDescription{
SpecDescriptors: []olm.SpecDescriptor{
{
Path: "path1",
DisplayName: "name to display 1",
Description: "description 1",
},
},
},
want: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"path1": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "description 1",
Title: "name to display 1",
},
},
},
},
},
},

{
name: "multiple-levels property",
repr: olm.CRDDescription{
SpecDescriptors: []olm.SpecDescriptor{
{
Path: "subpath1.path1",
DisplayName: "name to display 1.1",
Description: "description 1.1",
},
{
Path: "subpath1.path2",
DisplayName: "name to display 1.2",
Description: "description 1.2",
},
{
Path: "subpath2.path1",
DisplayName: "name to display 2.1",
Description: "description 2.1",
},
},
},
want: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"subpath1": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"path1": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "description 1.1",
Title: "name to display 1.1",
},
},
"path2": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "description 1.2",
Title: "name to display 1.2",
},
},
},
},
},
"subpath2": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"path1": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "description 2.1",
Title: "name to display 2.1",
},
},
},
},
},
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := toOpenAPISpec(&tt.repr)
if !reflect.DeepEqual(*result, tt.want) {
t.Errorf("Failed %s:\n\ngot: %+v\n\nwant: %+v", t.Name(), result, tt.want)
}
})
}
}
Loading

0 comments on commit 3ca2cd7

Please sign in to comment.