fix cli: vela show support list the parameter of ComponentDefinition which use helm charts (#1543)

* vela show support helm

* show helm parameter in web

* add test

* support link jump

* add test
This commit is contained in:
yangsoon
2021-04-23 16:50:44 +08:00
committed by GitHub
parent 4dd00ac536
commit 63855abed9
9 changed files with 480 additions and 49 deletions
+11 -9
View File
@@ -38,15 +38,16 @@ type CRDInfo struct {
// Capability defines the content of a capability
type Capability struct {
Name string `json:"name"`
Type CapType `json:"type"`
CueTemplate string `json:"template,omitempty"`
CueTemplateURI string `json:"templateURI,omitempty"`
Parameters []Parameter `json:"parameters,omitempty"`
CrdName string `json:"crdName,omitempty"`
Center string `json:"center,omitempty"`
Status string `json:"status,omitempty"`
Description string `json:"description,omitempty"`
Name string `json:"name"`
Type CapType `json:"type"`
CueTemplate string `json:"template,omitempty"`
CueTemplateURI string `json:"templateURI,omitempty"`
Parameters []Parameter `json:"parameters,omitempty"`
CrdName string `json:"crdName,omitempty"`
Center string `json:"center,omitempty"`
Status string `json:"status,omitempty"`
Description string `json:"description,omitempty"`
Category CapabilityCategory `json:"category,omitempty"`
// trait only
AppliesTo []string `json:"appliesTo,omitempty"`
@@ -121,6 +122,7 @@ type Parameter struct {
Usage string `json:"usage,omitempty"`
Type cue.Kind `json:"type,omitempty"`
Alias string `json:"alias,omitempty"`
JSONType string `json:"jsonType,omitempty"`
}
// SetFlagBy set cli flag from Parameter
+6
View File
@@ -43,6 +43,7 @@ var app v1beta1.Application
var testShowCdDef v1beta1.ComponentDefinition
var testShowTdDef v1beta1.TraitDefinition
var testCdDef v1beta1.ComponentDefinition
var testCdDefWithHelm v1beta1.ComponentDefinition
var testTdDef v1beta1.TraitDefinition
func TestKubectlPlugin(t *testing.T) {
@@ -75,6 +76,10 @@ var _ = BeforeSuite(func(done Done) {
err = k8sClient.Create(ctx, &testCdDef)
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
Expect(yaml.Unmarshal([]byte(componentDefWithHelm), &testCdDefWithHelm)).Should(BeNil())
err = k8sClient.Create(ctx, &testCdDefWithHelm)
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
Expect(yaml.Unmarshal([]byte(traitDef), &testTdDef)).Should(BeNil())
err = k8sClient.Create(ctx, &testTdDef)
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
@@ -99,6 +104,7 @@ var _ = AfterSuite(func() {
By("delete application and definitions")
Expect(k8sClient.Delete(ctx, &app)).Should(BeNil())
Expect(k8sClient.Delete(ctx, &testCdDef)).Should(BeNil())
Expect(k8sClient.Delete(ctx, &testCdDefWithHelm)).Should(BeNil())
Expect(k8sClient.Delete(ctx, &testTdDef)).Should(BeNil())
Expect(k8sClient.Delete(ctx, &testShowCdDef)).Should(BeNil())
Expect(k8sClient.Delete(ctx, &testShowTdDef)).Should(BeNil())
+30
View File
@@ -117,6 +117,12 @@ var _ = Describe("Test Kubectl Plugin", func() {
Expect(err).NotTo(HaveOccurred())
Expect(output).Should(Equal(showTdResult))
})
It("Test show componentDefinition use Helm Charts as Workload", func() {
cdName := "test-webapp-chart"
output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s", cdName))
Expect(err).NotTo(HaveOccurred())
Expect(output).Should(ContainSubstring("Properties"))
})
})
})
@@ -344,6 +350,30 @@ spec:
`
var componentDefWithHelm = `
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
name: test-webapp-chart
namespace: default
annotations:
definition.oam.dev/description: helm chart for webapp
spec:
workload:
definition:
apiVersion: apps/v1
kind: Deployment
schematic:
helm:
release:
chart:
spec:
chart: "podinfo"
version: "5.1.4"
repository:
url: "http://oam.dev/catalog/"
`
var traitDef = `
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
+3 -1
View File
@@ -17,6 +17,7 @@ limitations under the License.
package main
import (
"context"
"fmt"
"os"
@@ -25,7 +26,8 @@ import (
func main() {
ref := &plugins.MarkdownReference{}
if err := ref.GenerateReferenceDocs(plugins.BaseRefPath); err != nil {
ctx := context.Background()
if err := ref.GenerateReferenceDocs(ctx, plugins.BaseRefPath); err != nil {
fmt.Println(err)
os.Exit(1)
}
+34 -4
View File
@@ -132,8 +132,18 @@ func startReferenceDocsSite(ctx context.Context, c common.Args, ioStreams cmduti
if !capabilityIsValid {
return fmt.Errorf("%s is not a valid component type or trait", capabilityName)
}
ref := &plugins.MarkdownReference{}
if err := ref.CreateMarkdown(capabilities, docsPath, plugins.ReferenceSourcePath); err != nil {
cli, err := c.GetClient()
if err != nil {
return err
}
ref := &plugins.MarkdownReference{
ParseReference: plugins.ParseReference{
Client: cli,
},
}
if err := ref.CreateMarkdown(ctx, capabilities, docsPath, plugins.ReferenceSourcePath); err != nil {
return err
}
@@ -345,11 +355,31 @@ func ShowReferenceConsole(ctx context.Context, c common.Args, ioStreams cmdutil.
return err
}
ref := &plugins.ConsoleReference{}
propertyConsole, err := ref.GenerateCapabilityProperties(capability)
cli, err := c.GetClient()
if err != nil {
return err
}
ref := &plugins.ConsoleReference{
ParseReference: plugins.ParseReference{
Client: cli,
},
}
var propertyConsole []plugins.ConsoleReference
switch capability.Category {
case types.HelmCategory:
_, propertyConsole, err = ref.GenerateHELMProperties(ctx, capability)
if err != nil {
return err
}
case types.CUECategory:
propertyConsole, err = ref.GenerateCUETemplateProperties(capability)
if err != nil {
return err
}
default:
return fmt.Errorf("unsupport capability category %s", capability.Category)
}
for _, p := range propertyConsole {
ioStreams.Info(p.TableName)
p.TableObject.Render()
+7 -3
View File
@@ -205,15 +205,17 @@ func HandleTemplate(in *runtime.RawExtension, schematic *commontypes.Schematic,
tmp.CueTemplate = string(b)
}
if tmp.CueTemplate == "" {
if schematic != nil && schematic.HELM != nil {
tmp.Category = types.HelmCategory
return tmp, nil
}
return types.Capability{}, errors.New("template not exist in definition")
}
if err != nil {
return types.Capability{}, err
}
tmp.Parameters, err = cue.GetParameters(tmp.CueTemplate)
if err != nil {
return types.Capability{}, err
}
tmp.Category = types.CUECategory
return tmp, nil
}
@@ -278,6 +280,7 @@ func SyncDefinitionToLocal(ctx context.Context, c common.Args, capabilityName st
template, err := HandleDefinition(capabilityName, ref.Name,
componentDef.Annotations, componentDef.Spec.Extension, types.TypeComponentDefinition, nil, componentDef.Spec.Schematic)
if err == nil {
template.Namespace = componentDef.Namespace
return &template, nil
}
}
@@ -297,6 +300,7 @@ func SyncDefinitionToLocal(ctx context.Context, c common.Args, capabilityName st
template, err := HandleDefinition(capabilityName, traitDef.Spec.Reference.Name,
traitDef.Annotations, traitDef.Spec.Extension, types.TypeTrait, nil, traitDef.Spec.Schematic)
if err == nil {
template.Namespace = traitDef.Namespace
return &template, nil
}
}
+2
View File
@@ -48,6 +48,7 @@ var _ = Describe("DefinitionFiles", func() {
Type: types.TypeComponentDefinition,
CrdName: "deployments.apps",
Description: "description not defined",
Category: types.CUECategory,
Parameters: []types.Parameter{
{
Type: cue.ListKind,
@@ -80,6 +81,7 @@ var _ = Describe("DefinitionFiles", func() {
Name: WebserviceName,
Type: types.TypeComponentDefinition,
Description: "description not defined",
Category: types.CUECategory,
Parameters: []types.Parameter{{
Name: "env", Type: cue.ListKind,
}, {
+203 -2
View File
@@ -17,12 +17,16 @@ limitations under the License.
package plugins
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/getkin/kin-openapi/openapi3"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
@@ -68,11 +72,13 @@ parameter: {
Name: workloadName,
Type: types.TypeWorkload,
CueTemplate: workloadCueTemplate,
Category: types.CUECategory,
},
{
Name: traitName,
Type: types.TypeTrait,
CueTemplate: traitCueTemplate,
Category: types.CUECategory,
},
},
want: nil,
@@ -89,9 +95,10 @@ parameter: {
},
}
ref := &MarkdownReference{}
ctx := context.Background()
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
got := ref.CreateMarkdown(tc.capabilities, RefTestDir, ReferenceSourcePath)
got := ref.CreateMarkdown(ctx, tc.capabilities, RefTestDir, ReferenceSourcePath)
if diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nCreateMakrdown(...): -want error, +got error:\n%s", tc.reason, diff)
}
@@ -113,7 +120,7 @@ func TestPrepareParameterTable(t *testing.T) {
parameterName := "cpu"
parameterList[0].Name = parameterName
parameterList[0].Required = true
refContent := ref.prepareParameter(tableName, parameterList)
refContent := ref.prepareParameter(tableName, parameterList, types.CUECategory)
assert.Contains(t, refContent, parameterName)
assert.Contains(t, refContent, "cpu")
}
@@ -124,3 +131,197 @@ func TestDeleteRefTestDir(t *testing.T) {
assert.NoError(t, err)
}
}
func TestWalkParameterSchema(t *testing.T) {
testcases := []struct {
data string
ExpectRefs map[string]map[string]ReferenceParameter
}{
{
data: `{
"properties": {
"cmd": {
"description": "Commands to run in the container",
"items": {
"type": "string"
},
"title": "cmd",
"type": "array"
},
"image": {
"description": "Which image would you like to use for your service",
"title": "image",
"type": "string"
}
},
"required": [
"image"
],
"type": "object"
}`,
ExpectRefs: map[string]map[string]ReferenceParameter{
"# Properties": {
"cmd": ReferenceParameter{
Parameter: types.Parameter{
Name: "cmd",
Usage: "Commands to run in the container",
JSONType: "array",
},
PrintableType: "array",
},
"image": ReferenceParameter{
Parameter: types.Parameter{
Name: "image",
Required: true,
Usage: "Which image would you like to use for your service",
JSONType: "string",
},
PrintableType: "string",
},
},
},
},
{
data: `{
"properties": {
"obj": {
"properties": {
"f0": {
"default": "v0",
"type": "string"
},
"f1": {
"default": "v1",
"type": "string"
},
"f2": {
"default": "v2",
"type": "string"
}
},
"type": "object"
},
},
"type": "object"
}`,
ExpectRefs: map[string]map[string]ReferenceParameter{
"# Properties": {
"obj": ReferenceParameter{
Parameter: types.Parameter{
Name: "obj",
JSONType: "object",
},
PrintableType: "[obj](#obj)",
},
},
"## obj": {
"f0": ReferenceParameter{
Parameter: types.Parameter{
Name: "f0",
Default: "v0",
JSONType: "string",
},
PrintableType: "string",
},
"f1": ReferenceParameter{
Parameter: types.Parameter{
Name: "f1",
Default: "v1",
JSONType: "string",
},
PrintableType: "string",
},
"f2": ReferenceParameter{
Parameter: types.Parameter{
Name: "f2",
Default: "v2",
JSONType: "string",
},
PrintableType: "string",
},
},
},
},
{
data: `{
"properties": {
"obj": {
"properties": {
"f0": {
"default": "v0",
"type": "string"
},
"f1": {
"default": "v1",
"type": "object",
"properties": {
"g0": {
"default": "v2",
"type": "string"
}
}
}
},
"type": "object"
}
},
"type": "object"
}`,
ExpectRefs: map[string]map[string]ReferenceParameter{
"# Properties": {
"obj": ReferenceParameter{
Parameter: types.Parameter{
Name: "obj",
JSONType: "object",
},
PrintableType: "[obj](#obj)",
},
},
"## obj": {
"f0": ReferenceParameter{
Parameter: types.Parameter{
Name: "f0",
Default: "v0",
JSONType: "string",
},
PrintableType: "string",
},
"f1": ReferenceParameter{
Parameter: types.Parameter{
Name: "f1",
Default: "v1",
JSONType: "object",
},
PrintableType: "[f1](#f1)",
},
},
"### f1": {
"g0": ReferenceParameter{
Parameter: types.Parameter{
Name: "g0",
Default: "v2",
JSONType: "string",
},
PrintableType: "string",
},
},
},
},
}
for _, cases := range testcases {
helmRefs = make([]HELMReference, 0)
parameterJSON := fmt.Sprintf(BaseOpenAPIV3Template, cases.data)
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(json.RawMessage(parameterJSON))
assert.Equal(t, nil, err)
parameters := swagger.Components.Schemas["parameter"].Value
WalkParameterSchema(parameters, "Properties", 0)
refs := make(map[string]map[string]ReferenceParameter)
for _, items := range helmRefs {
refs[items.Name] = make(map[string]ReferenceParameter)
for _, item := range items.Parameters {
refs[items.Name][item.Name] = item
}
}
assert.Equal(t, true, reflect.DeepEqual(cases.ExpectRefs, refs))
}
}
+184 -30
View File
@@ -17,6 +17,8 @@ limitations under the License.
package plugins
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
@@ -25,7 +27,11 @@ import (
"strings"
"cuelang.org/go/cue"
"github.com/getkin/kin-openapi/openapi3"
"github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/types"
mycue "github.com/oam-dev/kubevela/pkg/cue"
@@ -62,6 +68,7 @@ type Reference interface {
// ParseReference is used to include the common function `parseParameter`
type ParseReference struct {
Client client.Client
}
// MarkdownReference is the struct for capability information in
@@ -181,6 +188,21 @@ services:
`,
}
// BaseOpenAPIV3Template is Standard OpenAPIV3 Template
var BaseOpenAPIV3Template = `{
"openapi": "3.0.0",
"info": {
"title": "definition-parameter",
"version": "1.0"
},
"paths": {},
"components": {
"schemas": {
"parameter": %s
}
}
}`
// ReferenceParameter is the parameter section of CUE template
type ReferenceParameter struct {
types.Parameter `json:",inline,omitempty"`
@@ -194,13 +216,14 @@ var refContent string
var recurseDepth *int
var propertyConsole []ConsoleReference
var displayFormat *string
var helmRefs []HELMReference
func setDisplayFormat(format string) {
displayFormat = &format
}
// GenerateReferenceDocs generates reference docs
func (ref *MarkdownReference) GenerateReferenceDocs(baseRefPath string) error {
func (ref *MarkdownReference) GenerateReferenceDocs(ctx context.Context, baseRefPath string) error {
c, err := common.InitBaseRestConfig()
if err != nil {
return err
@@ -212,15 +235,15 @@ func (ref *MarkdownReference) GenerateReferenceDocs(baseRefPath string) error {
if baseRefPath == "" {
baseRefPath = BaseRefPath
}
return ref.CreateMarkdown(caps, baseRefPath, ReferenceSourcePath)
return ref.CreateMarkdown(ctx, caps, baseRefPath, ReferenceSourcePath)
}
// CreateMarkdown creates markdown based on capabilities
func (ref *MarkdownReference) CreateMarkdown(caps []types.Capability, baseRefPath, referenceSourcePath string) error {
func (ref *MarkdownReference) CreateMarkdown(ctx context.Context, caps []types.Capability, baseRefPath, referenceSourcePath string) error {
setDisplayFormat("markdown")
var capabilityType string
var specificationType string
for _, c := range caps {
for i, c := range caps {
switch c.Type {
case types.TypeWorkload:
capabilityType = WorkloadTypePath
@@ -253,25 +276,35 @@ func (ref *MarkdownReference) CreateMarkdown(caps []types.Capability, baseRefPat
return fmt.Errorf("failed to truncate file %s: %w", markdownFile, err)
}
capName := c.Name
cueValue, err := common.GetCUEParameterValue(c.CueTemplate)
if err != nil {
return fmt.Errorf("failed to retrieve `parameters` value from %s with err: %w", c.Name, err)
}
refContent = ""
var defaultDepth = 0
recurseDepth = &defaultDepth
capNameInTitle := strings.Title(capName)
if err := ref.parseParameters(cueValue, "Properties", defaultDepth); err != nil {
return err
switch c.Category {
case types.CUECategory:
cueValue, err := common.GetCUEParameterValue(c.CueTemplate)
if err != nil {
return fmt.Errorf("failed to retrieve `parameters` value from %s with err: %w", c.Name, err)
}
var defaultDepth = 0
recurseDepth = &defaultDepth
if err := ref.parseParameters(cueValue, "Properties", defaultDepth); err != nil {
return err
}
case types.HelmCategory:
properties, _, err := ref.GenerateHELMProperties(ctx, &caps[i])
if err != nil {
return fmt.Errorf("failed to retrieve `parameters` value from %s with err: %w", c.Name, err)
}
for _, property := range properties {
refContent += ref.prepareParameter("#"+property.Name, property.Parameters, types.HelmCategory)
}
default:
return fmt.Errorf("unsupport capability category %s", c.Category)
}
title := fmt.Sprintf("# %s", capNameInTitle)
description := fmt.Sprintf("\n\n## Description\n\n%s", c.Description)
specificationIntro := fmt.Sprintf("List of all configuration options for a `%s` %s.", capNameInTitle, specificationType)
specificationContent := ref.generateSpecification(capName)
if err != nil {
return err
}
specification := fmt.Sprintf("\n\n## Specification\n\n%s\n\n%s", specificationIntro, specificationContent)
// it's fine if the conflict info files not found
@@ -289,26 +322,45 @@ func (ref *MarkdownReference) CreateMarkdown(caps []types.Capability, baseRefPat
}
// prepareParameter prepares the table content for each property
func (ref *MarkdownReference) prepareParameter(tableName string, parameterList []ReferenceParameter) string {
func (ref *MarkdownReference) prepareParameter(tableName string, parameterList []ReferenceParameter, category types.CapabilityCategory) string {
refContent := fmt.Sprintf("\n\n%s\n\n", tableName)
refContent += "Name | Description | Type | Required | Default \n"
refContent += "------------ | ------------- | ------------- | ------------- | ------------- \n"
for _, p := range parameterList {
printableDefaultValue := ref.getPrintableDefaultValue(p.Default)
refContent += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, p.Usage, p.PrintableType, p.Required, printableDefaultValue)
switch category {
case types.CUECategory:
for _, p := range parameterList {
printableDefaultValue := ref.getCUEPrintableDefaultValue(p.Default)
refContent += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, p.Usage, p.PrintableType, p.Required, printableDefaultValue)
}
case types.HelmCategory:
for _, p := range parameterList {
printableDefaultValue := ref.getHELMPrintableDefaultValue(p.JSONType, p.Default)
refContent += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, strings.ReplaceAll(p.Usage, "\n", ""), p.PrintableType, p.Required, printableDefaultValue)
}
default:
}
return refContent
}
// prepareParameter prepares the table content for each property
func (ref *ConsoleReference) prepareParameter(tableName string, parameterList []ReferenceParameter) ConsoleReference {
func (ref *ParseReference) prepareParameter(tableName string, parameterList []ReferenceParameter, category types.CapabilityCategory) ConsoleReference {
table := tablewriter.NewWriter(os.Stdout)
table.SetColWidth(100)
table.SetHeader([]string{"Name", "Description", "Type", "Required", "Default"})
for _, p := range parameterList {
printableDefaultValue := ref.getPrintableDefaultValue(p.Default)
table.Append([]string{p.Name, p.Usage, p.PrintableType, strconv.FormatBool(p.Required), printableDefaultValue})
switch category {
case types.CUECategory:
for _, p := range parameterList {
printableDefaultValue := ref.getCUEPrintableDefaultValue(p.Default)
table.Append([]string{p.Name, p.Usage, p.PrintableType, strconv.FormatBool(p.Required), printableDefaultValue})
}
case types.HelmCategory:
for _, p := range parameterList {
printableDefaultValue := ref.getHELMPrintableDefaultValue(p.JSONType, p.Default)
table.Append([]string{p.Name, p.Usage, p.PrintableType, strconv.FormatBool(p.Required), printableDefaultValue})
}
default:
}
return ConsoleReference{TableName: tableName, TableObject: table}
}
@@ -382,18 +434,18 @@ func (ref *ParseReference) parseParameters(paraValue cue.Value, paramKey string,
case "markdown":
tableName := fmt.Sprintf("%s %s", strings.Repeat("#", depth+2), paramKey)
ref := MarkdownReference{}
refContent = ref.prepareParameter(tableName, params) + refContent
refContent = ref.prepareParameter(tableName, params, types.CUECategory) + refContent
case "console":
ref := ConsoleReference{}
tableName := fmt.Sprintf("%s %s", strings.Repeat("#", depth+1), paramKey)
console := ref.prepareParameter(tableName, params)
console := ref.prepareParameter(tableName, params, types.CUECategory)
propertyConsole = append([]ConsoleReference{console}, propertyConsole...)
}
return nil
}
// getPrintableDefaultValue converts the value in `interface{}` type to be printable
func (ref *ParseReference) getPrintableDefaultValue(v interface{}) string {
// getCUEPrintableDefaultValue converts the value in `interface{}` type to be printable
func (ref *ParseReference) getCUEPrintableDefaultValue(v interface{}) string {
if v == nil {
return ""
}
@@ -411,6 +463,20 @@ func (ref *ParseReference) getPrintableDefaultValue(v interface{}) string {
return ""
}
func (ref *ParseReference) getHELMPrintableDefaultValue(dataType string, value interface{}) string {
if value != nil {
return strings.TrimSpace(fmt.Sprintf("%v", value))
}
defaultValueMap := map[string]string{
"number": "0",
"boolean": "false",
"string": "\"\"",
"object": "{}",
"array": "[]",
}
return defaultValueMap[dataType]
}
// generateSpecification generates Specification part for reference docs
func (ref *MarkdownReference) generateSpecification(capabilityName string) string {
return fmt.Sprintf("```yaml%s```", ConfigurationYamlSample[capabilityName])
@@ -429,8 +495,8 @@ func (ref *MarkdownReference) generateConflictWithAndMore(capabilityName string,
return "\n" + string(data), nil
}
// GenerateCapabilityProperties get all properties of a capability
func (ref *ConsoleReference) GenerateCapabilityProperties(capability *types.Capability) ([]ConsoleReference, error) {
// GenerateCUETemplateProperties get all properties of a capability
func (ref *ConsoleReference) GenerateCUETemplateProperties(capability *types.Capability) ([]ConsoleReference, error) {
setDisplayFormat("console")
capName := capability.Name
@@ -446,3 +512,91 @@ func (ref *ConsoleReference) GenerateCapabilityProperties(capability *types.Capa
return propertyConsole, nil
}
// HELMReference contains parameters info of HelmCategory type capability
type HELMReference struct {
Name string
Parameters []ReferenceParameter
Depth int
}
// HELMSchema is a struct contains *openapi3.Schema style parameter
type HELMSchema struct {
Name string
Schemas *openapi3.Schema
}
// GenerateHELMProperties get all properties of a HelmCategory type capability
func (ref *ParseReference) GenerateHELMProperties(ctx context.Context, capability *types.Capability) ([]HELMReference, []ConsoleReference, error) {
cmName := fmt.Sprintf("%s%s", types.CapabilityConfigMapNamePrefix, capability.Name)
var cm v1.ConfigMap
helmRefs = make([]HELMReference, 0)
if err := ref.Client.Get(ctx, client.ObjectKey{Namespace: capability.Namespace, Name: cmName}, &cm); err != nil {
return nil, nil, err
}
data, ok := cm.Data[types.OpenapiV3JSONSchema]
if !ok {
return nil, nil, errors.Errorf("configMap doesn't have openapi-v3-json-schema data")
}
parameterJSON := fmt.Sprintf(BaseOpenAPIV3Template, data)
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(json.RawMessage(parameterJSON))
if err != nil {
return nil, nil, err
}
parameters := swagger.Components.Schemas["parameter"].Value
WalkParameterSchema(parameters, "Properties", 0)
var consoleRefs []ConsoleReference
for _, item := range helmRefs {
consoleRefs = append(consoleRefs, ref.prepareParameter(item.Name, item.Parameters, types.HelmCategory))
}
return helmRefs, consoleRefs, err
}
// WalkParameterSchema will extract properties from *openapi3.Schema
func WalkParameterSchema(parameters *openapi3.Schema, name string, depth int) {
if parameters == nil {
return
}
var schemas []HELMSchema
var helmParameters []ReferenceParameter
for k, v := range parameters.Properties {
p := ReferenceParameter{
Parameter: types.Parameter{
Name: k,
Default: v.Value.Default,
Usage: v.Value.Description,
JSONType: v.Value.Type,
},
PrintableType: v.Value.Type,
}
required := false
for _, requiredType := range parameters.Required {
if k == requiredType {
required = true
break
}
}
p.Required = required
if v.Value.Type == "object" {
if v.Value.Properties != nil {
schemas = append(schemas, HELMSchema{
Name: k,
Schemas: v.Value,
})
}
p.PrintableType = fmt.Sprintf("[%s](#%s)", k, k)
}
helmParameters = append(helmParameters, p)
}
helmRefs = append(helmRefs, HELMReference{
Name: fmt.Sprintf("%s %s", strings.Repeat("#", depth+1), name),
Parameters: helmParameters,
Depth: depth + 1,
})
for _, schema := range schemas {
WalkParameterSchema(schema.Schemas, schema.Name, depth+1)
}
}