2021-03-10 11:51:53 +08:00
|
|
|
/*
|
|
|
|
|
Copyright 2021 The KubeVela Authors.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
2021-03-10 10:44:58 +08:00
|
|
|
package utils
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
2021-12-07 15:50:04 +08:00
|
|
|
"io/ioutil"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2021-03-10 10:44:58 +08:00
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"cuelang.org/go/cue"
|
2021-04-08 20:35:21 +08:00
|
|
|
"cuelang.org/go/cue/build"
|
2021-03-10 10:44:58 +08:00
|
|
|
"github.com/getkin/kin-openapi/openapi3"
|
2021-03-26 23:02:58 +08:00
|
|
|
"github.com/pkg/errors"
|
2021-12-07 15:50:04 +08:00
|
|
|
git "gopkg.in/src-d/go-git.v4"
|
2021-03-10 10:44:58 +08:00
|
|
|
v1 "k8s.io/api/core/v1"
|
|
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2021-08-26 19:56:46 +08:00
|
|
|
"k8s.io/klog/v2"
|
2021-03-10 10:44:58 +08:00
|
|
|
"k8s.io/utils/pointer"
|
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
|
|
2021-03-24 13:27:29 +08:00
|
|
|
commontypes "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
2021-04-09 12:28:04 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
2021-03-10 10:44:58 +08:00
|
|
|
"github.com/oam-dev/kubevela/apis/types"
|
2021-04-11 14:10:16 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/appfile"
|
2021-03-23 00:16:17 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/appfile/helm"
|
2021-06-02 15:37:06 +08:00
|
|
|
velacue "github.com/oam-dev/kubevela/pkg/cue"
|
2021-08-30 11:43:20 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/cue/model"
|
2021-06-02 15:37:06 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/cue/packages"
|
2021-03-10 10:44:58 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/oam/util"
|
|
|
|
|
"github.com/oam-dev/kubevela/pkg/utils/common"
|
2021-12-01 10:03:08 +08:00
|
|
|
"github.com/oam-dev/kubevela/pkg/utils/terraform"
|
2021-03-10 10:44:58 +08:00
|
|
|
)
|
|
|
|
|
|
2021-06-03 11:23:20 +08:00
|
|
|
// data types of parameter value
|
|
|
|
|
const (
|
|
|
|
|
TerraformVariableString string = "string"
|
|
|
|
|
TerraformVariableNumber string = "number"
|
|
|
|
|
TerraformVariableBool string = "bool"
|
|
|
|
|
TerraformVariableList string = "list"
|
|
|
|
|
TerraformVariableTuple string = "tuple"
|
|
|
|
|
TerraformVariableMap string = "map"
|
|
|
|
|
TerraformVariableObject string = "object"
|
2021-08-30 20:16:24 +08:00
|
|
|
TerraformVariableNull string = ""
|
2022-02-08 18:48:38 +08:00
|
|
|
TerraformVariableAny string = "any"
|
2021-08-30 20:16:24 +08:00
|
|
|
|
|
|
|
|
TerraformListTypePrefix string = "list("
|
|
|
|
|
TerraformTupleTypePrefix string = "tuple("
|
|
|
|
|
TerraformMapTypePrefix string = "map("
|
|
|
|
|
TerraformObjectTypePrefix string = "object("
|
2022-02-08 18:48:38 +08:00
|
|
|
TerraformSetTypePrefix string = "set("
|
2021-11-09 13:06:55 +08:00
|
|
|
|
|
|
|
|
typeTraitDefinition = "trait"
|
|
|
|
|
typeComponentDefinition = "component"
|
|
|
|
|
typeWorkflowStepDefinition = "workflowstep"
|
2021-06-03 11:23:20 +08:00
|
|
|
)
|
|
|
|
|
|
2021-06-15 13:36:23 +08:00
|
|
|
// ErrNoSectionParameterInCue means there is not parameter section in Cue template of a workload
|
|
|
|
|
type ErrNoSectionParameterInCue struct {
|
|
|
|
|
capName string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e ErrNoSectionParameterInCue) Error() string {
|
|
|
|
|
return fmt.Sprintf("capability %s doesn't contain section `parameter`", e.capName)
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 10:44:58 +08:00
|
|
|
// CapabilityDefinitionInterface is the interface for Capability (WorkloadDefinition and TraitDefinition)
|
|
|
|
|
type CapabilityDefinitionInterface interface {
|
2021-03-29 17:20:33 +08:00
|
|
|
GetCapabilityObject(ctx context.Context, k8sClient client.Client, namespace, name string) (*types.Capability, error)
|
|
|
|
|
GetOpenAPISchema(ctx context.Context, k8sClient client.Client, namespace, name string) ([]byte, error)
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
|
2021-03-15 11:08:46 +08:00
|
|
|
// CapabilityComponentDefinition is the struct for ComponentDefinition
|
|
|
|
|
type CapabilityComponentDefinition struct {
|
2021-04-09 12:28:04 +08:00
|
|
|
Name string `json:"name"`
|
|
|
|
|
ComponentDefinition v1beta1.ComponentDefinition `json:"componentDefinition"`
|
2021-03-17 17:18:54 +08:00
|
|
|
|
|
|
|
|
WorkloadType util.WorkloadType `json:"workloadType"`
|
|
|
|
|
WorkloadDefName string `json:"workloadDefName"`
|
|
|
|
|
|
2021-06-03 11:23:20 +08:00
|
|
|
Helm *commontypes.Helm `json:"helm"`
|
|
|
|
|
Kube *commontypes.Kube `json:"kube"`
|
|
|
|
|
Terraform *commontypes.Terraform `json:"terraform"`
|
2021-03-10 10:44:58 +08:00
|
|
|
CapabilityBaseDefinition
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-17 22:47:57 +08:00
|
|
|
// NewCapabilityComponentDef will create a CapabilityComponentDefinition
|
|
|
|
|
func NewCapabilityComponentDef(componentDefinition *v1beta1.ComponentDefinition) CapabilityComponentDefinition {
|
|
|
|
|
var def CapabilityComponentDefinition
|
|
|
|
|
def.Name = componentDefinition.Name
|
|
|
|
|
if componentDefinition.Spec.Workload.Definition == (commontypes.WorkloadGVK{}) && componentDefinition.Spec.Workload.Type != "" {
|
|
|
|
|
def.WorkloadType = util.ReferWorkload
|
|
|
|
|
def.WorkloadDefName = componentDefinition.Spec.Workload.Type
|
|
|
|
|
}
|
2021-06-03 11:23:20 +08:00
|
|
|
if componentDefinition.Spec.Schematic != nil {
|
|
|
|
|
if componentDefinition.Spec.Schematic.HELM != nil {
|
|
|
|
|
def.WorkloadType = util.HELMDef
|
|
|
|
|
def.Helm = componentDefinition.Spec.Schematic.HELM
|
|
|
|
|
}
|
|
|
|
|
if componentDefinition.Spec.Schematic.KUBE != nil {
|
|
|
|
|
def.WorkloadType = util.KubeDef
|
|
|
|
|
def.Kube = componentDefinition.Spec.Schematic.KUBE
|
|
|
|
|
}
|
|
|
|
|
if componentDefinition.Spec.Schematic.Terraform != nil {
|
|
|
|
|
def.WorkloadType = util.TerraformDef
|
|
|
|
|
def.Terraform = componentDefinition.Spec.Schematic.Terraform
|
|
|
|
|
}
|
2021-05-17 22:47:57 +08:00
|
|
|
}
|
|
|
|
|
def.ComponentDefinition = *componentDefinition.DeepCopy()
|
|
|
|
|
return def
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 10:44:58 +08:00
|
|
|
// GetOpenAPISchema gets OpenAPI v3 schema by WorkloadDefinition name
|
2021-06-02 15:37:06 +08:00
|
|
|
func (def *CapabilityComponentDefinition) GetOpenAPISchema(pd *packages.PackageDiscover, name string) ([]byte, error) {
|
2021-05-23 14:16:06 +08:00
|
|
|
capability, err := appfile.ConvertTemplateJSON2Object(name, def.ComponentDefinition.Spec.Extension, def.ComponentDefinition.Spec.Schematic)
|
2021-03-10 10:44:58 +08:00
|
|
|
if err != nil {
|
2021-05-23 14:16:06 +08:00
|
|
|
return nil, fmt.Errorf("failed to convert ComponentDefinition to Capability Object")
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
2021-05-23 14:16:06 +08:00
|
|
|
return getOpenAPISchema(capability, pd)
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
|
2021-06-03 11:23:20 +08:00
|
|
|
// GetOpenAPISchemaFromTerraformComponentDefinition gets OpenAPI v3 schema by WorkloadDefinition name
|
|
|
|
|
func GetOpenAPISchemaFromTerraformComponentDefinition(configuration string) ([]byte, error) {
|
|
|
|
|
schemas := make(map[string]*openapi3.Schema)
|
|
|
|
|
var required []string
|
2022-01-05 19:45:30 +08:00
|
|
|
variables, _, err := common.ParseTerraformVariables(configuration)
|
2021-06-03 11:23:20 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to generate capability properties")
|
|
|
|
|
}
|
|
|
|
|
for k, v := range variables {
|
|
|
|
|
var schema *openapi3.Schema
|
|
|
|
|
switch v.Type {
|
|
|
|
|
case TerraformVariableString:
|
|
|
|
|
schema = openapi3.NewStringSchema()
|
|
|
|
|
case TerraformVariableNumber:
|
|
|
|
|
schema = openapi3.NewFloat64Schema()
|
|
|
|
|
case TerraformVariableBool:
|
|
|
|
|
schema = openapi3.NewBoolSchema()
|
|
|
|
|
case TerraformVariableList, TerraformVariableTuple:
|
|
|
|
|
schema = openapi3.NewArraySchema()
|
|
|
|
|
case TerraformVariableMap, TerraformVariableObject:
|
|
|
|
|
schema = openapi3.NewObjectSchema()
|
2022-02-08 18:48:38 +08:00
|
|
|
case TerraformVariableAny:
|
|
|
|
|
switch v.Default.(type) {
|
|
|
|
|
case []interface{}:
|
|
|
|
|
schema = openapi3.NewArraySchema()
|
|
|
|
|
case map[string]interface{}:
|
|
|
|
|
schema = openapi3.NewObjectSchema()
|
|
|
|
|
}
|
2021-08-30 20:16:24 +08:00
|
|
|
case TerraformVariableNull:
|
2022-02-08 18:48:38 +08:00
|
|
|
switch v.Default.(type) {
|
|
|
|
|
case nil, string:
|
|
|
|
|
schema = openapi3.NewStringSchema()
|
|
|
|
|
case []interface{}:
|
|
|
|
|
schema = openapi3.NewArraySchema()
|
|
|
|
|
case map[string]interface{}:
|
|
|
|
|
schema = openapi3.NewObjectSchema()
|
|
|
|
|
case int, float64:
|
|
|
|
|
schema = openapi3.NewFloat64Schema()
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("null type variable is NOT supported, please specify a type for the variable: %s", v.Name)
|
|
|
|
|
}
|
2021-08-30 20:16:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// To identify unusual list type
|
|
|
|
|
if schema == nil {
|
|
|
|
|
switch {
|
2022-02-08 18:48:38 +08:00
|
|
|
case strings.HasPrefix(v.Type, TerraformListTypePrefix) || strings.HasPrefix(v.Type, TerraformTupleTypePrefix) ||
|
|
|
|
|
strings.HasPrefix(v.Type, TerraformSetTypePrefix):
|
2021-08-30 20:16:24 +08:00
|
|
|
schema = openapi3.NewArraySchema()
|
|
|
|
|
case strings.HasPrefix(v.Type, TerraformMapTypePrefix) || strings.HasPrefix(v.Type, TerraformObjectTypePrefix):
|
|
|
|
|
schema = openapi3.NewObjectSchema()
|
2022-02-08 18:48:38 +08:00
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("the type `%s` of variable %s is NOT supported", v.Type, v.Name)
|
2021-08-30 20:16:24 +08:00
|
|
|
}
|
2021-06-03 11:23:20 +08:00
|
|
|
}
|
|
|
|
|
schema.Title = k
|
2022-02-15 18:53:40 +08:00
|
|
|
if v.Required {
|
|
|
|
|
required = append(required, k)
|
|
|
|
|
}
|
2021-06-03 11:23:20 +08:00
|
|
|
if v.Default != nil {
|
|
|
|
|
schema.Default = v.Default
|
|
|
|
|
}
|
|
|
|
|
schema.Description = v.Description
|
|
|
|
|
schemas[v.Name] = schema
|
|
|
|
|
}
|
2021-12-01 10:03:08 +08:00
|
|
|
|
|
|
|
|
otherProperties := parseOtherProperties4TerraformDefinition()
|
|
|
|
|
for k, v := range otherProperties {
|
|
|
|
|
schemas[k] = v
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 11:23:20 +08:00
|
|
|
return generateJSONSchemaWithRequiredProperty(schemas, required)
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-05 19:45:30 +08:00
|
|
|
// GetTerraformConfigurationFromRemote gets Terraform Configuration(HCL)
|
|
|
|
|
func GetTerraformConfigurationFromRemote(name, remoteURL, remotePath string) (string, error) {
|
2021-12-07 15:50:04 +08:00
|
|
|
tmpPath := filepath.Join("./tmp/terraform", name)
|
2022-01-10 14:08:48 +08:00
|
|
|
// Check if the directory exists. If yes, remove it.
|
|
|
|
|
if _, err := os.Stat(tmpPath); err == nil {
|
|
|
|
|
err := os.RemoveAll(tmpPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", errors.Wrap(err, "failed to remove the directory")
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-07 15:50:04 +08:00
|
|
|
_, err := git.PlainClone(tmpPath, false, &git.CloneOptions{
|
|
|
|
|
URL: remoteURL,
|
2022-01-10 14:08:48 +08:00
|
|
|
Progress: nil,
|
2021-12-07 15:50:04 +08:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-24 11:52:07 +08:00
|
|
|
tfPath := filepath.Join(tmpPath, remotePath, "variables.tf")
|
|
|
|
|
if _, err := os.Stat(tfPath); err != nil {
|
|
|
|
|
tfPath = filepath.Join(tmpPath, remotePath, "main.tf")
|
|
|
|
|
if _, err := os.Stat(tfPath); err != nil {
|
|
|
|
|
return "", errors.Wrap(err, "failed to find main.tf or variables.tf in Terraform configurations of the remote repository")
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-07 15:50:04 +08:00
|
|
|
conf, err := ioutil.ReadFile(filepath.Clean(tfPath))
|
|
|
|
|
if err != nil {
|
2022-01-24 11:52:07 +08:00
|
|
|
return "", errors.Wrap(err, "failed to read Terraform configuration")
|
2021-12-07 15:50:04 +08:00
|
|
|
}
|
|
|
|
|
if err := os.RemoveAll(tmpPath); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string(conf), nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-01 10:03:08 +08:00
|
|
|
func parseOtherProperties4TerraformDefinition() map[string]*openapi3.Schema {
|
|
|
|
|
otherProperties := make(map[string]*openapi3.Schema)
|
|
|
|
|
|
|
|
|
|
// 1. writeConnectionSecretToRef
|
|
|
|
|
secretName := openapi3.NewStringSchema()
|
|
|
|
|
secretName.Title = "name"
|
|
|
|
|
secretName.Description = terraform.TerraformSecretNameDescription
|
|
|
|
|
|
|
|
|
|
secretNamespace := openapi3.NewStringSchema()
|
|
|
|
|
secretNamespace.Title = "namespace"
|
|
|
|
|
secretNamespace.Description = terraform.TerraformSecretNamespaceDescription
|
|
|
|
|
|
|
|
|
|
secret := openapi3.NewObjectSchema()
|
|
|
|
|
secret.Title = terraform.TerraformWriteConnectionSecretToRefName
|
|
|
|
|
secret.Description = terraform.TerraformWriteConnectionSecretToRefDescription
|
|
|
|
|
secret.Properties = openapi3.Schemas{
|
|
|
|
|
"name": &openapi3.SchemaRef{Value: secretName},
|
|
|
|
|
"namespace": &openapi3.SchemaRef{Value: secretNamespace},
|
|
|
|
|
}
|
|
|
|
|
secret.Required = []string{"name"}
|
|
|
|
|
|
|
|
|
|
otherProperties[terraform.TerraformWriteConnectionSecretToRefName] = secret
|
|
|
|
|
|
|
|
|
|
// 2. providerRef
|
|
|
|
|
providerName := openapi3.NewStringSchema()
|
|
|
|
|
providerName.Title = "name"
|
|
|
|
|
providerName.Description = "The name of the Terraform Cloud provider"
|
|
|
|
|
|
|
|
|
|
providerNamespace := openapi3.NewStringSchema()
|
|
|
|
|
providerNamespace.Title = "namespace"
|
|
|
|
|
providerNamespace.Default = "default"
|
|
|
|
|
providerNamespace.Description = "The namespace of the Terraform Cloud provider"
|
|
|
|
|
|
|
|
|
|
var providerRefName = "providerRef"
|
|
|
|
|
provider := openapi3.NewObjectSchema()
|
|
|
|
|
provider.Title = providerRefName
|
|
|
|
|
provider.Description = "specifies the Provider"
|
|
|
|
|
provider.Properties = openapi3.Schemas{
|
|
|
|
|
"name": &openapi3.SchemaRef{Value: providerName},
|
|
|
|
|
"namespace": &openapi3.SchemaRef{Value: providerNamespace},
|
|
|
|
|
}
|
|
|
|
|
provider.Required = []string{"name"}
|
|
|
|
|
|
|
|
|
|
otherProperties[providerRefName] = provider
|
|
|
|
|
|
|
|
|
|
// 3. deleteResource
|
|
|
|
|
var deleteResourceName = "deleteResource"
|
|
|
|
|
deleteResource := openapi3.NewBoolSchema()
|
|
|
|
|
deleteResource.Title = deleteResourceName
|
|
|
|
|
deleteResource.Description = "DeleteResource will determine whether provisioned cloud resources will be deleted when application is deleted"
|
|
|
|
|
deleteResource.Default = true
|
|
|
|
|
otherProperties[deleteResourceName] = deleteResource
|
|
|
|
|
|
|
|
|
|
// 4. region
|
|
|
|
|
var regionName = "region"
|
|
|
|
|
region := openapi3.NewStringSchema()
|
|
|
|
|
region.Title = regionName
|
|
|
|
|
region.Description = "Region is cloud provider's region. It will override providerRef"
|
|
|
|
|
otherProperties[regionName] = region
|
|
|
|
|
|
|
|
|
|
return otherProperties
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 11:23:20 +08:00
|
|
|
func generateJSONSchemaWithRequiredProperty(schemas map[string]*openapi3.Schema, required []string) ([]byte, error) {
|
|
|
|
|
s := openapi3.NewObjectSchema().WithProperties(schemas)
|
|
|
|
|
if len(required) > 0 {
|
|
|
|
|
s.Required = required
|
|
|
|
|
}
|
|
|
|
|
b, err := s.MarshalJSON()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "cannot marshal generated schema into json")
|
|
|
|
|
}
|
|
|
|
|
return b, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 21:16:40 +08:00
|
|
|
// GetKubeSchematicOpenAPISchema gets OpenAPI v3 schema based on kube schematic parameters for component and trait definition
|
|
|
|
|
func GetKubeSchematicOpenAPISchema(params []commontypes.KubeParameter) ([]byte, error) {
|
2021-03-26 23:02:58 +08:00
|
|
|
required := []string{}
|
|
|
|
|
properties := map[string]*openapi3.Schema{}
|
|
|
|
|
for _, p := range params {
|
|
|
|
|
var tmp *openapi3.Schema
|
|
|
|
|
switch p.ValueType {
|
|
|
|
|
case commontypes.StringType:
|
|
|
|
|
tmp = openapi3.NewStringSchema()
|
|
|
|
|
case commontypes.NumberType:
|
|
|
|
|
tmp = openapi3.NewFloat64Schema()
|
|
|
|
|
case commontypes.BooleanType:
|
|
|
|
|
tmp = openapi3.NewBoolSchema()
|
|
|
|
|
default:
|
|
|
|
|
tmp = openapi3.NewStringSchema()
|
|
|
|
|
}
|
|
|
|
|
if p.Required != nil && *p.Required {
|
|
|
|
|
required = append(required, p.Name)
|
|
|
|
|
}
|
2021-05-26 21:16:40 +08:00
|
|
|
|
2021-03-26 23:02:58 +08:00
|
|
|
if p.Description != nil {
|
2021-05-26 21:16:40 +08:00
|
|
|
tmp.Description = fmt.Sprintf("%s %s", tmp.Description, *p.Description)
|
|
|
|
|
} else {
|
|
|
|
|
// save FieldPaths into description
|
|
|
|
|
tmp.Description = fmt.Sprintf("The value will be applied to fields: [%s].", strings.Join(p.FieldPaths, ","))
|
2021-03-26 23:02:58 +08:00
|
|
|
}
|
|
|
|
|
properties[p.Name] = tmp
|
|
|
|
|
}
|
2021-06-03 11:23:20 +08:00
|
|
|
return generateJSONSchemaWithRequiredProperty(properties, required)
|
2021-03-26 23:02:58 +08:00
|
|
|
}
|
|
|
|
|
|
2021-03-10 10:44:58 +08:00
|
|
|
// StoreOpenAPISchema stores OpenAPI v3 schema in ConfigMap from WorkloadDefinition
|
2021-04-26 10:07:55 +08:00
|
|
|
func (def *CapabilityComponentDefinition) StoreOpenAPISchema(ctx context.Context, k8sClient client.Client,
|
2021-06-02 15:37:06 +08:00
|
|
|
pd *packages.PackageDiscover, namespace, name, revName string) (string, error) {
|
2021-03-23 00:16:17 +08:00
|
|
|
var jsonSchema []byte
|
|
|
|
|
var err error
|
|
|
|
|
switch def.WorkloadType {
|
|
|
|
|
case util.HELMDef:
|
|
|
|
|
jsonSchema, err = helm.GetChartValuesJSONSchema(ctx, def.Helm)
|
2021-03-26 23:02:58 +08:00
|
|
|
case util.KubeDef:
|
2021-05-26 21:16:40 +08:00
|
|
|
jsonSchema, err = GetKubeSchematicOpenAPISchema(def.Kube.Parameters)
|
2021-06-03 11:23:20 +08:00
|
|
|
case util.TerraformDef:
|
|
|
|
|
if def.Terraform == nil {
|
|
|
|
|
return "", fmt.Errorf("no Configuration is set in Terraform specification: %s", def.Name)
|
|
|
|
|
}
|
2021-12-07 15:50:04 +08:00
|
|
|
configuration := def.Terraform.Configuration
|
|
|
|
|
if def.Terraform.Type == "remote" {
|
2022-01-05 19:45:30 +08:00
|
|
|
configuration, err = GetTerraformConfigurationFromRemote(def.Name, def.Terraform.Configuration, def.Terraform.Path)
|
2021-12-07 15:50:04 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("cannot get Terraform configuration %s from remote: %w", def.Name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
jsonSchema, err = GetOpenAPISchemaFromTerraformComponentDefinition(configuration)
|
2021-03-23 00:16:17 +08:00
|
|
|
default:
|
2021-05-23 14:16:06 +08:00
|
|
|
jsonSchema, err = def.GetOpenAPISchema(pd, name)
|
2021-03-23 00:16:17 +08:00
|
|
|
}
|
2021-03-10 10:44:58 +08:00
|
|
|
if err != nil {
|
2021-05-23 14:16:06 +08:00
|
|
|
return "", fmt.Errorf("failed to generate OpenAPI v3 JSON schema for capability %s: %w", def.Name, err)
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
2021-03-15 11:08:46 +08:00
|
|
|
componentDefinition := def.ComponentDefinition
|
2021-03-10 10:44:58 +08:00
|
|
|
ownerReference := []metav1.OwnerReference{{
|
2021-03-15 11:08:46 +08:00
|
|
|
APIVersion: componentDefinition.APIVersion,
|
|
|
|
|
Kind: componentDefinition.Kind,
|
|
|
|
|
Name: componentDefinition.Name,
|
|
|
|
|
UID: componentDefinition.GetUID(),
|
2021-03-10 10:44:58 +08:00
|
|
|
Controller: pointer.BoolPtr(true),
|
|
|
|
|
BlockOwnerDeletion: pointer.BoolPtr(true),
|
|
|
|
|
}}
|
2021-11-30 16:53:01 +08:00
|
|
|
cmName, err := def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, componentDefinition.Name, typeComponentDefinition, componentDefinition.Labels, nil, jsonSchema, ownerReference)
|
2021-03-15 11:08:46 +08:00
|
|
|
if err != nil {
|
2021-05-23 14:16:06 +08:00
|
|
|
return cmName, err
|
2021-03-15 11:08:46 +08:00
|
|
|
}
|
2021-04-26 10:07:55 +08:00
|
|
|
|
2021-08-26 19:56:46 +08:00
|
|
|
// Create a configmap to store parameter for each definitionRevision
|
|
|
|
|
defRev := new(v1beta1.DefinitionRevision)
|
|
|
|
|
if err = k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revName}, defRev); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
ownerReference = []metav1.OwnerReference{{
|
|
|
|
|
APIVersion: defRev.APIVersion,
|
|
|
|
|
Kind: defRev.Kind,
|
|
|
|
|
Name: defRev.Name,
|
|
|
|
|
UID: defRev.GetUID(),
|
|
|
|
|
Controller: pointer.BoolPtr(true),
|
|
|
|
|
BlockOwnerDeletion: pointer.BoolPtr(true),
|
|
|
|
|
}}
|
2021-11-30 16:53:01 +08:00
|
|
|
_, err = def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, revName, typeComponentDefinition, defRev.Spec.ComponentDefinition.Labels, nil, jsonSchema, ownerReference)
|
2021-04-26 10:07:55 +08:00
|
|
|
if err != nil {
|
2021-05-23 14:16:06 +08:00
|
|
|
return cmName, err
|
2021-04-26 10:07:55 +08:00
|
|
|
}
|
2021-05-23 14:16:06 +08:00
|
|
|
return cmName, nil
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CapabilityTraitDefinition is the Capability struct for TraitDefinition
|
|
|
|
|
type CapabilityTraitDefinition struct {
|
2021-04-26 10:07:55 +08:00
|
|
|
Name string `json:"name"`
|
|
|
|
|
TraitDefinition v1beta1.TraitDefinition `json:"traitDefinition"`
|
2021-05-26 21:16:40 +08:00
|
|
|
|
|
|
|
|
DefCategoryType util.WorkloadType `json:"defCategoryType"`
|
|
|
|
|
|
|
|
|
|
Kube *commontypes.Kube `json:"kube"`
|
|
|
|
|
|
2021-03-10 10:44:58 +08:00
|
|
|
CapabilityBaseDefinition
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 21:16:40 +08:00
|
|
|
// NewCapabilityTraitDef will create a CapabilityTraitDefinition
|
|
|
|
|
func NewCapabilityTraitDef(traitdefinition *v1beta1.TraitDefinition) CapabilityTraitDefinition {
|
|
|
|
|
var def CapabilityTraitDefinition
|
|
|
|
|
def.Name = traitdefinition.Name // or def.Name = req.NamespacedName.Name
|
|
|
|
|
if traitdefinition.Spec.Schematic != nil && traitdefinition.Spec.Schematic.KUBE != nil {
|
|
|
|
|
def.DefCategoryType = util.KubeDef
|
|
|
|
|
def.Kube = traitdefinition.Spec.Schematic.KUBE
|
|
|
|
|
}
|
|
|
|
|
def.TraitDefinition = *traitdefinition.DeepCopy()
|
|
|
|
|
return def
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 10:44:58 +08:00
|
|
|
// GetOpenAPISchema gets OpenAPI v3 schema by TraitDefinition name
|
2021-06-02 15:37:06 +08:00
|
|
|
func (def *CapabilityTraitDefinition) GetOpenAPISchema(pd *packages.PackageDiscover, name string) ([]byte, error) {
|
2021-05-23 14:16:06 +08:00
|
|
|
capability, err := appfile.ConvertTemplateJSON2Object(name, def.TraitDefinition.Spec.Extension, def.TraitDefinition.Spec.Schematic)
|
2021-03-10 10:44:58 +08:00
|
|
|
if err != nil {
|
2021-05-23 14:16:06 +08:00
|
|
|
return nil, fmt.Errorf("failed to convert WorkloadDefinition to Capability Object")
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
2021-05-23 14:16:06 +08:00
|
|
|
return getOpenAPISchema(capability, pd)
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StoreOpenAPISchema stores OpenAPI v3 schema from TraitDefinition in ConfigMap
|
2021-06-02 15:37:06 +08:00
|
|
|
func (def *CapabilityTraitDefinition) StoreOpenAPISchema(ctx context.Context, k8sClient client.Client, pd *packages.PackageDiscover, namespace, name string, revName string) (string, error) {
|
2021-05-26 21:16:40 +08:00
|
|
|
var jsonSchema []byte
|
|
|
|
|
var err error
|
|
|
|
|
switch def.DefCategoryType {
|
|
|
|
|
case util.KubeDef: // Kube template
|
|
|
|
|
jsonSchema, err = GetKubeSchematicOpenAPISchema(def.Kube.Parameters)
|
|
|
|
|
default: // CUE template
|
|
|
|
|
jsonSchema, err = def.GetOpenAPISchema(pd, name)
|
|
|
|
|
}
|
2021-03-10 10:44:58 +08:00
|
|
|
if err != nil {
|
2021-05-26 21:16:40 +08:00
|
|
|
return "", fmt.Errorf("failed to generate OpenAPI v3 JSON schema for capability %s: %w", def.Name, err)
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
traitDefinition := def.TraitDefinition
|
|
|
|
|
ownerReference := []metav1.OwnerReference{{
|
|
|
|
|
APIVersion: traitDefinition.APIVersion,
|
|
|
|
|
Kind: traitDefinition.Kind,
|
|
|
|
|
Name: traitDefinition.Name,
|
|
|
|
|
UID: traitDefinition.GetUID(),
|
|
|
|
|
Controller: pointer.BoolPtr(true),
|
|
|
|
|
BlockOwnerDeletion: pointer.BoolPtr(true),
|
|
|
|
|
}}
|
2021-11-30 16:53:01 +08:00
|
|
|
cmName, err := def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, traitDefinition.Name, typeTraitDefinition, traitDefinition.Labels, traitDefinition.Spec.AppliesToWorkloads, jsonSchema, ownerReference)
|
2021-11-09 13:06:55 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return cmName, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a configmap to store parameter for each definitionRevision
|
|
|
|
|
defRev := new(v1beta1.DefinitionRevision)
|
|
|
|
|
if err = k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revName}, defRev); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
ownerReference = []metav1.OwnerReference{{
|
|
|
|
|
APIVersion: defRev.APIVersion,
|
|
|
|
|
Kind: defRev.Kind,
|
|
|
|
|
Name: defRev.Name,
|
|
|
|
|
UID: defRev.GetUID(),
|
|
|
|
|
Controller: pointer.BoolPtr(true),
|
|
|
|
|
BlockOwnerDeletion: pointer.BoolPtr(true),
|
|
|
|
|
}}
|
2021-11-30 16:53:01 +08:00
|
|
|
_, err = def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, revName, typeTraitDefinition, defRev.Spec.TraitDefinition.Labels, defRev.Spec.TraitDefinition.Spec.AppliesToWorkloads, jsonSchema, ownerReference)
|
2021-11-09 13:06:55 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return cmName, err
|
|
|
|
|
}
|
|
|
|
|
return cmName, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CapabilityStepDefinition is the Capability struct for WorkflowStepDefinition
|
|
|
|
|
type CapabilityStepDefinition struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
StepDefinition v1beta1.WorkflowStepDefinition `json:"stepDefinition"`
|
|
|
|
|
|
|
|
|
|
CapabilityBaseDefinition
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewCapabilityStepDef will create a CapabilityStepDefinition
|
|
|
|
|
func NewCapabilityStepDef(stepdefinition *v1beta1.WorkflowStepDefinition) CapabilityStepDefinition {
|
|
|
|
|
var def CapabilityStepDefinition
|
|
|
|
|
def.Name = stepdefinition.Name
|
|
|
|
|
def.StepDefinition = *stepdefinition.DeepCopy()
|
|
|
|
|
return def
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetOpenAPISchema gets OpenAPI v3 schema by StepDefinition name
|
|
|
|
|
func (def *CapabilityStepDefinition) GetOpenAPISchema(pd *packages.PackageDiscover, name string) ([]byte, error) {
|
|
|
|
|
capability, err := appfile.ConvertTemplateJSON2Object(name, nil, def.StepDefinition.Spec.Schematic)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to convert WorkflowStepDefinition to Capability Object")
|
|
|
|
|
}
|
|
|
|
|
return getOpenAPISchema(capability, pd)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StoreOpenAPISchema stores OpenAPI v3 schema from StepDefinition in ConfigMap
|
|
|
|
|
func (def *CapabilityStepDefinition) StoreOpenAPISchema(ctx context.Context, k8sClient client.Client, pd *packages.PackageDiscover, namespace, name string, revName string) (string, error) {
|
|
|
|
|
var jsonSchema []byte
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
jsonSchema, err = def.GetOpenAPISchema(pd, name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("failed to generate OpenAPI v3 JSON schema for capability %s: %w", def.Name, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stepDefinition := def.StepDefinition
|
|
|
|
|
ownerReference := []metav1.OwnerReference{{
|
|
|
|
|
APIVersion: stepDefinition.APIVersion,
|
|
|
|
|
Kind: stepDefinition.Kind,
|
|
|
|
|
Name: stepDefinition.Name,
|
|
|
|
|
UID: stepDefinition.GetUID(),
|
|
|
|
|
Controller: pointer.BoolPtr(true),
|
|
|
|
|
BlockOwnerDeletion: pointer.BoolPtr(true),
|
|
|
|
|
}}
|
2021-11-30 16:53:01 +08:00
|
|
|
cmName, err := def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, stepDefinition.Name, typeWorkflowStepDefinition, stepDefinition.Labels, nil, jsonSchema, ownerReference)
|
2021-03-15 11:08:46 +08:00
|
|
|
if err != nil {
|
2021-05-23 14:16:06 +08:00
|
|
|
return cmName, err
|
2021-03-15 11:08:46 +08:00
|
|
|
}
|
2021-05-23 14:16:06 +08:00
|
|
|
|
2021-08-26 19:56:46 +08:00
|
|
|
// Create a configmap to store parameter for each definitionRevision
|
|
|
|
|
defRev := new(v1beta1.DefinitionRevision)
|
|
|
|
|
if err = k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revName}, defRev); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
ownerReference = []metav1.OwnerReference{{
|
|
|
|
|
APIVersion: defRev.APIVersion,
|
|
|
|
|
Kind: defRev.Kind,
|
|
|
|
|
Name: defRev.Name,
|
|
|
|
|
UID: defRev.GetUID(),
|
|
|
|
|
Controller: pointer.BoolPtr(true),
|
|
|
|
|
BlockOwnerDeletion: pointer.BoolPtr(true),
|
|
|
|
|
}}
|
2021-11-30 16:53:01 +08:00
|
|
|
_, err = def.CreateOrUpdateConfigMap(ctx, k8sClient, namespace, revName, typeWorkflowStepDefinition, defRev.Spec.WorkflowStepDefinition.Labels, nil, jsonSchema, ownerReference)
|
2021-05-23 14:16:06 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return cmName, err
|
|
|
|
|
}
|
|
|
|
|
return cmName, nil
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CapabilityBaseDefinition is the base struct for CapabilityWorkloadDefinition and CapabilityTraitDefinition
|
|
|
|
|
type CapabilityBaseDefinition struct {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CreateOrUpdateConfigMap creates ConfigMap to store OpenAPI v3 schema or or updates data in ConfigMap
|
2021-05-23 14:16:06 +08:00
|
|
|
func (def *CapabilityBaseDefinition) CreateOrUpdateConfigMap(ctx context.Context, k8sClient client.Client, namespace,
|
2021-11-30 16:53:01 +08:00
|
|
|
definitionName, definitionType string, labels map[string]string, appliedWorkloads []string, jsonSchema []byte, ownerReferences []metav1.OwnerReference) (string, error) {
|
2021-11-09 13:06:55 +08:00
|
|
|
cmName := fmt.Sprintf("%s-%s%s", definitionType, types.CapabilityConfigMapNamePrefix, definitionName)
|
2021-03-10 10:44:58 +08:00
|
|
|
var cm v1.ConfigMap
|
|
|
|
|
var data = map[string]string{
|
|
|
|
|
types.OpenapiV3JSONSchema: string(jsonSchema),
|
|
|
|
|
}
|
2021-11-30 16:53:01 +08:00
|
|
|
if labels == nil {
|
|
|
|
|
labels = make(map[string]string)
|
|
|
|
|
}
|
|
|
|
|
labels[types.LabelDefinition] = "schema"
|
|
|
|
|
labels[types.LabelDefinitionName] = definitionName
|
|
|
|
|
annotations := make(map[string]string)
|
|
|
|
|
if appliedWorkloads != nil {
|
|
|
|
|
annotations[types.AnnoDefinitionAppliedWorkloads] = strings.Join(appliedWorkloads, ",")
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 10:44:58 +08:00
|
|
|
// No need to check the existence of namespace, if it doesn't exist, API server will return the error message
|
2021-04-26 10:07:55 +08:00
|
|
|
// before it's to be reconciled by ComponentDefinition/TraitDefinition controller.
|
2021-03-10 10:44:58 +08:00
|
|
|
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: cmName}, &cm)
|
|
|
|
|
if err != nil && apierrors.IsNotFound(err) {
|
|
|
|
|
cm = v1.ConfigMap{
|
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
Kind: "ConfigMap",
|
|
|
|
|
},
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
Name: cmName,
|
|
|
|
|
Namespace: namespace,
|
|
|
|
|
OwnerReferences: ownerReferences,
|
2021-11-30 16:53:01 +08:00
|
|
|
Labels: labels,
|
|
|
|
|
Annotations: annotations,
|
2021-03-10 10:44:58 +08:00
|
|
|
},
|
|
|
|
|
Data: data,
|
|
|
|
|
}
|
|
|
|
|
err = k8sClient.Create(ctx, &cm)
|
|
|
|
|
if err != nil {
|
2021-03-15 11:08:46 +08:00
|
|
|
return cmName, fmt.Errorf(util.ErrUpdateCapabilityInConfigMap, definitionName, err)
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
2021-08-26 19:56:46 +08:00
|
|
|
klog.InfoS("Successfully stored Capability Schema in ConfigMap", "configMap", klog.KRef(namespace, cmName))
|
2021-03-15 11:08:46 +08:00
|
|
|
return cmName, nil
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cm.Data = data
|
2021-11-30 16:53:01 +08:00
|
|
|
cm.Labels = labels
|
|
|
|
|
cm.Annotations = annotations
|
2021-03-10 10:44:58 +08:00
|
|
|
if err = k8sClient.Update(ctx, &cm); err != nil {
|
2021-03-15 11:08:46 +08:00
|
|
|
return cmName, fmt.Errorf(util.ErrUpdateCapabilityInConfigMap, definitionName, err)
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
2021-08-26 19:56:46 +08:00
|
|
|
klog.InfoS("Successfully update Capability Schema in ConfigMap", "configMap", klog.KRef(namespace, cmName))
|
2021-03-15 11:08:46 +08:00
|
|
|
return cmName, nil
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
|
2021-11-09 13:06:55 +08:00
|
|
|
// getOpenAPISchema is the main function for GetDefinition API
|
2021-06-02 15:37:06 +08:00
|
|
|
func getOpenAPISchema(capability types.Capability, pd *packages.PackageDiscover) ([]byte, error) {
|
2021-04-08 20:35:21 +08:00
|
|
|
openAPISchema, err := generateOpenAPISchemaFromCapabilityParameter(capability, pd)
|
2021-03-10 10:44:58 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-03-29 17:20:33 +08:00
|
|
|
schema, err := ConvertOpenAPISchema2SwaggerObject(openAPISchema)
|
2021-03-10 10:44:58 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-11-15 17:17:07 +08:00
|
|
|
FixOpenAPISchema("", schema)
|
2021-03-10 10:44:58 +08:00
|
|
|
|
|
|
|
|
parameter, err := schema.MarshalJSON()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return parameter, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generateOpenAPISchemaFromCapabilityParameter returns the parameter of a definition in cue.Value format
|
2021-06-02 15:37:06 +08:00
|
|
|
func generateOpenAPISchemaFromCapabilityParameter(capability types.Capability, pd *packages.PackageDiscover) ([]byte, error) {
|
2021-11-08 23:13:04 +08:00
|
|
|
template, err := PrepareParameterCue(capability.Name, capability.CueTemplate)
|
2021-03-10 10:44:58 +08:00
|
|
|
if err != nil {
|
2021-06-15 13:36:23 +08:00
|
|
|
if errors.As(err, &ErrNoSectionParameterInCue{}) {
|
|
|
|
|
// return OpenAPI with empty object parameter, making it possible to generate ConfigMap
|
|
|
|
|
var r cue.Runtime
|
|
|
|
|
cueInst, _ := r.Compile("-", "")
|
|
|
|
|
return common.GenOpenAPI(cueInst)
|
|
|
|
|
}
|
2021-03-10 10:44:58 +08:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-04-09 12:28:04 +08:00
|
|
|
|
2021-06-02 15:37:06 +08:00
|
|
|
template += velacue.BaseTemplate
|
2021-04-08 20:35:21 +08:00
|
|
|
if pd == nil {
|
|
|
|
|
var r cue.Runtime
|
2021-04-09 12:28:04 +08:00
|
|
|
cueInst, err := r.Compile("-", template)
|
2021-04-08 20:35:21 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-04-09 12:28:04 +08:00
|
|
|
return common.GenOpenAPI(cueInst)
|
|
|
|
|
}
|
|
|
|
|
bi := build.NewContext().NewInstance("", nil)
|
|
|
|
|
err = bi.AddFile("-", template)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-03-10 10:44:58 +08:00
|
|
|
|
2021-04-09 20:15:02 +08:00
|
|
|
cueInst, err := pd.ImportPackagesAndBuildInstance(bi)
|
2021-04-09 12:28:04 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
return common.GenOpenAPI(cueInst)
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-08 20:35:21 +08:00
|
|
|
// GenerateOpenAPISchemaFromDefinition returns the parameter of a definition
|
|
|
|
|
func GenerateOpenAPISchemaFromDefinition(definitionName, cueTemplate string) ([]byte, error) {
|
|
|
|
|
capability := types.Capability{
|
|
|
|
|
Name: definitionName,
|
|
|
|
|
CueTemplate: cueTemplate,
|
|
|
|
|
}
|
|
|
|
|
return generateOpenAPISchemaFromCapabilityParameter(capability, nil)
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-08 23:13:04 +08:00
|
|
|
// PrepareParameterCue cuts `parameter` section form definition .cue file
|
|
|
|
|
func PrepareParameterCue(capabilityName, capabilityTemplate string) (string, error) {
|
2021-03-10 10:44:58 +08:00
|
|
|
var template string
|
|
|
|
|
var withParameterFlag bool
|
2021-05-19 20:08:51 +08:00
|
|
|
r := regexp.MustCompile(`[[:space:]]*parameter:[[:space:]]*`)
|
|
|
|
|
trimRe := regexp.MustCompile(`\s+`)
|
2021-03-10 10:44:58 +08:00
|
|
|
|
|
|
|
|
for _, text := range strings.Split(capabilityTemplate, "\n") {
|
|
|
|
|
if r.MatchString(text) {
|
|
|
|
|
// a variable has to be refined as a definition which starts with "#"
|
2021-05-19 20:08:51 +08:00
|
|
|
// text may be start with space or tab, we should clean up text
|
|
|
|
|
text = fmt.Sprintf("parameter: #parameter\n#%s", trimRe.ReplaceAllString(text, ""))
|
2021-03-10 10:44:58 +08:00
|
|
|
withParameterFlag = true
|
|
|
|
|
}
|
|
|
|
|
template += fmt.Sprintf("%s\n", text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !withParameterFlag {
|
2021-06-15 13:36:23 +08:00
|
|
|
return "", ErrNoSectionParameterInCue{capName: capabilityName}
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
return template, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-15 17:17:07 +08:00
|
|
|
// FixOpenAPISchema fixes tainted `description` filed, missing of title `field`.
|
|
|
|
|
func FixOpenAPISchema(name string, schema *openapi3.Schema) {
|
2021-03-10 10:44:58 +08:00
|
|
|
t := schema.Type
|
|
|
|
|
switch t {
|
|
|
|
|
case "object":
|
|
|
|
|
for k, v := range schema.Properties {
|
|
|
|
|
s := v.Value
|
2021-11-15 17:17:07 +08:00
|
|
|
FixOpenAPISchema(k, s)
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
case "array":
|
2021-08-02 13:04:46 +08:00
|
|
|
if schema.Items != nil {
|
2021-11-15 17:17:07 +08:00
|
|
|
FixOpenAPISchema("", schema.Items.Value)
|
2021-08-02 13:04:46 +08:00
|
|
|
}
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
|
|
|
|
if name != "" {
|
|
|
|
|
schema.Title = name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
description := schema.Description
|
2021-04-11 14:10:16 +08:00
|
|
|
if strings.Contains(description, appfile.UsageTag) {
|
|
|
|
|
description = strings.Split(description, appfile.UsageTag)[1]
|
2021-03-10 10:44:58 +08:00
|
|
|
}
|
2021-04-11 14:10:16 +08:00
|
|
|
if strings.Contains(description, appfile.ShortTag) {
|
|
|
|
|
description = strings.Split(description, appfile.ShortTag)[0]
|
2021-03-10 10:44:58 +08:00
|
|
|
description = strings.TrimSpace(description)
|
|
|
|
|
}
|
|
|
|
|
schema.Description = description
|
|
|
|
|
}
|
2021-03-29 17:20:33 +08:00
|
|
|
|
|
|
|
|
// ConvertOpenAPISchema2SwaggerObject converts OpenAPI v2 JSON schema to Swagger Object
|
|
|
|
|
func ConvertOpenAPISchema2SwaggerObject(data []byte) (*openapi3.Schema, error) {
|
|
|
|
|
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-30 11:43:20 +08:00
|
|
|
schemaRef, ok := swagger.Components.Schemas[model.ParameterFieldName]
|
2021-03-29 17:20:33 +08:00
|
|
|
if !ok {
|
|
|
|
|
return nil, errors.New(util.ErrGenerateOpenAPIV2JSONSchemaForCapability)
|
|
|
|
|
}
|
|
|
|
|
return schemaRef.Value, nil
|
|
|
|
|
}
|