Update samples for "vela show xxx --web" (#1616)

* Update samples for "vela show xxx --web"

Also stop generating components/traits docs under
docs/en/end-user and remove the command from Makefile

Fix #1602

* fix doc build issue

* Update references/plugins/references.go

Co-authored-by: Jianbo Sun <wonderflow.sun@gmail.com>

* Enhance --web feature for "vela show"

- Find the capability in "vela-system" if notfound in the current ns
- Don't depend on local capabilities

* add ut for GetCapabilityByName in references/plugins/cluster.go

* add ut for GetNamespacedCapabilitiesFromCluster in references/plugins/cluster.go

* add ut for GenerateTerraformCapabilityProperties in references/plugins/references.go

* fix import issue

Co-authored-by: Jianbo Sun <wonderflow.sun@gmail.com>
This commit is contained in:
Zheng Xi Zhou
2021-05-17 11:04:06 +08:00
committed by yangsoon
co-authored by Jianbo Sun
parent 2fdd33a80a
commit e01ff21d30
10 changed files with 637 additions and 296 deletions
+5 -7
View File
@@ -80,7 +80,7 @@ func NewCapabilityShowCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra
return err
}
if webSite {
return startReferenceDocsSite(ctx, c, ioStreams, capabilityName)
return startReferenceDocsSite(ctx, velaEnv.Namespace, c, ioStreams, capabilityName)
}
return ShowReferenceConsole(ctx, c, ioStreams, capabilityName, velaEnv.Namespace)
},
@@ -94,7 +94,7 @@ func NewCapabilityShowCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra
return cmd
}
func startReferenceDocsSite(ctx context.Context, c common.Args, ioStreams cmdutil.IOStreams, capabilityName string) error {
func startReferenceDocsSite(ctx context.Context, ns string, c common.Args, ioStreams cmdutil.IOStreams, capabilityName string) error {
home, err := system.GetVelaHomeDir()
if err != nil {
return err
@@ -113,13 +113,11 @@ func startReferenceDocsSite(ctx context.Context, c common.Args, ioStreams cmduti
return err
}
}
capabilities, _, err := plugins.SyncDefinitionsToLocal(ctx, c, definitionPath)
capabilities, err := plugins.GetNamespacedCapabilitiesFromCluster(ctx, ns, c, nil)
if err != nil {
return err
}
// check input capability is valid
// check whether input capability is valid
var capabilityIsValid bool
var capabilityType types.CapType
for _, c := range capabilities {
@@ -378,7 +376,7 @@ func ShowReferenceConsole(ctx context.Context, c common.Args, ioStreams cmdutil.
return err
}
case types.TerraformCategory:
propertyConsole, err = ref.GenerateTerraformCapabilityProperties(capability)
propertyConsole, err = ref.GenerateTerraformCapabilityProperties(*capability)
if err != nil {
return err
}
+99 -66
View File
@@ -57,16 +57,46 @@ func GetCapabilitiesFromCluster(ctx context.Context, namespace string, c common.
return workloads, nil
}
// GetNamespacedCapabilitiesFromCluster will get capability from K8s cluster in the specified namespace and default namespace
// If the definition could be found from `namespace`, try to find in namespace `types.DefaultKubeVelaNS`
func GetNamespacedCapabilitiesFromCluster(ctx context.Context, namespace string, c common.Args, selector labels.Selector) ([]types.Capability, error) {
var capabilities []types.Capability
if workloads, _, err := GetComponentsFromClusterWithValidateOption(ctx, namespace, c, selector, false); err == nil {
capabilities = append(capabilities, workloads...)
}
if traits, _, err := GetTraitsFromClusterWithValidateOption(ctx, namespace, c, selector, false); err == nil {
capabilities = append(capabilities, traits...)
}
// get components from default namespace
if workloads, _, err := GetComponentsFromClusterWithValidateOption(ctx, types.DefaultKubeVelaNS, c, selector, false); err == nil {
capabilities = append(capabilities, workloads...)
}
// get traits from default namespace
if traits, _, err := GetTraitsFromClusterWithValidateOption(ctx, types.DefaultKubeVelaNS, c, selector, false); err == nil {
capabilities = append(capabilities, traits...)
}
if len(capabilities) > 0 {
return capabilities, nil
}
return nil, fmt.Errorf("could not find any components or traits from namespace %s and %s", namespace, types.DefaultKubeVelaNS)
}
// GetComponentsFromCluster will get capability from K8s cluster
func GetComponentsFromCluster(ctx context.Context, namespace string, c common.Args, selector labels.Selector) ([]types.Capability, []error, error) {
return GetComponentsFromClusterWithValidateOption(ctx, namespace, c, selector, true)
}
// GetComponentsFromClusterWithValidateOption will get capability from K8s cluster with an option whether to valid Components
func GetComponentsFromClusterWithValidateOption(ctx context.Context, namespace string, c common.Args, selector labels.Selector, validateFlag bool) ([]types.Capability, []error, error) {
newClient, err := c.GetClient()
if err != nil {
return nil, nil, err
}
dm, err := discoverymapper.New(c.Config)
if err != nil {
return nil, nil, err
}
var templates []types.Capability
var componentsDefs v1beta1.ComponentDefinitionList
@@ -77,27 +107,37 @@ func GetComponentsFromCluster(ctx context.Context, namespace string, c common.Ar
var templateErrors []error
for _, cd := range componentsDefs.Items {
ref, err := util.ConvertWorkloadGVK2Definition(dm, cd.Spec.Workload.Definition)
dm, err := c.GetDiscoveryMapper()
if err != nil {
templateErrors = append(templateErrors, errors.Wrapf(err, "convert workload definition `%s` failed", cd.Name))
continue
}
tmp, err := HandleDefinition(cd.Name, ref.Name, cd.Annotations, cd.Spec.Extension, types.TypeComponentDefinition, nil, cd.Spec.Schematic)
if err != nil {
templateErrors = append(templateErrors, errors.Wrapf(err, "handle workload template `%s` failed", cd.Name))
continue
}
tmp.Namespace = namespace
if tmp, err = validateCapabilities(tmp, dm, cd.Name, ref); err != nil {
return nil, nil, err
}
templates = append(templates, tmp)
ref, err := util.ConvertWorkloadGVK2Definition(dm, cd.Spec.Workload.Definition)
if err != nil {
return nil, nil, err
}
tmp, err := GetCapabilityByComponentDefinitionObject(cd, ref.Name)
if err != nil {
templateErrors = append(templateErrors, err)
continue
}
if validateFlag {
if err = validateCapabilities(tmp, dm, cd.Name, ref); err != nil {
return nil, nil, err
}
}
templates = append(templates, *tmp)
}
return templates, templateErrors, nil
}
// GetTraitsFromCluster will get capability from K8s cluster
func GetTraitsFromCluster(ctx context.Context, namespace string, c common.Args, selector labels.Selector) ([]types.Capability, []error, error) {
return GetTraitsFromClusterWithValidateOption(ctx, namespace, c, selector, true)
}
// GetTraitsFromClusterWithValidateOption will get capability from K8s cluster with an option whether to valid Traits
func GetTraitsFromClusterWithValidateOption(ctx context.Context, namespace string, c common.Args, selector labels.Selector, validateFlag bool) ([]types.Capability, []error, error) {
newClient, err := c.GetClient()
if err != nil {
return nil, nil, err
@@ -115,28 +155,30 @@ func GetTraitsFromCluster(ctx context.Context, namespace string, c common.Args,
var templateErrors []error
for _, td := range traitDefs.Items {
tmp, err := HandleDefinition(td.Name, td.Spec.Reference.Name, td.Annotations, td.Spec.Extension, types.TypeTrait, td.Spec.AppliesToWorkloads, td.Spec.Schematic)
tmp, err := GetCapabilityByTraitDefinitionObject(td)
if err != nil {
templateErrors = append(templateErrors, errors.Wrapf(err, "handle trait template `%s` failed", td.Name))
continue
}
tmp.Namespace = namespace
if tmp, err = validateCapabilities(tmp, dm, td.Name, td.Spec.Reference); err != nil {
return nil, nil, err
if validateFlag {
if err = validateCapabilities(tmp, dm, td.Name, td.Spec.Reference); err != nil {
return nil, nil, err
}
}
templates = append(templates, tmp)
templates = append(templates, *tmp)
}
return templates, templateErrors, nil
}
// validateCapabilities validates whether helm charts are successful installed, GVK are successfully retrieved.
func validateCapabilities(tmp types.Capability, dm discoverymapper.DiscoveryMapper, definitionName string, reference commontypes.DefinitionReference) (types.Capability, error) {
func validateCapabilities(tmp *types.Capability, dm discoverymapper.DiscoveryMapper, definitionName string, reference commontypes.DefinitionReference) error {
var err error
if tmp.Install != nil {
tmp.Source = &types.Source{ChartName: tmp.Install.Helm.Name}
ioStream := util2.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
if err = helm.InstallHelmChart(ioStream, tmp.Install.Helm); err != nil {
return tmp, fmt.Errorf("unable to install helm chart dependency %s(%s from %s) for this trait '%s': %w ", tmp.Install.Helm.Name, tmp.Install.Helm.Version, tmp.Install.Helm.URL, definitionName, err)
return fmt.Errorf("unable to install helm chart dependency %s(%s from %s) for this trait '%s': %w ", tmp.Install.Helm.Name, tmp.Install.Helm.Version, tmp.Install.Helm.URL, definitionName, err)
}
}
gvk, err := util.GetGVKFromDefinition(dm, reference)
@@ -146,14 +188,14 @@ func validateCapabilities(tmp types.Capability, dm discoverymapper.DiscoveryMapp
if strings.Contains(errMsg, substr) {
err = fmt.Errorf("expected provider: %s", strings.Split(errMsg, substr)[1])
}
return tmp, fmt.Errorf("installing capability '%s'... %w", definitionName, err)
return fmt.Errorf("installing capability '%s'... %w", definitionName, err)
}
tmp.CrdInfo = &types.CRDInfo{
APIVersion: gvk.GroupVersion().String(),
Kind: gvk.Kind,
}
return tmp, nil
return nil
}
// HandleDefinition will handle definition to capability
@@ -226,42 +268,11 @@ func HandleTemplate(in *runtime.RawExtension, schematic *commontypes.Schematic,
return tmp, nil
}
// SyncDefinitionsToLocal sync definitions to local
func SyncDefinitionsToLocal(ctx context.Context, c common.Args, localDefinitionDir string) ([]types.Capability, []string, error) {
var syncedTemplates []types.Capability
var warnings []string
templates, templateErrors, err := GetComponentsFromCluster(ctx, types.DefaultKubeVelaNS, c, nil)
if err != nil {
return nil, nil, err
}
if len(templateErrors) > 0 {
for _, e := range templateErrors {
warnings = append(warnings, fmt.Sprintf("WARN: %v, you will unable to use this component capability\n", e))
}
}
syncedTemplates = append(syncedTemplates, templates...)
SinkTemp2Local(templates, localDefinitionDir)
templates, templateErrors, err = GetTraitsFromCluster(ctx, types.DefaultKubeVelaNS, c, nil)
if err != nil {
return nil, warnings, err
}
if len(templateErrors) > 0 {
for _, e := range templateErrors {
warnings = append(warnings, fmt.Sprintf("WARN: %v, you will unable to use this trait capability\n", e))
}
}
syncedTemplates = append(syncedTemplates, templates...)
SinkTemp2Local(templates, localDefinitionDir)
return syncedTemplates, warnings, nil
}
// GetCapabilityByName gets capability by definition name
func GetCapabilityByName(ctx context.Context, c common.Args, capabilityName string, ns string) (*types.Capability, error) {
var (
foundCapability bool
template types.Capability
capability *types.Capability
err error
)
@@ -289,13 +300,11 @@ func GetCapabilityByName(ctx context.Context, c common.Args, capabilityName stri
if err != nil {
return nil, err
}
template, err = HandleDefinition(capabilityName, ref.Name,
componentDef.Annotations, componentDef.Spec.Extension, types.TypeComponentDefinition, nil, componentDef.Spec.Schematic)
capability, err = GetCapabilityByComponentDefinitionObject(componentDef, ref.Name)
if err != nil {
return nil, errors.Wrap(err, "failed to handle ComponentDefinition")
return nil, err
}
template.Namespace = componentDef.Namespace
return &template, nil
return capability, nil
}
foundCapability = false
@@ -310,13 +319,37 @@ func GetCapabilityByName(ctx context.Context, c common.Args, capabilityName stri
}
}
if foundCapability {
template, err = HandleDefinition(capabilityName, traitDef.Spec.Reference.Name,
traitDef.Annotations, traitDef.Spec.Extension, types.TypeTrait, nil, traitDef.Spec.Schematic)
capability, err = GetCapabilityByTraitDefinitionObject(traitDef)
if err != nil {
return nil, errors.Wrap(err, "failed to handle TraitDefinition")
return nil, err
}
template.Namespace = traitDef.Namespace
return &template, nil
return capability, nil
}
return nil, fmt.Errorf("cloud not find %s is namespace %s, or %s", capabilityName, ns, types.DefaultKubeVelaNS)
}
// GetCapabilityByComponentDefinitionObject gets capability by ComponentDefinition object
func GetCapabilityByComponentDefinitionObject(componentDef v1beta1.ComponentDefinition, referenceName string) (*types.Capability, error) {
capability, err := HandleDefinition(componentDef.Name, referenceName,
componentDef.Annotations, componentDef.Spec.Extension, types.TypeComponentDefinition, nil, componentDef.Spec.Schematic)
if err != nil {
return nil, errors.Wrap(err, "failed to handle ComponentDefinition")
}
capability.Namespace = componentDef.Namespace
return &capability, nil
}
// GetCapabilityByTraitDefinitionObject gets capability by TraitDefinition object
func GetCapabilityByTraitDefinitionObject(traitDef v1beta1.TraitDefinition) (*types.Capability, error) {
var (
capability types.Capability
err error
)
capability, err = HandleDefinition(traitDef.Name, traitDef.Spec.Reference.Name,
traitDef.Annotations, traitDef.Spec.Extension, types.TypeTrait, nil, traitDef.Spec.Schematic)
if err != nil {
return nil, errors.Wrap(err, "failed to handle TraitDefinition")
}
capability.Namespace = traitDef.Namespace
return &capability, nil
}
+168 -4
View File
@@ -19,23 +19,27 @@ package plugins
import (
"context"
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"io/ioutil"
"cuelang.org/go/cue"
"github.com/ghodss/yaml"
"github.com/google/go-cmp/cmp"
"github.com/oam-dev/kubevela/pkg/oam/util"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
logf "sigs.k8s.io/controller-runtime/pkg/log"
corev1beta1 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
)
const (
TestDir = "testdata"
RouteName = "routes.test"
DeployName = "deployments.testapps"
WebserviceName = "webservice.testapps"
)
@@ -132,3 +136,163 @@ var _ = Describe("DefinitionFiles", func() {
Expect(cmp.Diff(alldef, []types.Capability{deployment, websvc})).Should(BeEquivalentTo(""))
})
})
var _ = Describe("test GetCapabilityByName", func() {
var (
ctx context.Context
c common.Args
ns string
defaultNS string
cd1 corev1beta1.ComponentDefinition
cd2 corev1beta1.ComponentDefinition
td1 corev1beta1.TraitDefinition
td2 corev1beta1.TraitDefinition
component1 string
component2 string
trait1 string
trait2 string
)
BeforeEach(func() {
c = common.Args{
Client: k8sClient,
Config: cfg,
Schema: scheme,
}
ctx = context.Background()
ns = "cluster-test-ns"
defaultNS = types.DefaultKubeVelaNS
component1 = "cd1"
component2 = "cd2"
trait1 = "td1"
trait2 = "td2"
By("create namespace")
Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}})).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: defaultNS}})).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
By("create ComponentDefinition")
data, _ := ioutil.ReadFile("testdata/componentDef.yaml")
yaml.Unmarshal(data, &cd1)
yaml.Unmarshal(data, &cd2)
cd1.Namespace = ns
cd1.Name = component1
Expect(k8sClient.Create(ctx, &cd1)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
cd2.Namespace = defaultNS
cd2.Name = component2
Expect(k8sClient.Create(ctx, &cd2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
By("create TraitDefinition")
data, _ = ioutil.ReadFile("testdata/manualscalars.yaml")
yaml.Unmarshal(data, &td1)
yaml.Unmarshal(data, &td2)
td1.Namespace = ns
td1.Name = trait1
Expect(k8sClient.Create(ctx, &td1)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
td2.Namespace = defaultNS
td2.Name = trait2
Expect(k8sClient.Create(ctx, &td2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
})
It("get capability", func() {
Context("ComponentDefinition is in the current namespace", func() {
_, err := GetCapabilityByName(ctx, c, component1, ns)
Expect(err).Should(BeNil())
})
Context("ComponentDefinition is in the default namespace", func() {
_, err := GetCapabilityByName(ctx, c, component2, ns)
Expect(err).Should(BeNil())
})
Context("TraitDefinition is in the current namespace", func() {
_, err := GetCapabilityByName(ctx, c, trait1, ns)
Expect(err).Should(BeNil())
})
Context("TraitDefinitionDefinition is in the default namespace", func() {
_, err := GetCapabilityByName(ctx, c, trait2, ns)
Expect(err).Should(BeNil())
})
Context("capability cloud not be found", func() {
_, err := GetCapabilityByName(ctx, c, "a-component-definition-not-existed", ns)
Expect(err).Should(HaveOccurred())
})
})
})
var _ = Describe("test GetNamespacedCapabilitiesFromCluster", func() {
var (
ctx context.Context
c common.Args
ns string
defaultNS string
cd1 corev1beta1.ComponentDefinition
cd2 corev1beta1.ComponentDefinition
td1 corev1beta1.TraitDefinition
td2 corev1beta1.TraitDefinition
component1 string
component2 string
trait1 string
trait2 string
)
BeforeEach(func() {
c = common.Args{
Client: k8sClient,
Config: cfg,
Schema: scheme,
}
ctx = context.Background()
ns = "cluster-test-ns"
defaultNS = types.DefaultKubeVelaNS
component1 = "cd1"
component2 = "cd2"
trait1 = "td1"
trait2 = "td2"
By("create namespace")
Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}})).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: defaultNS}})).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
By("create ComponentDefinition")
data, _ := ioutil.ReadFile("testdata/componentDef.yaml")
yaml.Unmarshal(data, &cd1)
yaml.Unmarshal(data, &cd2)
cd1.Namespace = ns
cd1.Name = component1
Expect(k8sClient.Create(ctx, &cd1)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
cd2.Namespace = defaultNS
cd2.Name = component2
Expect(k8sClient.Create(ctx, &cd2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
By("create TraitDefinition")
data, _ = ioutil.ReadFile("testdata/manualscalars.yaml")
yaml.Unmarshal(data, &td1)
yaml.Unmarshal(data, &td2)
td1.Namespace = ns
td1.Name = trait1
Expect(k8sClient.Create(ctx, &td1)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
td2.Namespace = defaultNS
td2.Name = trait2
Expect(k8sClient.Create(ctx, &td2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
})
It("get namespaced capabilities", func() {
Context("found all capabilities", func() {
capabilities, err := GetNamespacedCapabilitiesFromCluster(ctx, ns, c, nil)
Expect(len(capabilities)).Should(Equal(4))
Expect(err).Should(BeNil())
})
Context("found two capabilities with a bad namespace", func() {
capabilities, err := GetNamespacedCapabilitiesFromCluster(ctx, "a-bad-ns", c, nil)
Expect(len(capabilities)).Should(Equal(2))
Expect(err).Should(BeNil())
})
})
})
+33 -5
View File
@@ -46,6 +46,7 @@ func TestCreateMarkdown(t *testing.T) {
workloadName := "workload1"
traitName := "trait1"
scopeName := "scope1"
workloadName2 := "workload2"
workloadCueTemplate := `
parameter: {
@@ -58,6 +59,29 @@ parameter: {
parameter: {
replicas: int
}
`
configuration := `
resource "alicloud_oss_bucket" "bucket-acl" {
bucket = var.bucket
acl = var.acl
}
output "BUCKET_NAME" {
value = "${alicloud_oss_bucket.bucket-acl.bucket}.${alicloud_oss_bucket.bucket-acl.extranet_endpoint}"
}
variable "bucket" {
description = "OSS bucket name"
default = "vela-website"
type = string
}
variable "acl" {
description = "OSS bucket ACL, supported 'private', 'public-read', 'public-read-write'"
default = "private"
type = string
}
`
cases := map[string]struct {
@@ -80,6 +104,12 @@ parameter: {
CueTemplate: traitCueTemplate,
Category: types.CUECategory,
},
{
Name: workloadName2,
TerraformConfiguration: configuration,
Type: types.TypeWorkload,
Category: types.TerraformCategory,
},
},
want: nil,
},
@@ -110,11 +140,9 @@ parameter: {
func TestPrepareParameterTable(t *testing.T) {
ref := MarkdownReference{}
tableName := "hello"
var depth int = 1
parameterList := []ReferenceParameter{
{
PrintableType: "string",
Depth: &depth,
},
}
parameterName := "cpu"
@@ -329,7 +357,7 @@ func TestWalkParameterSchema(t *testing.T) {
func TestGenerateTerraformCapabilityProperties(t *testing.T) {
ref := &ConsoleReference{}
type args struct {
cap *types.Capability
cap types.Capability
}
type want struct {
@@ -343,7 +371,7 @@ func TestGenerateTerraformCapabilityProperties(t *testing.T) {
}{
"normal": {
args: args{
cap: &types.Capability{
cap: types.Capability{
TerraformConfiguration: `
resource "alicloud_oss_bucket" "bucket-acl" {
bucket = var.bucket
@@ -376,7 +404,7 @@ variable "acl" {
},
"configuration is not valid": {
args: args{
cap: &types.Capability{
cap: types.Capability{
TerraformConfiguration: `abc`,
},
},
+330 -167
View File
@@ -42,11 +42,11 @@ import (
const (
// BaseRefPath is the target path for reference docs
BaseRefPath = "docs/en/developers/references"
BaseRefPath = "docs/en/end-user"
// ReferenceSourcePath is the location for source reference
ReferenceSourcePath = "hack/references"
// ComponentDefinitionTypePath is the URL path for component typed capability
ComponentDefinitionTypePath = "component-types"
ComponentDefinitionTypePath = "components"
// WorkloadTypePath is the URL path for workload typed capability
WorkloadTypePath = "workload-types"
// TraitPath is the URL path for trait typed capability
@@ -54,12 +54,8 @@ const (
)
const (
// TerraformVariableName is the name for Terraform Variable
TerraformVariableName = "variable"
// TerraformWriteConnectionSecretToRefName is the name for Terraform WriteConnectionSecretToRef
TerraformWriteConnectionSecretToRefName = "writeConnectionSecretToRef"
// TerraformVariableType is the type for Terraform Variable
TerraformVariableType = "[variable](#variable)"
// TerraformWriteConnectionSecretToRefType is the type for Terraform WriteConnectionSecretToRef
TerraformWriteConnectionSecretToRefType = "[writeConnectionSecretToRef](#writeConnectionSecretToRef)"
)
@@ -97,106 +93,224 @@ type ConsoleReference struct {
// ConfigurationYamlSample stores the configuration yaml sample for capabilities
var ConfigurationYamlSample = map[string]string{
"autoscale": `
name: testapp
services:
express-server:
...
autoscale:
min: 1
max: 4
cron:
startAt: "14:00"
duration: "2h"
days: "Monday, Thursday"
replicas: 2
timezone: "America/Los_Angeles"
cpuPercent: 10
"annotations": `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: myapp
spec:
components:
- name: express-server
type: webservice
properties:
image: crccheck/hello-world
port: 8000
traits:
- type: labels
properties:
"release": "stable"
- type: annotations
properties:
"description": "web application"
`,
"ingress": `
kind: Application
metadata:
name: first-vela-app
spec:
components:
- name: express-server
type: webservice
properties:
image: crccheck/hello-world
port: 8000
traits:
- type: ingress
properties:
domain: testsvc.example.com
http:
"/": 8000
`,
"labels": `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: myapp
spec:
components:
- name: express-server
type: webservice
properties:
image: crccheck/hello-world
port: 8000
traits:
- type: labels
properties:
"release": "stable"
- type: annotations
properties:
"description": "web application"
`,
"metrics": `
name: my-app-name
services:
my-service-name:
...
metrics:
format: "prometheus"
port: 8080
path: "/metrics"
scheme: "http"
enabled: true
`,
"rollout": `
servcies:
express-server:
...
rollout:
replicas: 2
stepWeight: 50
interval: "10s"
`,
"route": `
name: my-app-name
services:
my-service-name:
...
route:
domain: example.com
issuer: tls
rules:
- path: /testapp
rewriteTarget: /
`,
"scaler": `
name: my-app-name
services:
my-service-name:
...
scaler:
replicas: 100
`,
"task": `
name: my-app-name
services:
my-service-name:
type: task
image: perl
count: 10
cmd: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
`,
"webservice": `
name: my-app-name
services:
my-service-name:
type: webservice # could be skipped
image: oamdev/testapp:v1
cmd: ["node", "server.js"]
...
format: "prometheus"
port: 8080
cpu: "0.1"
env:
- name: FOO
value: bar
- name: FOO
valueFrom:
secretKeyRef:
name: bar
key: bar
path: "/metrics"
scheme: "http"
enabled: true
`,
"route": `
...
domain: example.com
issuer: tls
rules:
- path: /testapp
rewriteTarget: /
`,
"scaler": `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: website
spec:
components:
- name: frontend
type: webservice
properties:
image: nginx
traits:
- type: scaler
properties:
replicas: 2
- type: sidecar
properties:
name: "sidecar-test"
image: "fluentd"
- name: backend
type: worker
properties:
image: busybox
cmd:
- sleep
- '1000'
`,
"sidecar": `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: vela-app-with-sidecar
spec:
components:
- name: log-gen-worker
type: worker
properties:
image: busybox
cmd:
- /bin/sh
- -c
- >
i=0;
while true;
do
echo "$i: $(date)" >> /var/log/date.log;
i=$((i+1));
sleep 1;
done
volumes:
- name: varlog
mountPath: /var/log
type: emptyDir
traits:
- type: sidecar
properties:
name: count-log
image: busybox
cmd: [ /bin/sh, -c, 'tail -n+1 -f /var/log/date.log']
volumes:
- name: varlog
path: /var/log
`,
"task": `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: app-worker
spec:
components:
- name: mytask
type: task
properties:
image: perl
count: 10
cmd: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
`,
"volumes": `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: app-worker
spec:
components:
- name: myworker
type: worker
properties:
image: "busybox"
cmd:
- sleep
- "1000"
traits:
- type: aws-ebs-volume
properties:
name: "my-ebs"
mountPath: "/myebs"
volumeID: "my-ebs-id"
`,
"webservice": `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: website
spec:
components:
- name: frontend
type: webservice
properties:
image: oamdev/testapp:v1
cmd: ["node", "server.js"]
port: 8080
cpu: "0.1"
env:
- name: FOO
value: bar
- name: FOO
valueFrom:
secretKeyRef:
name: bar
key: bar
`,
"worker": `
name: my-app-name
services:
my-service-name:
type: worker
image: oamdev/testapp:v1
cmd: ["node", "server.js"]
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: app-worker
spec:
components:
- name: myworker
type: worker
properties:
image: "busybox"
cmd:
- sleep
- "1000"
`,
}
@@ -220,8 +334,13 @@ type ReferenceParameter struct {
types.Parameter `json:",inline,omitempty"`
// PrintableType is same to `parameter.Type` which could be printable
PrintableType string `json:"printableType"`
// Depth marks the depth for calling of function `parseParameters`
Depth *int `json:"depth"`
}
// ReferenceParameterTable stores the information of a bunch of ReferenceParameter in a table style
type ReferenceParameterTable struct {
Name string
Parameters []ReferenceParameter
Depth *int
}
var refContent string
@@ -254,19 +373,14 @@ func (ref *MarkdownReference) GenerateReferenceDocs(ctx context.Context, baseRef
func (ref *MarkdownReference) CreateMarkdown(ctx context.Context, caps []types.Capability, baseRefPath, referenceSourcePath string) error {
setDisplayFormat("markdown")
var capabilityType string
var specificationType string
for i, c := range caps {
switch c.Type {
case types.TypeWorkload:
capabilityType = WorkloadTypePath
specificationType = "workload type"
case types.TypeComponentDefinition:
capabilityType = ComponentDefinitionTypePath
specificationType = "component type"
case types.TypeTrait:
capabilityType = TraitPath
specificationType = "trait"
default:
return fmt.Errorf("the type of the capability is not right")
}
@@ -309,20 +423,28 @@ func (ref *MarkdownReference) CreateMarkdown(ctx context.Context, caps []types.C
for _, property := range properties {
refContent += ref.prepareParameter("#"+property.Name, property.Parameters, types.HelmCategory)
}
case types.TerraformCategory:
refContent, err = ref.GenerateTerraformCapabilityProperties(c)
if err != nil {
return err
}
default:
return fmt.Errorf("unsupport capability category %s", c.Category)
}
title := fmt.Sprintf("%s\n===============", capNameInTitle)
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)
specification := fmt.Sprintf("\n\n## Specification\n\n%s\n\n%s", specificationIntro, specificationContent)
description := fmt.Sprintf("\n\n# Description\n\n%s", c.Description)
var sample string
sampleContent := ref.generateSample(capName)
if sampleContent != "" {
sample = fmt.Sprintf("\n\n# Samples\n\n%s", sampleContent)
}
specification := fmt.Sprintf("\n\n# Specification\n%s", refContent)
// it's fine if the conflict info files not found
conflictWithAndMoreSection, _ := ref.generateConflictWithAndMore(capName, referenceSourcePath)
refContent = title + description + specification + refContent + conflictWithAndMoreSection
refContent = title + description + sample + conflictWithAndMoreSection + specification
if _, err := f.WriteString(refContent); err != nil {
return err
}
@@ -349,6 +471,11 @@ func (ref *MarkdownReference) prepareParameter(tableName string, 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)
}
case types.TerraformCategory:
// Terraform doesn't have default value
for _, p := range parameterList {
refContent += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, strings.ReplaceAll(p.Usage, "\n", ""), p.PrintableType, p.Required, "")
}
default:
}
return refContent
@@ -375,7 +502,6 @@ func (ref *ParseReference) prepareParameter(tableName string, parameterList []Re
for _, p := range parameterList {
table.Append([]string{p.Name, p.Usage, p.PrintableType, strconv.FormatBool(p.Required), ""})
}
default:
}
@@ -510,9 +636,13 @@ func (ref *ParseReference) getHELMPrintableDefaultValue(dataType string, value i
return defaultValueMap[dataType]
}
// generateSpecification generates Specification part for reference docs
func (ref *MarkdownReference) generateSpecification(capabilityName string) string {
return fmt.Sprintf("```yaml%s```", ConfigurationYamlSample[capabilityName])
// generateSample generates Specification part for reference docs
func (ref *MarkdownReference) generateSample(capabilityName string) string {
// TODO(zzxwill): we should generate the sample automatically instead of maintain hardcode example.
if _, ok := ConfigurationYamlSample[capabilityName]; ok {
return fmt.Sprintf("```yaml%s```", ConfigurationYamlSample[capabilityName])
}
return ""
}
// generateConflictWithAndMore generates Section `Conflicts With` and more like `How xxx works` in reference docs
@@ -586,6 +716,66 @@ func (ref *ParseReference) GenerateHELMProperties(ctx context.Context, capabilit
return helmRefs, consoleRefs, err
}
// GenerateTerraformCapabilityProperties generates Capability properties for Terraform ComponentDefinition
func (ref *ParseReference) parseTerraformCapabilityParameters(capability types.Capability) ([]ReferenceParameterTable, error) {
var (
tables []ReferenceParameterTable
refParameterList []ReferenceParameter
writeConnectionSecretToRefReferenceParameter ReferenceParameter
)
writeConnectionSecretToRefReferenceParameter.Name = TerraformWriteConnectionSecretToRefName
writeConnectionSecretToRefReferenceParameter.PrintableType = TerraformWriteConnectionSecretToRefType
writeConnectionSecretToRefReferenceParameter.Required = false
writeConnectionSecretToRefReferenceParameter.Usage = "The secret which the cloud resource connection will be written to"
variables, err := ref.parseTerraformVariables(capability.TerraformConfiguration)
if err != nil {
return nil, errors.Wrap(err, "failed to generate capability properties")
}
for _, v := range variables {
var refParam ReferenceParameter
refParam.Name = v.Name
refParam.PrintableType = v.Type
refParam.Usage = v.Description
refParam.Required = true
refParameterList = append(refParameterList, refParam)
}
refParameterList = append(refParameterList, writeConnectionSecretToRefReferenceParameter)
propertiesTableName := fmt.Sprintf("%s %s", strings.Repeat("#", 1), "Properties")
tables = append(tables, ReferenceParameterTable{
Name: propertiesTableName,
Parameters: refParameterList,
})
var (
writeSecretRefNameParam ReferenceParameter
writeSecretRefNameSpaceParam ReferenceParameter
)
// prepare `## writeConnectionSecretToRef`
writeSecretRefNameParam.Name = "name"
writeSecretRefNameParam.PrintableType = "string"
writeSecretRefNameParam.Required = true
writeSecretRefNameParam.Usage = "The secret name which the cloud resource connection will be written to"
writeSecretRefNameSpaceParam.Name = "namespace"
writeSecretRefNameSpaceParam.PrintableType = "string"
writeSecretRefNameSpaceParam.Required = false
writeSecretRefNameSpaceParam.Usage = "The secret namespace which the cloud resource connection will be written to"
writeSecretRefParameterList := []ReferenceParameter{writeSecretRefNameParam, writeSecretRefNameSpaceParam}
writeSecretTableName := fmt.Sprintf("%s %s", strings.Repeat("#", 2), TerraformWriteConnectionSecretToRefName)
tables = append(tables, ReferenceParameterTable{
Name: writeSecretTableName,
Parameters: writeSecretRefParameterList,
})
return tables, nil
}
// WalkParameterSchema will extract properties from *openapi3.Schema
func WalkParameterSchema(parameters *openapi3.Schema, name string, depth int) {
if parameters == nil {
@@ -634,57 +824,30 @@ func WalkParameterSchema(parameters *openapi3.Schema, name string, depth int) {
}
}
// GenerateTerraformCapabilityProperties generates Capability properties for Terraform ComponentDefinition
// application developers need to set two sections `variable` and `writeConnectionSecretToRef` in an application
func (ref *ConsoleReference) GenerateTerraformCapabilityProperties(capability *types.Capability) ([]ConsoleReference, error) {
var (
writeConnectionSecretToRefReferenceParameter ReferenceParameter
propertiesConsoleReference ConsoleReference
writeSecretConsoleReference ConsoleReference
)
// GenerateTerraformCapabilityProperties generates Capability properties for Terraform ComponentDefinition in Cli console
func (ref *ConsoleReference) GenerateTerraformCapabilityProperties(capability types.Capability) ([]ConsoleReference, error) {
var references []ConsoleReference
writeConnectionSecretToRefReferenceParameter.Name = TerraformWriteConnectionSecretToRefName
writeConnectionSecretToRefReferenceParameter.PrintableType = TerraformWriteConnectionSecretToRefType
writeConnectionSecretToRefReferenceParameter.Required = false
writeConnectionSecretToRefReferenceParameter.Usage = "The secret which the cloud resource connection will be written to"
variables, err := ref.parseTerraformVariables(capability.TerraformConfiguration)
tables, err := ref.parseTerraformCapabilityParameters(capability)
if err != nil {
return nil, errors.Wrap(err, "failed to generate capability properties")
return nil, err
}
var refParameterList []ReferenceParameter
for _, v := range variables {
var refParam ReferenceParameter
refParam.Name = v.Name
refParam.PrintableType = v.Type
refParam.Usage = v.Description
refParam.Required = true
refParameterList = append(refParameterList, refParam)
for _, t := range tables {
references = append(references, ref.prepareParameter(t.Name, t.Parameters, types.TerraformCategory))
}
refParameterList = append(refParameterList, writeConnectionSecretToRefReferenceParameter)
propertiesTableName := fmt.Sprintf("%s %s", strings.Repeat("#", 1), "Properties")
propertiesConsoleReference = ref.prepareParameter(propertiesTableName, refParameterList, types.CUECategory)
var (
writeSecretRefNameParam ReferenceParameter
writeSecretRefNameSpaceParam ReferenceParameter
)
// prepare `## writeConnectionSecretToRef`
writeSecretRefNameParam.Name = "name"
writeSecretRefNameParam.PrintableType = "string"
writeSecretRefNameParam.Required = true
writeSecretRefNameParam.Usage = "The secret name which the cloud resource connection will be written to"
writeSecretRefNameSpaceParam.Name = "namespace"
writeSecretRefNameSpaceParam.PrintableType = "string"
writeSecretRefNameSpaceParam.Required = false
writeSecretRefNameSpaceParam.Usage = "The secret namespace which the cloud resource connection will be written to"
writeSecretRefParameterList := []ReferenceParameter{writeSecretRefNameParam, writeSecretRefNameSpaceParam}
writeSecretTableName := fmt.Sprintf("%s %s", strings.Repeat("#", 2), TerraformWriteConnectionSecretToRefName)
writeSecretConsoleReference = ref.prepareParameter(writeSecretTableName, writeSecretRefParameterList, types.CUECategory)
return []ConsoleReference{propertiesConsoleReference, writeSecretConsoleReference}, nil
return references, nil
}
// GenerateTerraformCapabilityProperties generates Capability properties for Terraform ComponentDefinition in a local website
func (ref *MarkdownReference) GenerateTerraformCapabilityProperties(capability types.Capability) (string, error) {
var references string
tables, err := ref.parseTerraformCapabilityParameters(capability)
if err != nil {
return "", err
}
for _, t := range tables {
references += ref.prepareParameter(t.Name, t.Parameters, types.CUECategory)
}
return references, nil
}
+1 -1
View File
@@ -57,7 +57,7 @@ var td corev1beta1.TraitDefinition
var wd, websvcWD corev1beta1.WorkloadDefinition
var cd, websvcCD corev1beta1.ComponentDefinition
func TestAPIs(t *testing.T) {
func TestReferencePlugins(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,