mirror of https://github.com/kubevela/kubevela.git
Compare commits
4 Commits
5741865d0f
...
639c5b7347
| Author | SHA1 | Date |
|---|---|---|
|
|
639c5b7347 | |
|
|
531ad8bebf | |
|
|
bf8d5f350a | |
|
|
1d7b186664 |
|
|
@ -47,14 +47,7 @@ func GetParameters(templateStr string) ([]types.Parameter, error) {
|
|||
if iter.Selector().IsDefinition() {
|
||||
continue
|
||||
}
|
||||
// Use String() instead of Unquoted() to avoid panic on pattern parameter selectors
|
||||
// Unquoted() panics unless the selector is a StringLabel with a concrete name
|
||||
selector := iter.Selector()
|
||||
name := selector.String()
|
||||
// If it's a quoted string, unquote it safely
|
||||
if selector.IsString() && selector.LabelType() == cue.StringLabel {
|
||||
name = selector.Unquoted()
|
||||
}
|
||||
name := GetSelectorLabel(iter.Selector())
|
||||
var param = types.Parameter{
|
||||
Name: name,
|
||||
Required: !iter.IsOptional(),
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ import (
|
|||
"github.com/kubevela/workflow/pkg/cue/model/value"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
velacue "github.com/oam-dev/kubevela/pkg/cue"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -143,14 +145,7 @@ func getStatusMap(templateContext map[string]interface{}, statusFields string, p
|
|||
return templateContext, nil, errors.WithMessage(err, "get context fields")
|
||||
}
|
||||
for iter.Next() {
|
||||
// Use String() instead of Unquoted() to avoid panic on definition or hidden labels
|
||||
// Unquoted() panics unless the selector is a StringLabel with a concrete name
|
||||
selector := iter.Selector()
|
||||
label := selector.String()
|
||||
// If it's a quoted string, unquote it safely
|
||||
if selector.IsString() && selector.LabelType() == cue.StringLabel {
|
||||
label = selector.Unquoted()
|
||||
}
|
||||
label := velacue.GetSelectorLabel(iter.Selector())
|
||||
contextLabels = append(contextLabels, label)
|
||||
}
|
||||
|
||||
|
|
@ -168,14 +163,7 @@ func getStatusMap(templateContext map[string]interface{}, statusFields string, p
|
|||
|
||||
outer:
|
||||
for iter.Next() {
|
||||
// Use String() instead of Unquoted() to avoid panic on definition or hidden labels
|
||||
// Unquoted() panics unless the selector is a StringLabel with a concrete name
|
||||
selector := iter.Selector()
|
||||
label := selector.String()
|
||||
// If it's a quoted string, unquote it safely
|
||||
if selector.IsString() && selector.LabelType() == cue.StringLabel {
|
||||
label = selector.Unquoted()
|
||||
}
|
||||
label := velacue.GetSelectorLabel(iter.Selector())
|
||||
|
||||
if len(label) >= 32 {
|
||||
klog.Warningf("status.details field label %s is too long, skipping", label)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import (
|
|||
"github.com/kubevela/workflow/pkg/cue/model/value"
|
||||
"github.com/kubevela/workflow/pkg/cue/process"
|
||||
|
||||
velacue "github.com/oam-dev/kubevela/pkg/cue"
|
||||
velaprocess "github.com/oam-dev/kubevela/pkg/cue/process"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/task"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
|
|
@ -139,14 +140,7 @@ func (wd *workloadDef) Complete(ctx process.Context, abstractTemplate string, pa
|
|||
continue
|
||||
}
|
||||
other, err := model.NewOther(iter.Value())
|
||||
// Use String() instead of Unquoted() to avoid panic on pattern labels
|
||||
// Unquoted() panics unless the selector is a StringLabel with a concrete name
|
||||
selector := iter.Selector()
|
||||
name := selector.String()
|
||||
// If it's a quoted string, unquote it safely
|
||||
if selector.IsString() && selector.LabelType() == cue.StringLabel {
|
||||
name = selector.Unquoted()
|
||||
}
|
||||
name := velacue.GetSelectorLabel(iter.Selector())
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "invalid outputs(%s) of workload %s", name, wd.name)
|
||||
}
|
||||
|
|
@ -279,14 +273,7 @@ func (td *traitDef) Complete(ctx process.Context, abstractTemplate string, param
|
|||
continue
|
||||
}
|
||||
other, err := model.NewOther(iter.Value())
|
||||
// Use String() instead of Unquoted() to avoid panic on pattern labels
|
||||
// Unquoted() panics unless the selector is a StringLabel with a concrete name
|
||||
selector := iter.Selector()
|
||||
name := selector.String()
|
||||
// If it's a quoted string, unquote it safely
|
||||
if selector.IsString() && selector.LabelType() == cue.StringLabel {
|
||||
name = selector.Unquoted()
|
||||
}
|
||||
name := velacue.GetSelectorLabel(iter.Selector())
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "invalid outputs(resource=%s) of trait %s", name, td.name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package cue
|
||||
|
||||
import "cuelang.org/go/cue"
|
||||
|
||||
// GetSelectorLabel safely extracts a label from a CUE selector.
|
||||
// It uses String() by default to avoid panics on pattern parameter selectors,
|
||||
// and only uses Unquoted() when it's safe (i.e., for StringLabel with concrete names).
|
||||
// This prevents panics that would occur when calling Unquoted() on pattern constraints like [string]: T.
|
||||
func GetSelectorLabel(selector cue.Selector) string {
|
||||
// Use String() as a safe default
|
||||
label := selector.String()
|
||||
// If it's a quoted string, unquote it safely
|
||||
if selector.IsString() && selector.LabelType() == cue.StringLabel {
|
||||
label = selector.Unquoted()
|
||||
}
|
||||
return label
|
||||
}
|
||||
|
|
@ -226,23 +226,8 @@ func (def *Definition) ToCUEString() (string, error) {
|
|||
return "", errors.Wrapf(err, "failed to parse template cue string")
|
||||
}
|
||||
|
||||
// Extract imports from original file before fix.File() clears them
|
||||
var importPaths []string
|
||||
for _, decl := range f.Decls {
|
||||
if importDecl, ok := decl.(*ast.ImportDecl); ok {
|
||||
for _, spec := range importDecl.Specs {
|
||||
if spec.Path != nil {
|
||||
importPath := spec.Path.Value
|
||||
if spec.Name != nil {
|
||||
// Handle named imports
|
||||
importPaths = append(importPaths, fmt.Sprintf("%s %s", spec.Name.Name, importPath))
|
||||
} else {
|
||||
importPaths = append(importPaths, importPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Extract imports before fix.File() clears them
|
||||
importPaths := extractImportsFromFile(f)
|
||||
|
||||
f = fix.File(f)
|
||||
var templateDecls []ast.Decl
|
||||
|
|
@ -674,13 +659,10 @@ func GetDefinitionDefaultSpec(kind string) map[string]interface{} {
|
|||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
func formatCUEString(cueString string) (string, error) {
|
||||
f, err := parser.ParseFile("-", cueString, parser.ParseComments)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to parse file during format cue string")
|
||||
}
|
||||
|
||||
// Extract imports before fix.File() clears them (same workaround as in ToCUEString)
|
||||
// extractImportsFromFile extracts import paths from an AST file before fix.File() clears them.
|
||||
// This is necessary because fix.File() removes import declarations that are not directly used.
|
||||
// Returns a slice of import paths, where named imports are formatted as "name path".
|
||||
func extractImportsFromFile(f *ast.File) []string {
|
||||
var importPaths []string
|
||||
for _, decl := range f.Decls {
|
||||
if importDecl, ok := decl.(*ast.ImportDecl); ok {
|
||||
|
|
@ -697,6 +679,17 @@ func formatCUEString(cueString string) (string, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return importPaths
|
||||
}
|
||||
|
||||
func formatCUEString(cueString string) (string, error) {
|
||||
f, err := parser.ParseFile("-", cueString, parser.ParseComments)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to parse file during format cue string")
|
||||
}
|
||||
|
||||
// Extract imports before fix.File() clears them
|
||||
importPaths := extractImportsFromFile(f)
|
||||
|
||||
n := fix.File(f)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,22 +16,111 @@ limitations under the License.
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
sigs "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/references/appfile/api"
|
||||
|
||||
utilcommon "github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
querytypes "github.com/oam-dev/kubevela/pkg/utils/types"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
func TestExportFromAppFile(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
assert.NoError(t, v1beta1.AddToScheme(s))
|
||||
clt := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
var out bytes.Buffer
|
||||
ioStream := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
|
||||
o := &AppfileOptions{
|
||||
Kubecli: clt,
|
||||
IO: ioStream,
|
||||
Namespace: "default",
|
||||
}
|
||||
|
||||
appFile := &api.AppFile{
|
||||
Name: "test-app-export-from",
|
||||
}
|
||||
|
||||
c := utilcommon.Args{}
|
||||
c.SetClient(clt)
|
||||
|
||||
result, data, err := o.ExportFromAppFile(appFile, "default", true, c)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.NotNil(t, data)
|
||||
assert.Contains(t, string(data), "name: test-app-export-from")
|
||||
assert.Equal(t, "test-app-export-from", result.application.Name)
|
||||
}
|
||||
|
||||
func TestApplyApp(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
|
||||
t.Run("create app if not exist", func(t *testing.T) {
|
||||
cltCreate := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
var outCreate bytes.Buffer
|
||||
ioStreamCreate := cmdutil.IOStreams{In: os.Stdin, Out: &outCreate, ErrOut: &outCreate}
|
||||
oCreate := &AppfileOptions{
|
||||
Kubecli: cltCreate,
|
||||
IO: ioStreamCreate,
|
||||
Namespace: "default",
|
||||
}
|
||||
appCreate := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app-apply",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
err := oCreate.ApplyApp(appCreate, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, outCreate.String(), "App has not been deployed, creating a new deployment...")
|
||||
assert.Contains(t, outCreate.String(), "vela port-forward test-app-apply")
|
||||
})
|
||||
|
||||
t.Run("update app if exists", func(t *testing.T) {
|
||||
existingApp := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app-apply",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
cltUpdate := fake.NewClientBuilder().WithScheme(s).WithObjects(existingApp).Build()
|
||||
var outUpdate bytes.Buffer
|
||||
ioStreamUpdate := cmdutil.IOStreams{In: os.Stdin, Out: &outUpdate, ErrOut: &outUpdate}
|
||||
oUpdate := &AppfileOptions{
|
||||
Kubecli: cltUpdate,
|
||||
IO: ioStreamUpdate,
|
||||
Namespace: "default",
|
||||
}
|
||||
appUpdate := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app-apply",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
err := oUpdate.ApplyApp(appUpdate, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, outUpdate.String(), "App exists, updating existing deployment...")
|
||||
assert.Contains(t, outUpdate.String(), "vela port-forward test-app-apply")
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
s := runtime.NewScheme()
|
||||
|
|
@ -95,7 +184,7 @@ func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
|
|||
|
||||
k8sClient5 := fake.NewClientBuilder().WithScheme(s).WithObjects(app2, def2, conf2).Build()
|
||||
type args struct {
|
||||
k8sClient client.Client
|
||||
k8sClient sigs.Client
|
||||
namespace string
|
||||
name string
|
||||
}
|
||||
|
|
@ -176,5 +265,115 @@ func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIsAppfile(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "valid appfile json",
|
||||
data: []byte(`{"name": "test"}`),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "invalid json",
|
||||
data: []byte(`{"name": "test"`),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "application yaml",
|
||||
data: []byte(`
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: test-app
|
||||
`),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "appfile yaml",
|
||||
data: []byte(`
|
||||
name: test-app
|
||||
`),
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := IsAppfile(tc.data)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
app := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "test-ns",
|
||||
},
|
||||
}
|
||||
info := Info(app)
|
||||
assert.Contains(t, info, "vela port-forward test-app -n test-ns")
|
||||
assert.Contains(t, info, "vela exec test-app -n test-ns")
|
||||
assert.Contains(t, info, "vela logs test-app -n test-ns")
|
||||
assert.Contains(t, info, "vela status test-app -n test-ns")
|
||||
assert.Contains(t, info, "vela status test-app -n test-ns --endpoint")
|
||||
}
|
||||
|
||||
func TestSonLeafResource(t *testing.T) {
|
||||
node := &querytypes.ResourceTreeNode{
|
||||
LeafNodes: []*querytypes.ResourceTreeNode{
|
||||
{
|
||||
Object: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "Deployment",
|
||||
"apiVersion": "apps/v1",
|
||||
},
|
||||
},
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
},
|
||||
}
|
||||
resources := sonLeafResource(node, "Deployment", "apps/v1")
|
||||
assert.Equal(t, 1, len(resources))
|
||||
assert.Equal(t, "Deployment", resources[0].GetKind())
|
||||
}
|
||||
|
||||
func TestLoadAppFile(t *testing.T) {
|
||||
content := "name: test-app"
|
||||
tmpFile, err := os.CreateTemp("", "appfile-*.yaml")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
_, err = tmpFile.WriteString(content)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, tmpFile.Close())
|
||||
|
||||
appFile, err := LoadAppFile(tmpFile.Name())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, appFile)
|
||||
assert.Equal(t, "test-app", appFile.Name)
|
||||
}
|
||||
|
||||
func TestApplyApplication(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
clt := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
app := v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
var out bytes.Buffer
|
||||
ioStream := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
err := ApplyApplication(app, ioStream, clt)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out.String(), "Applying an application in vela K8s object format...")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,39 +18,50 @@ package common
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
appv1beta1 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
)
|
||||
|
||||
func TestToPercentageStr(t *testing.T) {
|
||||
var v1, v2 int64
|
||||
v1, v2 = 10, 100
|
||||
assert.Equal(t, ToPercentageStr(v1, v2), "10%")
|
||||
v1, v2 = 10, 0
|
||||
assert.Equal(t, ToPercentageStr(v1, v2), "N/A")
|
||||
t.Run("valid percentage", func(t *testing.T) {
|
||||
var v1, v2 int64 = 10, 100
|
||||
assert.Equal(t, "10%", ToPercentageStr(v1, v2))
|
||||
})
|
||||
t.Run("division by zero", func(t *testing.T) {
|
||||
var v1, v2 int64 = 10, 0
|
||||
assert.Equal(t, "N/A", ToPercentageStr(v1, v2))
|
||||
})
|
||||
}
|
||||
|
||||
func TestToPercentage(t *testing.T) {
|
||||
t.Run("valid percentage", func(t *testing.T) {
|
||||
var v1, v2 int64 = 10, 100
|
||||
assert.Equal(t, 10, ToPercentage(v1, v2))
|
||||
})
|
||||
t.Run("division by zero", func(t *testing.T) {
|
||||
var v1, v2 int64 = 10, 0
|
||||
assert.Equal(t, 0, ToPercentage(v1, v2))
|
||||
})
|
||||
t.Run("floor", func(t *testing.T) {
|
||||
var v1, v2 int64 = 1, 3
|
||||
assert.Equal(t, 33, ToPercentage(v1, v2))
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetPodResourceSpecAndUsage(t *testing.T) {
|
||||
testEnv := &envtest.Environment{
|
||||
ControlPlaneStartTimeout: time.Minute * 3,
|
||||
ControlPlaneStopTimeout: time.Minute,
|
||||
UseExistingCluster: ptr.To(false),
|
||||
}
|
||||
cfg, err := testEnv.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
k8sClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
assert.NoError(t, err)
|
||||
s := runtime.NewScheme()
|
||||
v1.AddToScheme(s)
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
|
||||
quantityLimitsCPU, _ := resource.ParseQuantity("10m")
|
||||
quantityLimitsMemory, _ := resource.ParseQuantity("10Mi")
|
||||
|
|
@ -83,10 +94,157 @@ func TestGetPodResourceSpecAndUsage(t *testing.T) {
|
|||
}
|
||||
|
||||
spec, usage := GetPodResourceSpecAndUsage(k8sClient, pod, podMetric)
|
||||
assert.Equal(t, usage.CPU, int64(8))
|
||||
assert.Equal(t, usage.Mem, int64(20971520))
|
||||
assert.Equal(t, spec.Lcpu, int64(10))
|
||||
assert.Equal(t, spec.Lmem, int64(10485760))
|
||||
assert.Equal(t, spec.Rcpu, int64(100))
|
||||
assert.Equal(t, spec.Rmem, int64(52428800))
|
||||
assert.Equal(t, int64(8), usage.CPU)
|
||||
assert.Equal(t, int64(20971520), usage.Mem)
|
||||
assert.Equal(t, int64(10), spec.Lcpu)
|
||||
assert.Equal(t, int64(10485760), spec.Lmem)
|
||||
assert.Equal(t, int64(100), spec.Rcpu)
|
||||
assert.Equal(t, int64(52428800), spec.Rmem)
|
||||
}
|
||||
|
||||
func TestGetPodStorage(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1.AddToScheme(s)
|
||||
|
||||
pvc := &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-pvc",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(pvc).Build()
|
||||
|
||||
podWithPVC := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "test-storage",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: "test-pvc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
podWithoutPVC := &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "test-emptydir",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
podWithNonExistentPVC := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "test-storage",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: "non-existent-pvc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("pod with pvc", func(t *testing.T) {
|
||||
storages := GetPodStorage(k8sClient, podWithPVC)
|
||||
assert.Equal(t, 1, len(storages))
|
||||
assert.Equal(t, "test-pvc", storages[0].Name)
|
||||
})
|
||||
|
||||
t.Run("pod without pvc", func(t *testing.T) {
|
||||
storages := GetPodStorage(k8sClient, podWithoutPVC)
|
||||
assert.Equal(t, 0, len(storages))
|
||||
})
|
||||
|
||||
t.Run("pod with non-existent pvc", func(t *testing.T) {
|
||||
storages := GetPodStorage(k8sClient, podWithNonExistentPVC)
|
||||
assert.Equal(t, 0, len(storages))
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetPodOfManagedResource(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1.AddToScheme(s)
|
||||
appv1beta1.AddToScheme(s)
|
||||
|
||||
app := &appv1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
|
||||
pod := &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-pod",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
podUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pod)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rt := &appv1beta1.ResourceTracker{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app-v1", // A tracker name
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.oam.dev/name": "test-app",
|
||||
"app.oam.dev/namespace": "default",
|
||||
},
|
||||
},
|
||||
Spec: appv1beta1.ResourceTrackerSpec{
|
||||
Type: appv1beta1.ResourceTrackerTypeRoot,
|
||||
ManagedResources: []appv1beta1.ManagedResource{
|
||||
{
|
||||
ClusterObjectReference: common.ClusterObjectReference{
|
||||
ObjectReference: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
Name: "test-pod",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
OAMObjectReference: common.OAMObjectReference{
|
||||
Component: "test-component",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(app, rt, &unstructured.Unstructured{Object: podUnstructured}).Build()
|
||||
|
||||
t.Run("get existing pod", func(t *testing.T) {
|
||||
pods := GetPodOfManagedResource(k8sClient, app, "test-component")
|
||||
assert.Equal(t, 2, len(pods))
|
||||
assert.Equal(t, "test-pod", pods[0].Name)
|
||||
assert.Equal(t, "test-pod", pods[1].Name)
|
||||
})
|
||||
|
||||
t.Run("get pod for non-existent component", func(t *testing.T) {
|
||||
pods := GetPodOfManagedResource(k8sClient, app, "non-existent-component")
|
||||
assert.Equal(t, 0, len(pods))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,16 +17,178 @@ limitations under the License.
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
func TestInstallComponentDefinition(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
assert.NoError(t, v1beta1.AddToScheme(s))
|
||||
|
||||
validComponentData := []byte(`
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
name: test-component
|
||||
spec:
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
`)
|
||||
|
||||
existingComponent := &v1beta1.ComponentDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-component",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
|
||||
err := InstallComponentDefinition(k8sClient, validComponentData, ioStreams)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out.String(), "Installing component: test-component")
|
||||
|
||||
var cd v1beta1.ComponentDefinition
|
||||
err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "test-component", Namespace: types.DefaultKubeVelaNS}, &cd)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test-component", cd.Name)
|
||||
})
|
||||
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(existingComponent).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
|
||||
err := InstallComponentDefinition(k8sClient, validComponentData, ioStreams)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("client error", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithInterceptorFuncs(interceptor.Funcs{
|
||||
Create: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.CreateOption) error {
|
||||
return errors.New("client create error")
|
||||
},
|
||||
}).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
|
||||
err := InstallComponentDefinition(k8sClient, validComponentData, ioStreams)
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "client create error")
|
||||
})
|
||||
|
||||
t.Run("invalid data", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
invalidData := []byte(`{"invalid": "yaml"`)
|
||||
|
||||
err := InstallComponentDefinition(k8sClient, invalidData, ioStreams)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("nil data", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
|
||||
err := InstallComponentDefinition(k8sClient, nil, ioStreams)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "componentData is nil", err.Error())
|
||||
})
|
||||
}
|
||||
|
||||
func TestInstallTraitDefinition(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
|
||||
validTraitData := []byte(`
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: test-trait
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
`)
|
||||
|
||||
existingTrait := &v1beta1.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-trait",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
|
||||
err := InstallTraitDefinition(k8sClient, validTraitData, ioStreams)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out.String(), "Installing trait test-trait")
|
||||
|
||||
var td v1beta1.TraitDefinition
|
||||
err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "test-trait", Namespace: types.DefaultKubeVelaNS}, &td)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test-trait", td.Name)
|
||||
})
|
||||
|
||||
t.Run("already exists", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(existingTrait).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
|
||||
err := InstallTraitDefinition(k8sClient, validTraitData, ioStreams)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("client error", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithInterceptorFuncs(interceptor.Funcs{
|
||||
Create: func(ctx context.Context, cl client.WithWatch, obj client.Object, opts ...client.CreateOption) error {
|
||||
return errors.New("client create error")
|
||||
},
|
||||
}).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
|
||||
err := InstallTraitDefinition(k8sClient, validTraitData, ioStreams)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "client create error", err.Error())
|
||||
})
|
||||
|
||||
t.Run("invalid data", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
var out bytes.Buffer
|
||||
ioStreams := cmdutil.IOStreams{In: os.Stdin, Out: &out, ErrOut: &out}
|
||||
invalidData := []byte(`{"invalid": "yaml"`)
|
||||
|
||||
err := InstallTraitDefinition(k8sClient, invalidData, ioStreams)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddSourceIntoDefinition(t *testing.T) {
|
||||
caseJson := []byte(`{"template":""}`)
|
||||
wantJson := []byte(`{"source":{"repoName":"foo"},"template":""}`)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
func TestListRawWorkloadDefinitions(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
|
||||
sysWorkload := v1beta1.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sys-worker",
|
||||
Namespace: oam.SystemDefinitionNamespace,
|
||||
},
|
||||
}
|
||||
userWorkload := v1beta1.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "user-worker",
|
||||
Namespace: "my-ns",
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("list from both user and system namespaces", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(&sysWorkload, &userWorkload).Build()
|
||||
c := common.Args{}
|
||||
c.SetClient(k8sClient)
|
||||
|
||||
defs, err := ListRawWorkloadDefinitions("my-ns", c)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(defs))
|
||||
})
|
||||
|
||||
t.Run("list from only system namespace", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(&sysWorkload).Build()
|
||||
c := common.Args{}
|
||||
c.SetClient(k8sClient)
|
||||
|
||||
defs, err := ListRawWorkloadDefinitions("my-ns", c)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(defs))
|
||||
assert.Equal(t, "sys-worker", defs[0].Name)
|
||||
})
|
||||
|
||||
t.Run("list from only user namespace", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(&userWorkload).Build()
|
||||
c := common.Args{}
|
||||
c.SetClient(k8sClient)
|
||||
|
||||
defs, err := ListRawWorkloadDefinitions("my-ns", c)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(defs))
|
||||
assert.Equal(t, "user-worker", defs[0].Name)
|
||||
})
|
||||
|
||||
t.Run("no definitions found", func(t *testing.T) {
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
c := common.Args{}
|
||||
c.SetClient(k8sClient)
|
||||
|
||||
defs, err := ListRawWorkloadDefinitions("my-ns", c)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(defs))
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
corecommon "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
func TestInitApplication(t *testing.T) {
|
||||
c := common.Args{}
|
||||
c.SetClient(fake.NewClientBuilder().Build())
|
||||
|
||||
t.Run("with appGroup", func(t *testing.T) {
|
||||
app, err := InitApplication("default", c, "my-workload", "my-app")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, app)
|
||||
assert.Equal(t, "my-app", app.Name)
|
||||
})
|
||||
|
||||
t.Run("without appGroup", func(t *testing.T) {
|
||||
app, err := InitApplication("default", c, "my-workload", "")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, app)
|
||||
assert.Equal(t, "my-workload", app.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBaseComplete(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
assert.NoError(t, v1beta1.AddToScheme(s))
|
||||
|
||||
template := &v1beta1.ComponentDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "worker",
|
||||
Namespace: oam.SystemDefinitionNamespace,
|
||||
},
|
||||
Spec: v1beta1.ComponentDefinitionSpec{
|
||||
Workload: corecommon.WorkloadTypeDescriptor{
|
||||
Type: types.AutoDetectWorkloadDefinition,
|
||||
},
|
||||
Schematic: &corecommon.Schematic{
|
||||
CUE: &corecommon.CUE{
|
||||
Template: `
|
||||
parameter: {
|
||||
image: string
|
||||
port: *8080 | int
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(template).Build()
|
||||
c := common.Args{}
|
||||
c.SetClient(k8sClient)
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
flagSet.String("image", "my-image", "")
|
||||
flagSet.Int64("port", 80, "")
|
||||
|
||||
app, err := BaseComplete(oam.SystemDefinitionNamespace, c, "my-workload", "my-app", flagSet, "worker")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, app)
|
||||
|
||||
workload, ok := app.Services["my-workload"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "my-image", workload["image"])
|
||||
assert.Equal(t, int64(80), workload["port"])
|
||||
})
|
||||
|
||||
t.Run("missing required flag", func(t *testing.T) {
|
||||
flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
// not setting the required "image" flag
|
||||
|
||||
_, err := BaseComplete(oam.SystemDefinitionNamespace, c, "my-workload", "my-app", flagSet, "worker")
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), `required flag(s) "image" not set`)
|
||||
})
|
||||
}
|
||||
|
|
@ -27,6 +27,8 @@ import (
|
|||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
velacue "github.com/oam-dev/kubevela/pkg/cue"
|
||||
)
|
||||
|
||||
// GenerateProvidersMarkdown generates markdown documentation for providers.
|
||||
|
|
@ -100,13 +102,7 @@ func GenerateProviderMarkdown(provider io.Reader, w io.Writer) error {
|
|||
}
|
||||
|
||||
// header - handle both string and non-string selectors
|
||||
selector := iter.Selector()
|
||||
var selectorStr string
|
||||
if selector.IsString() {
|
||||
selectorStr = selector.Unquoted()
|
||||
} else {
|
||||
selectorStr = selector.String()
|
||||
}
|
||||
selectorStr := velacue.GetSelectorLabel(iter.Selector())
|
||||
fmt.Fprintf(docs, "## %s\n", selectorStr)
|
||||
|
||||
doc, _, err := ref.parseParameters("", item.LookupPath(cue.ParsePath(paramsKey)), "*Params*", 0, true)
|
||||
|
|
|
|||
Loading…
Reference in New Issue