Fix: generate docs for Terraform ComponentDefinition (#3051)

* Fix: generate docs for Terraform ComponentDefinition

Generated kubevela.io docs for Terraform typed ComponentDefinition
with `vela def gen-doc xxx` cli.

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>

* Refactor code

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>

* add ut

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>

* add ut

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>

* refine cloud resource title

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
This commit is contained in:
Zheng Xi Zhou
2022-01-10 14:08:48 +08:00
committed by GitHub
parent b17abe0081
commit 1a50dd76b5
8 changed files with 131 additions and 32 deletions
+9 -1
View File
@@ -21,6 +21,8 @@ import (
"fmt"
"os"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
)
@@ -34,7 +36,13 @@ func main() {
path = plugins.KubeVelaIOTerraformPath
}
if err := ref.GenerateReferenceDocs(ctx, path); err != nil {
c, err := common.InitBaseRestConfig()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if err := ref.GenerateReferenceDocs(ctx, c, path, types.DefaultKubeVelaNS); err != nil {
fmt.Println(err)
os.Exit(1)
}
+8 -1
View File
@@ -190,9 +190,16 @@ func GetOpenAPISchemaFromTerraformComponentDefinition(configuration string) ([]b
// GetTerraformConfigurationFromRemote gets Terraform Configuration(HCL)
func GetTerraformConfigurationFromRemote(name, remoteURL, remotePath string) (string, error) {
tmpPath := filepath.Join("./tmp/terraform", name)
// 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")
}
}
_, err := git.PlainClone(tmpPath, false, &git.CloneOptions{
URL: remoteURL,
Progress: os.Stdout,
Progress: nil,
})
if err != nil {
return "", err
+8 -6
View File
@@ -377,6 +377,9 @@ variable "bbb" {
}
func TestGetTerraformConfigurationFromRemote(t *testing.T) {
// If you hit a panic on macOS as below, please fix it by referencing https://github.com/eisenxp/macos-golink-wrapper.
// panic: permission denied [recovered]
// panic: permission denied
type want struct {
config string
err error
@@ -419,16 +422,15 @@ variable "aaa" {
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
patch := ApplyFunc(git.PlainCloneContext, func(ctx context.Context, path string, isBare bool, o *git.CloneOptions) (*git.Repository, error) {
tmpPath := filepath.Join("./tmp/terraform", tc.name)
err := os.MkdirAll(tmpPath, os.ModePerm)
assert.NilError(t, err)
err = ioutil.WriteFile(filepath.Clean(filepath.Join(tmpPath, "main.tf")), tc.data, 0644)
assert.NilError(t, err)
return nil, nil
})
defer patch.Reset()
tmpPath := filepath.Join("./tmp/terraform", tc.name)
err := os.MkdirAll(tmpPath, os.ModePerm)
assert.NilError(t, err)
err = ioutil.WriteFile(filepath.Clean(filepath.Join(tmpPath, "main.tf")), tc.data, 0644)
assert.NilError(t, err)
conf, err := GetTerraformConfigurationFromRemote(tc.name, tc.url, tc.path)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nGetTerraformConfigurationFromRemote(...): -want error, +got error:\n%s", name, diff)
+36
View File
@@ -44,6 +44,7 @@ import (
"github.com/oam-dev/kubevela/pkg/cue/model/sets"
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/references/plugins"
)
const (
@@ -73,6 +74,7 @@ func DefinitionCommandGroup(c common.Args, order string) *cobra.Command {
NewDefinitionDelCommand(c),
NewDefinitionInitCommand(c),
NewDefinitionValidateCommand(c),
NewDefinitionGenDocCommand(c),
)
return cmd
}
@@ -324,6 +326,40 @@ func NewDefinitionGetCommand(c common.Args) *cobra.Command {
return cmd
}
// NewDefinitionGenDocCommand create the `vela def gen-doc` command to generate documentation of definitions
func NewDefinitionGenDocCommand(c common.Args) *cobra.Command {
cmd := &cobra.Command{
Use: "gen-doc NAME",
Short: "Generate documentation of definitions (Only Terraform typed definitions are supported)",
Long: "Generate documentation of definitions",
Example: "1. Generate documentation for ComponentDefinition alibaba-vpc:\n" +
"> vela def gen-doc alibaba-vpc -n vela-system\n",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("please specify definition name")
}
namespace, err := cmd.Flags().GetString(FlagNamespace)
if err != nil {
return errors.Wrapf(err, "failed to get `%s`", Namespace)
}
ref := &plugins.MarkdownReference{}
ctx := context.Background()
ref.DefinitionName = args[0]
path := plugins.KubeVelaIOTerraformPath
if err := ref.GenerateReferenceDocs(ctx, c, path, namespace); err != nil {
return errors.Wrap(err, "failed to generate reference docs")
}
cmd.Printf("Generated docs for %s in ./%s/%s.md\n", args[0], path, args[0])
return nil
},
}
cmd.Flags().StringP(Namespace, "n", "", "Specify the namespace of the definition.")
return cmd
}
// NewDefinitionListCommand create the `vela def list` command to list definition from k8s
func NewDefinitionListCommand(c common.Args) *cobra.Command {
cmd := &cobra.Command{
+12 -2
View File
@@ -26,9 +26,8 @@ import (
"testing"
"time"
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
@@ -36,6 +35,7 @@ import (
common3 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
)
@@ -205,6 +205,7 @@ func TestNewDefinitionInitCommand(t *testing.T) {
func TestNewDefinitionGetCommand(t *testing.T) {
c := initArgs()
// normal test
cmd := NewDefinitionGetCommand(c)
initCommand(cmd)
@@ -230,6 +231,15 @@ func TestNewDefinitionGetCommand(t *testing.T) {
}
}
func TestNewDefinitionGenDocCommand(t *testing.T) {
c := initArgs()
cmd := NewDefinitionGenDocCommand(c)
assert.NotNil(t, cmd.Execute())
cmd.SetArgs([]string{"alibaba-xxxxxxx"})
assert.NotNil(t, cmd.Execute())
}
func TestNewDefinitionListCommand(t *testing.T) {
c := initArgs()
// normal test
+3
View File
@@ -357,6 +357,9 @@ func GetCapabilityByName(ctx context.Context, c common.Args, capabilityName stri
}
return capability, nil
}
if ns == types.DefaultKubeVelaNS {
return nil, fmt.Errorf("could not find %s in namespace %s", capabilityName, ns)
}
return nil, fmt.Errorf("could not find %s in namespace %s, or %s", capabilityName, ns, types.DefaultKubeVelaNS)
}
+36
View File
@@ -487,6 +487,42 @@ func TestPrepareTerraformOutputs(t *testing.T) {
t.Errorf("prepareTerraformOutputs(...): -want, +got:\n%s\n", cmp.Diff(tc.expect, content))
}
})
}
}
func TestMakeReadableTitle(t *testing.T) {
type args struct {
title string
}
testcases := []struct {
args args
want string
}{
{
args: args{
title: "abc",
},
want: "Abc",
},
{
args: args{
title: "abc-def",
},
want: "Abc-Def",
},
{
args: args{
title: "alibaba-def-ghi",
},
want: "Alibaba Cloud DEF-GHI",
},
}
for _, tc := range testcases {
t.Run("", func(t *testing.T) {
title := makeReadableTitle(tc.args.title)
if title != tc.want {
t.Errorf("makeReadableTitle(...): -want, +got:\n%s\n", cmp.Diff(tc.want, title))
}
})
}
}
+19 -22
View File
@@ -365,24 +365,21 @@ func setDisplayFormat(format string) {
}
// GenerateReferenceDocs generates reference docs
func (ref *MarkdownReference) GenerateReferenceDocs(ctx context.Context, baseRefPath string) error {
func (ref *MarkdownReference) GenerateReferenceDocs(ctx context.Context, c common.Args, baseRefPath string, namespace string) error {
var (
caps []types.Capability
err error
)
c, err := common.InitBaseRestConfig()
if err != nil {
return err
}
if ref.DefinitionName == "" {
caps, err = LoadAllInstalledCapability("default", c)
if err != nil {
return fmt.Errorf("failed to generate reference docs for all capabilities: %w", err)
return fmt.Errorf("failed to get all capabilityes: %w", err)
}
} else {
cap, err := GetCapabilityByName(ctx, c, ref.DefinitionName, types.DefaultKubeVelaNS)
cap, err := GetCapabilityByName(ctx, c, ref.DefinitionName, namespace)
if err != nil {
return fmt.Errorf("failed to generate reference docs for capability %s: %w", ref.DefinitionName, err)
return fmt.Errorf("failed to get capability capability %s: %w", ref.DefinitionName, err)
}
caps = []types.Capability{*cap}
}
@@ -393,28 +390,19 @@ func (ref *MarkdownReference) GenerateReferenceDocs(ctx context.Context, baseRef
// CreateMarkdown creates markdown based on capabilities
func (ref *MarkdownReference) CreateMarkdown(ctx context.Context, caps []types.Capability, baseRefPath, referenceSourcePath string) error {
setDisplayFormat("markdown")
var capabilityType string
for i, c := range caps {
switch c.Type {
case types.TypeWorkload:
capabilityType = WorkloadTypePath
case types.TypeComponentDefinition:
capabilityType = ComponentDefinitionTypePath
case types.TypeTrait:
capabilityType = TraitPath
default:
if c.Type != types.TypeWorkload && c.Type != types.TypeComponentDefinition && c.Type != types.TypeTrait {
return fmt.Errorf("the type of the capability is not right")
}
fileName := fmt.Sprintf("%s.md", c.Name)
filePath := filepath.Join(baseRefPath, capabilityType)
if _, err := os.Stat(filePath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(filePath, 0750); err != nil {
if _, err := os.Stat(baseRefPath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(baseRefPath, 0750); err != nil {
return err
}
}
markdownFile := filepath.Join(baseRefPath, capabilityType, fileName)
markdownFile := filepath.Join(baseRefPath, fileName)
f, err := os.OpenFile(filepath.Clean(markdownFile), os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return fmt.Errorf("failed to open file %s: %w", markdownFile, err)
@@ -424,7 +412,7 @@ func (ref *MarkdownReference) CreateMarkdown(ctx context.Context, caps []types.C
}
capName := c.Name
refContent = ""
capNameInTitle := strings.Title(capName)
capNameInTitle := makeReadableTitle(capName)
switch c.Category {
case types.CUECategory:
cueValue, err := common.GetCUEParameterValue(c.CueTemplate)
@@ -483,6 +471,15 @@ func (ref *MarkdownReference) CreateMarkdown(ctx context.Context, caps []types.C
return nil
}
func makeReadableTitle(title string) string {
const alibabaCloud = "alibaba-"
if strings.HasPrefix(title, alibabaCloud) {
cloudResource := strings.Replace(title, alibabaCloud, "", 1)
return "Alibaba Cloud " + strings.ToUpper(cloudResource)
}
return strings.Title(title)
}
// prepareParameter prepares the table content for each property
func (ref *MarkdownReference) prepareParameter(tableName string, parameterList []ReferenceParameter, category types.CapabilityCategory) string {
refContent := fmt.Sprintf("\n\n%s\n\n", tableName)