mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 10:00:06 +00:00
* update go and golangci version Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * update golangci version Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * update staticcheck version Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * update staticcheck version Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * update staticcheck version Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * update golangci_lint version Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * update golangci_lint version Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml and formating in go files Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated .golangci.yaml and formating in go files Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * updated makefile step Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * fix formatting issue Signed-off-by: Gowtham S <gowthams316@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> * update ginkgo tests Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com> * updated ginkgo tests Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com> * upgraded go/x/crypto to v0.37.0 Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com> * go/kin-openapi version to v0.118.0 and go/x/net version upgraded to v0.39.0 Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com> * upgrades go/x/oauth2 version to v0.29.0 Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com> * update ginkgo test Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com> * upgrades alpine image version to 3.21 Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com> * updates ginkgo tests Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com> --------- Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Pushparaj Shetty K S <kspushparajshetty@gmail.com> Signed-off-by: Gowtham S <gowthams316@gmail.com> Signed-off-by: Shivin Gopalani <gopalanishivin@gmail.com>
849 lines
28 KiB
Go
849 lines
28 KiB
Go
/*
|
|
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 gen_sdk
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"go/format"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
j "github.com/dave/jennifer/jen"
|
|
"github.com/ettle/strcase"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
|
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
|
|
)
|
|
|
|
// GoProxyEnvKey with the environment variable name that defines the GOPROXY preferences.
|
|
const GoProxyEnvKey = "GOPROXY"
|
|
|
|
var (
|
|
mainModuleVersionKey langArgKey = "MainModuleVersion"
|
|
goProxyKey langArgKey = "GoProxy"
|
|
|
|
mainModuleVersion = LangArg{
|
|
Name: mainModuleVersionKey,
|
|
Desc: "The version of main module, it will be used in go get command. For example, tag, commit id, branch name",
|
|
// default hash of main module. This is a commit hash of kubevela-contrib/kubvela-go-sdk. It will be used in go get command.
|
|
Default: "cd431bb25a9a",
|
|
}
|
|
goProxy = LangArg{
|
|
Name: goProxyKey,
|
|
Desc: "The proxy for go get/go mod tidy command",
|
|
Default: "",
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
// Propagate the environment GOPROXY variable to the tests that are executed through external containers.
|
|
goProxy.Default = os.Getenv(GoProxyEnvKey)
|
|
registerLangArg("go", mainModuleVersion, goProxy)
|
|
}
|
|
|
|
const (
|
|
// PackagePlaceHolder is the package name placeholder
|
|
PackagePlaceHolder = "github.com/kubevela/vela-go-sdk"
|
|
)
|
|
|
|
var (
|
|
// DefinitionKindToPascal is the map of definition kind to pascal case
|
|
DefinitionKindToPascal = map[string]string{
|
|
v1beta1.ComponentDefinitionKind: "Component",
|
|
v1beta1.TraitDefinitionKind: "Trait",
|
|
v1beta1.WorkflowStepDefinitionKind: "WorkflowStep",
|
|
v1beta1.PolicyDefinitionKind: "Policy",
|
|
}
|
|
// DefinitionKindToBaseType is the map of definition kind to base type
|
|
DefinitionKindToBaseType = map[string]string{
|
|
v1beta1.ComponentDefinitionKind: "ComponentBase",
|
|
v1beta1.TraitDefinitionKind: "TraitBase",
|
|
v1beta1.WorkflowStepDefinitionKind: "WorkflowStepBase",
|
|
v1beta1.PolicyDefinitionKind: "PolicyBase",
|
|
}
|
|
// DefinitionKindToStatement is the map of definition kind to statement
|
|
DefinitionKindToStatement = map[string]*j.Statement{
|
|
v1beta1.ComponentDefinitionKind: j.Qual("common", "ApplicationComponent"),
|
|
v1beta1.TraitDefinitionKind: j.Qual("common", "ApplicationTrait"),
|
|
v1beta1.WorkflowStepDefinitionKind: j.Qual("v1beta1", "WorkflowStep"),
|
|
v1beta1.PolicyDefinitionKind: j.Qual("v1beta1", "AppPolicy"),
|
|
}
|
|
)
|
|
|
|
// GoDefModifier is the Modifier for golang, modify code for each definition
|
|
type GoDefModifier struct {
|
|
*GenMeta
|
|
*goArgs
|
|
|
|
defStructPointer *j.Statement
|
|
}
|
|
|
|
// GoModuleModifier is the Modifier for golang, modify code for each module which contains multiple definitions
|
|
type GoModuleModifier struct {
|
|
*GenMeta
|
|
*goArgs
|
|
}
|
|
|
|
type goArgs struct {
|
|
apiDir string
|
|
defDir string
|
|
utilsDir string
|
|
// def name of different cases
|
|
nameInSnakeCase string
|
|
nameInPascalCase string
|
|
specNameInPascalCase string
|
|
typeVarName string
|
|
defStructName string
|
|
defFuncReceiver string
|
|
}
|
|
|
|
func (a *goArgs) init(m *GenMeta) error {
|
|
var err error
|
|
a.apiDir, err = filepath.Abs(path.Join(m.Output, m.APIDirectory))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.defDir = path.Join(a.apiDir, pkgdef.DefinitionKindToType[m.kind], m.name)
|
|
a.utilsDir = path.Join(m.Output, "pkg", "apis", "utils")
|
|
a.nameInSnakeCase = strcase.ToSnake(m.name)
|
|
a.nameInPascalCase = strcase.ToPascal(m.name)
|
|
a.typeVarName = a.nameInPascalCase + "Type"
|
|
a.specNameInPascalCase = a.nameInPascalCase + "Spec"
|
|
a.defStructName = strcase.ToGoPascal(m.name + "-" + pkgdef.DefinitionKindToType[m.kind])
|
|
a.defFuncReceiver = m.name[:1]
|
|
return nil
|
|
}
|
|
|
|
// Modify implements Modifier
|
|
func (m *GoModuleModifier) Modify() error {
|
|
for _, fn := range []func() error{
|
|
m.init,
|
|
m.format,
|
|
m.addSubGoMod,
|
|
m.tidyMainMod,
|
|
} {
|
|
if err := fn(); err != nil {
|
|
return errors.Wrap(err, fnName(fn))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *GoModuleModifier) init() error {
|
|
m.goArgs = &goArgs{}
|
|
return m.goArgs.init(m.GenMeta)
|
|
}
|
|
|
|
// Name the name of modifier
|
|
func (m *GoModuleModifier) Name() string {
|
|
return "goModuleModifier"
|
|
}
|
|
|
|
// Name the name of modifier
|
|
func (m *GoDefModifier) Name() string {
|
|
return "GoDefModifier"
|
|
}
|
|
|
|
// Modify the modification of generated code
|
|
func (m *GoDefModifier) Modify() error {
|
|
for _, fn := range []func() error{
|
|
m.init,
|
|
m.clean,
|
|
m.moveUtils,
|
|
m.modifyDefs,
|
|
m.addDefAPI,
|
|
m.addValidateTraits,
|
|
m.exportMethods,
|
|
} {
|
|
if err := fn(); err != nil {
|
|
return errors.Wrap(err, fnName(fn))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *GoDefModifier) init() error {
|
|
m.goArgs = &goArgs{}
|
|
err := m.goArgs.init(m.GenMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
m.defStructPointer = j.Op("*").Id(m.defStructName)
|
|
|
|
err = os.MkdirAll(m.utilsDir, 0750)
|
|
return err
|
|
}
|
|
|
|
func (m *GoDefModifier) clean() error {
|
|
err := os.RemoveAll(path.Join(m.defDir, ".openapi-generator"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = os.RemoveAll(path.Join(m.defDir, "api"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
files, _ := os.ReadDir(m.defDir)
|
|
for _, f := range files {
|
|
dst := strings.TrimPrefix(f.Name(), "model_")
|
|
if dst == m.nameInSnakeCase+"_spec.go" {
|
|
dst = m.nameInSnakeCase + ".go"
|
|
}
|
|
err = os.Rename(path.Join(m.defDir, f.Name()), path.Join(m.defDir, dst))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
|
|
}
|
|
|
|
// addSubGoMod will add a go.mod and go.sum in the api directory if user mark that the api is a submodule
|
|
func (m *GoModuleModifier) addSubGoMod() error {
|
|
if !m.IsSubModule {
|
|
return nil
|
|
}
|
|
files := map[string]string{
|
|
"go.mod_": "go.mod",
|
|
"go.sum": "go.sum",
|
|
}
|
|
for src, dst := range files {
|
|
srcContent, err := Scaffold.ReadFile(path.Join(ScaffoldDir, "go", src))
|
|
if err != nil {
|
|
return errors.Wrap(err, "read "+src)
|
|
}
|
|
subModuleName := strings.TrimSuffix(fmt.Sprintf("%s/%s", m.Package, m.APIDirectory), "/")
|
|
srcContent = bytes.ReplaceAll(srcContent, []byte("module "+PackagePlaceHolder), []byte("module "+subModuleName))
|
|
srcContent = bytes.ReplaceAll(srcContent, []byte("// require "+PackagePlaceHolder), []byte("require "+m.Package))
|
|
|
|
err = os.WriteFile(path.Join(m.apiDir, dst), srcContent, 0600)
|
|
if err != nil {
|
|
return errors.Wrap(err, "write "+dst)
|
|
}
|
|
}
|
|
|
|
cmds := make([]*exec.Cmd, 0)
|
|
if m.LangArgs.Get(mainModuleVersionKey) != mainModuleVersion.Default {
|
|
// nolint:gosec
|
|
cmds = append(cmds, exec.Command("docker", "run",
|
|
"--rm",
|
|
"-v", m.apiDir+":/api",
|
|
"-w", "/api",
|
|
"golang:1.23.8-alpine@sha256:b7486658b87d34ecf95125e5b97e8dfe86c21f712aa36fc0c702e5dc41dc63e1",
|
|
"go", "get", fmt.Sprintf("%s@%s", m.Package, m.LangArgs.Get(mainModuleVersionKey)),
|
|
))
|
|
}
|
|
// nolint:gosec
|
|
cmds = append(cmds, exec.Command("docker", "run",
|
|
"--rm",
|
|
"-v", m.apiDir+":/api",
|
|
"-w", "/api",
|
|
"--env", "GOPROXY="+m.LangArgs.Get(goProxyKey),
|
|
"golang:1.23.8-alpine@sha256:b7486658b87d34ecf95125e5b97e8dfe86c21f712aa36fc0c702e5dc41dc63e1",
|
|
"go", "mod", "tidy",
|
|
))
|
|
for _, cmd := range cmds {
|
|
if m.Verbose {
|
|
fmt.Println(cmd.String())
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
}
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return errors.Wrapf(err, "fail to run command %s", cmd.String())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// tidyMainMod will run go mod tidy in the main module
|
|
func (m *GoModuleModifier) tidyMainMod() error {
|
|
if !m.InitSDK {
|
|
return nil
|
|
}
|
|
outDir, err := filepath.Abs(m.GenMeta.Output)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// nolint:gosec
|
|
cmd := exec.Command("docker", "run",
|
|
"--rm",
|
|
"-v", outDir+":/api",
|
|
"-w", "/api",
|
|
"golang:1.23.8-alpine@sha256:b7486658b87d34ecf95125e5b97e8dfe86c21f712aa36fc0c702e5dc41dc63e1",
|
|
"go", "mod", "tidy",
|
|
)
|
|
if m.Verbose {
|
|
fmt.Println(cmd.String())
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
}
|
|
return cmd.Run()
|
|
}
|
|
|
|
// read all files in definition directory,
|
|
// 1. replace the Nullable* Struct
|
|
// 2. replace the package name
|
|
func (m *GoDefModifier) modifyDefs() error {
|
|
changeNullableType := func(b []byte) []byte {
|
|
return regexp.MustCompile("Nullable(String|(Float|Int)(32|64)|Bool)").ReplaceAll(b, []byte("utils.Nullable$1"))
|
|
}
|
|
|
|
files, err := os.ReadDir(m.defDir)
|
|
defHandleFunc := []byteHandler{
|
|
m.packageFunc,
|
|
changeNullableType,
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, f := range files {
|
|
loc := path.Join(m.defDir, f.Name())
|
|
// nolint:gosec
|
|
b, err := os.ReadFile(loc)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "read file")
|
|
}
|
|
for _, h := range defHandleFunc {
|
|
b = h(b)
|
|
}
|
|
|
|
_ = os.WriteFile(loc, b, 0600)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *GoDefModifier) moveUtils() error {
|
|
// Adjust the generated files and code
|
|
err := os.Rename(path.Join(m.defDir, "utils.go"), path.Join(m.utilsDir, "utils.go"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
utilsFile := path.Join(m.utilsDir, "utils.go")
|
|
|
|
// nolint:gosec
|
|
utilsBytes, err := os.ReadFile(utilsFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
utilsBytes = bytes.Replace(utilsBytes, []byte(fmt.Sprintf("package %s", strcase.ToSnake(m.name))), []byte("package utils"), 1)
|
|
utilsBytes = bytes.ReplaceAll(utilsBytes, []byte("isNil"), []byte("IsNil"))
|
|
err = os.WriteFile(utilsFile, utilsBytes, 0600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// addDefAPI will add component/trait/workflowstep/policy Object to the api
|
|
func (m *GoDefModifier) addDefAPI() error {
|
|
file, err := os.OpenFile(path.Join(m.defDir, m.nameInSnakeCase+".go"), os.O_APPEND|os.O_WRONLY, 0600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
_ = file.Close()
|
|
}()
|
|
renderGroup := make([]*j.Statement, 0)
|
|
renderGroup = append(renderGroup, m.genCommonFunc()...)
|
|
renderGroup = append(renderGroup, m.genFromFunc()...)
|
|
renderGroup = append(renderGroup, m.genDedicatedFunc()...)
|
|
renderGroup = append(renderGroup, m.genNameTypeFunc()...)
|
|
renderGroup = append(renderGroup, m.genUnmarshalFunc()...)
|
|
renderGroup = append(renderGroup, m.genBaseSetterFunc()...)
|
|
renderGroup = append(renderGroup, m.genAddSubStepFunc())
|
|
|
|
buf := new(bytes.Buffer)
|
|
for _, r := range renderGroup {
|
|
// write content at the end of file
|
|
err := r.Render(buf)
|
|
buf.WriteString("\n\n")
|
|
if err != nil {
|
|
return errors.Wrap(err, "render code")
|
|
}
|
|
}
|
|
_, err = file.Write(buf.Bytes())
|
|
if err != nil {
|
|
return errors.Wrap(err, "append content to file")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *GoDefModifier) genCommonFunc() []*j.Statement {
|
|
kind := m.kind
|
|
typeName := j.Id(m.nameInPascalCase + "Type")
|
|
typeConst := j.Const().Add(typeName).Op("=").Lit(m.name)
|
|
j.Op("=").Lit(m.name)
|
|
defStruct := j.Type().Id(m.defStructName).Struct(
|
|
j.Id("Base").Id("apis").Dot(DefinitionKindToBaseType[kind]),
|
|
j.Id("Properties").Id(m.specNameInPascalCase),
|
|
)
|
|
|
|
initFunc := j.Func().Id("init").Params().BlockFunc(func(g *j.Group) {
|
|
g.Add(j.Qual("sdkcommon", "Register"+DefinitionKindToPascal[kind]).Call(j.Add(typeName), j.Id("From"+DefinitionKindToPascal[kind])))
|
|
if kind == v1beta1.WorkflowStepDefinitionKind {
|
|
g.Add(j.Qual("sdkcommon", "RegisterWorkflowSubStep").Call(j.Add(typeName), j.Id("FromWorkflowSubStep")))
|
|
}
|
|
},
|
|
)
|
|
|
|
defStructConstructor := j.Func().Id(m.nameInPascalCase).Params(
|
|
j.Do(func(s *j.Statement) {
|
|
switch kind {
|
|
case v1beta1.ComponentDefinitionKind, v1beta1.PolicyDefinitionKind, v1beta1.WorkflowStepDefinitionKind:
|
|
s.Id("name").String()
|
|
}
|
|
}),
|
|
).Add(m.defStructPointer).Block(
|
|
j.Id(m.defFuncReceiver).Op(":=").Op("&").Id(m.defStructName).Values(j.Dict{
|
|
j.Id("Base"): j.Id("apis").Dot(DefinitionKindToBaseType[kind]).BlockFunc(
|
|
func(g *j.Group) {
|
|
switch kind {
|
|
case v1beta1.ComponentDefinitionKind, v1beta1.PolicyDefinitionKind, v1beta1.WorkflowStepDefinitionKind:
|
|
g.Id("Name").Op(":").Id("name").Op(",")
|
|
g.Id("Type").Op(":").Add(typeName).Op(",")
|
|
}
|
|
}),
|
|
}),
|
|
j.Return(j.Id(m.defFuncReceiver)),
|
|
)
|
|
traitType := DefinitionKindToStatement[v1beta1.TraitDefinitionKind]
|
|
stepType := DefinitionKindToStatement[v1beta1.WorkflowStepDefinitionKind]
|
|
builderDict := j.Dict{
|
|
// all definition have type and properties
|
|
j.Id("Type"): j.Add(typeName),
|
|
j.Id("Properties"): j.Qual("util", "Object2RawExtension").Params(j.Id(m.defFuncReceiver).Dot("Properties")),
|
|
}
|
|
builderDictValues := map[string][]string{
|
|
v1beta1.PolicyDefinitionKind: {"Name"},
|
|
v1beta1.ComponentDefinitionKind: {"Name", "DependsOn", "Inputs", "Outputs"},
|
|
v1beta1.WorkflowStepDefinitionKind: {"Name", "DependsOn", "Inputs", "Outputs", "If", "Timeout", "Meta"},
|
|
}
|
|
for _, v := range builderDictValues[kind] {
|
|
builderDict[j.Id(v)] = j.Id(m.defFuncReceiver).Dot("Base").Dot(v)
|
|
}
|
|
switch kind {
|
|
case v1beta1.ComponentDefinitionKind:
|
|
builderDict[j.Id("Traits")] = j.Id("traits")
|
|
case v1beta1.WorkflowStepDefinitionKind:
|
|
builderDict[j.Id("SubSteps")] = j.Id("subSteps")
|
|
}
|
|
buildFunc := j.Func().
|
|
Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).
|
|
Id("Build").Params().
|
|
Add(DefinitionKindToStatement[kind]).BlockFunc(func(g *j.Group) {
|
|
switch kind {
|
|
case v1beta1.ComponentDefinitionKind:
|
|
g.Add(j.Id("traits").Op(":=").Make(j.Index().Add(traitType), j.Lit(0)))
|
|
g.Add(j.For(j.List(j.Id("_"), j.Id("trait")).Op(":=").Range().Id(m.defFuncReceiver).Dot("Base").Dot("Traits")).Block(
|
|
j.Id("traits").Op("=").Append(j.Id("traits"), j.Id("trait").Dot("Build").Call()),
|
|
))
|
|
case v1beta1.WorkflowStepDefinitionKind:
|
|
g.Add(j.Id("_subSteps").Op(":=").Make(j.Index().Add(stepType), j.Lit(0)))
|
|
g.Add(j.For(j.List(j.Id("_"), j.Id("subStep")).Op(":=").Range().Id(m.defFuncReceiver).Dot("Base").Dot("SubSteps")).Block(
|
|
j.Id("_subSteps").Op("=").Append(j.Id("_subSteps"), j.Id("subStep").Dot("Build").Call()),
|
|
))
|
|
g.Add(j.Id("subSteps").Op(":=").Make(j.Index().Qual("common", "WorkflowSubStep"), j.Lit(0)))
|
|
g.Add(j.For(j.List(j.Id("_"), j.Id("_s").Op(":=").Range().Id("_subSteps"))).Block(
|
|
j.Id("subSteps").Op("=").Append(j.Id("subSteps"), j.Qual("common", "WorkflowSubStep").ValuesFunc(
|
|
func(_g *j.Group) {
|
|
for _, v := range []string{"Name", "DependsOn", "Inputs", "Outputs", "If", "Timeout", "Meta", "Properties", "Type"} {
|
|
_g.Add(j.Id(v).Op(":").Id("_s").Dot(v))
|
|
}
|
|
}),
|
|
)),
|
|
)
|
|
}
|
|
g.Add(j.Id("res").Op(":=").Add(DefinitionKindToStatement[kind]).Values(builderDict))
|
|
g.Add(j.Return(j.Id("res")))
|
|
})
|
|
|
|
return []*j.Statement{typeConst, initFunc, defStruct, defStructConstructor, buildFunc}
|
|
}
|
|
|
|
func (m *GoDefModifier) genFromFunc() []*j.Statement {
|
|
kind := m.kind
|
|
kindBaseProperties := map[string][]string{
|
|
v1beta1.ComponentDefinitionKind: {"Name", "DependsOn", "Inputs", "Outputs"},
|
|
v1beta1.WorkflowStepDefinitionKind: {"Name", "DependsOn", "Inputs", "Outputs", "If", "Timeout", "Meta"},
|
|
v1beta1.PolicyDefinitionKind: {"Name"},
|
|
v1beta1.TraitDefinitionKind: {},
|
|
}
|
|
|
|
// fromFuncRsv means build from a part of K8s Object (e.g. v1beta1.Application.spec.component[*] to internal presentation (e.g. Component)
|
|
// fromFuncRsv will have a function receiver
|
|
getSubSteps := func(sub bool) func(g *j.Group) {
|
|
if m.kind != v1beta1.WorkflowStepDefinitionKind || sub {
|
|
return func(g *j.Group) {}
|
|
}
|
|
return func(g *j.Group) {
|
|
g.Add(j.Id("subSteps").Op(":=").Make(j.Index().Qual("apis", DefinitionKindToPascal[kind]), j.Lit(0)))
|
|
g.Add(
|
|
j.For(
|
|
j.List(j.Id("_"), j.Id("_s")).Op(":=").Range().Id("from").Dot("SubSteps")).Block(
|
|
j.List(j.Id("subStep"), j.Err()).Op(":=").Id(m.defFuncReceiver).Dot("FromWorkflowSubStep").Call(j.Id("_s")),
|
|
j.If(j.Err().Op("!=").Nil()).Block(
|
|
j.Return(j.Nil(), j.Err()),
|
|
),
|
|
j.Id("subSteps").Op("=").Append(j.Id("subSteps"), j.Id("subStep")),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
assignSubSteps := func(sub bool) func(g *j.Group) {
|
|
if m.kind != v1beta1.WorkflowStepDefinitionKind || sub {
|
|
return func(g *j.Group) {}
|
|
}
|
|
return func(g *j.Group) {
|
|
g.Add(j.Id(m.defFuncReceiver).Dot("Base").Dot("SubSteps").Op("=").Id("subSteps"))
|
|
}
|
|
}
|
|
fromFuncRsv := func(sub bool) *j.Statement {
|
|
funcName := "From" + DefinitionKindToPascal[kind]
|
|
params := DefinitionKindToStatement[kind]
|
|
if sub {
|
|
funcName = "FromWorkflowSubStep"
|
|
params = j.Qual("common", "WorkflowSubStep")
|
|
}
|
|
return j.Func().
|
|
Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).
|
|
Id(funcName).
|
|
Params(j.Id("from").Add(params)).Params(j.Add(m.defStructPointer), j.Error()).
|
|
BlockFunc(func(g *j.Group) {
|
|
if kind == v1beta1.ComponentDefinitionKind {
|
|
g.Add(j.For(j.List(j.Id("_"), j.Id("trait")).Op(":=").Range().Id("from").Dot("Traits")).Block(
|
|
j.List(j.Id("_t"), j.Err()).Op(":=").Qual("sdkcommon", "FromTrait").Call(j.Id("trait")),
|
|
j.If(j.Err().Op("!=").Nil()).Block(
|
|
j.Return(j.Nil(), j.Err()),
|
|
),
|
|
j.Id(m.defFuncReceiver).Dot("Base").Dot("Traits").Op("=").Append(j.Id(m.defFuncReceiver).Dot("Base").Dot("Traits"), j.Id("_t")),
|
|
))
|
|
}
|
|
g.Add(j.Var().Id("properties").Id(m.specNameInPascalCase))
|
|
g.Add(
|
|
j.If(j.Id("from").Dot("Properties").Op("!=").Nil()).Block(
|
|
j.Err().Op(":=").Qual("json", "Unmarshal").Call(j.Id("from").Dot("Properties").Dot("Raw"), j.Op("&").Id("properties")),
|
|
j.If(j.Err().Op("!=").Nil()).Block(
|
|
j.Return(j.Nil(), j.Err()),
|
|
),
|
|
),
|
|
)
|
|
getSubSteps(sub)(g)
|
|
|
|
for _, prop := range kindBaseProperties[kind] {
|
|
g.Add(j.Id(m.defFuncReceiver).Dot("Base").Dot(prop).Op("=").Id("from").Dot(prop))
|
|
}
|
|
g.Add(j.Id(m.defFuncReceiver).Dot("Base").Dot("Type").Op("=").Id(m.typeVarName))
|
|
g.Add(j.Id(m.defFuncReceiver).Dot("Properties").Op("=").Id("properties"))
|
|
|
|
assignSubSteps(sub)(g)
|
|
g.Add(j.Return(j.Id(m.defFuncReceiver), j.Nil()))
|
|
},
|
|
)
|
|
}
|
|
|
|
// fromFunc is like fromFuncRsv but not having function receiver, returning an internal presentation
|
|
fromFunc := j.Func().
|
|
Id("From"+DefinitionKindToPascal[kind]).
|
|
Params(j.Id("from").Add(DefinitionKindToStatement[kind])).Params(j.Qual("apis", DefinitionKindToPascal[kind]), j.Error()).
|
|
Block(
|
|
j.Id(m.defFuncReceiver).Op(":=").Op("&").Id(m.defStructName).Values(j.Dict{}),
|
|
j.Return(j.Id(m.defFuncReceiver).Dot("From"+DefinitionKindToPascal[kind]).Call(j.Id("from"))),
|
|
)
|
|
fromSubFunc := j.Func().Id("FromWorkflowSubStep").
|
|
Params(j.Id("from").Qual("common", "WorkflowSubStep")).Params(j.Qual("apis", DefinitionKindToPascal[kind]), j.Error()).
|
|
Block(
|
|
j.Id(m.defFuncReceiver).Op(":=").Op("&").Id(m.defStructName).Values(j.Dict{}),
|
|
j.Return(j.Id(m.defFuncReceiver).Dot("FromWorkflowSubStep").Call(j.Id("from"))),
|
|
)
|
|
|
|
res := []*j.Statement{fromFuncRsv(false), fromFunc}
|
|
if m.kind == v1beta1.WorkflowStepDefinitionKind {
|
|
res = append(res, fromFuncRsv(true), fromSubFunc)
|
|
}
|
|
return res
|
|
}
|
|
|
|
// genDedicatedFunc generate functions for definition kinds
|
|
func (m *GoDefModifier) genDedicatedFunc() []*j.Statement {
|
|
switch m.kind {
|
|
case v1beta1.ComponentDefinitionKind:
|
|
setTraitFunc := j.Func().
|
|
Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).
|
|
Id("SetTraits").
|
|
Params(j.Id("traits").Op("...").Qual("apis", "Trait")).
|
|
Add(m.defStructPointer).
|
|
Block(
|
|
j.For(j.List(j.Id("_"), j.Id("addTrait")).Op(":=").Range().Id("traits")).Block(
|
|
j.Id("found").Op(":=").False(),
|
|
j.For(j.List(j.Id("i"), j.Id("_t")).Op(":=").Range().Id(m.defFuncReceiver).Dot("Base").Dot("Traits")).Block(
|
|
j.If(j.Id("_t").Dot("DefType").Call().Op("==").Id("addTrait").Dot("DefType").Call()).Block(
|
|
j.Id(m.defFuncReceiver).Dot("Base").Dot("Traits").Index(j.Id("i")).Op("=").Id("addTrait"),
|
|
j.Id("found").Op("=").True(),
|
|
j.Break(),
|
|
),
|
|
),
|
|
j.If(j.Op("!").Id("found")).Block(
|
|
j.Id(m.defFuncReceiver).Dot("Base").Dot("Traits").Op("=").Append(j.Id(m.defFuncReceiver).Dot("Base").Dot("Traits"), j.Id("addTrait")),
|
|
),
|
|
),
|
|
j.Return(j.Id(m.defFuncReceiver)),
|
|
)
|
|
getTraitFunc := j.Func().
|
|
Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).
|
|
Id("GetTrait").
|
|
Params(j.Id("typ").String()).
|
|
Params(j.Qual("apis", "Trait")).
|
|
Block(
|
|
j.For(j.List(j.Id("_"), j.Id("_t")).Op(":=").Range().Id(m.defFuncReceiver).Dot("Base").Dot("Traits")).Block(
|
|
j.If(j.Id("_t").Dot("DefType").Call().Op("==").Id("typ")).Block(
|
|
j.Return(j.Id("_t")),
|
|
),
|
|
),
|
|
j.Return(j.Nil()),
|
|
)
|
|
getAllTraitFunc := j.Func().
|
|
Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).
|
|
Id("GetAllTraits").
|
|
Params().
|
|
Params(j.Index().Qual("apis", "Trait")).
|
|
Block(
|
|
j.Return(j.Id(m.defFuncReceiver).Dot("Base").Dot("Traits")),
|
|
)
|
|
|
|
return []*j.Statement{setTraitFunc, getTraitFunc, getAllTraitFunc}
|
|
case v1beta1.WorkflowStepDefinitionKind:
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *GoDefModifier) genNameTypeFunc() []*j.Statement {
|
|
nameFunc := j.Func().Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).Id(DefinitionKindToPascal[m.kind] + "Name").Params().String().Block(
|
|
j.Return(j.Id(m.defFuncReceiver).Dot("Base").Dot("Name")),
|
|
)
|
|
typeFunc := j.Func().Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).Id("DefType").Params().String().Block(
|
|
j.Return(j.Id(m.typeVarName)),
|
|
)
|
|
switch m.kind {
|
|
case v1beta1.ComponentDefinitionKind, v1beta1.WorkflowStepDefinitionKind, v1beta1.PolicyDefinitionKind:
|
|
return []*j.Statement{nameFunc, typeFunc}
|
|
case v1beta1.TraitDefinitionKind:
|
|
return []*j.Statement{typeFunc}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *GoDefModifier) genUnmarshalFunc() []*j.Statement {
|
|
return []*j.Statement{j.Null()}
|
|
}
|
|
|
|
func (m *GoDefModifier) genBaseSetterFunc() []*j.Statement {
|
|
baseFuncArgs := map[string][]struct {
|
|
funcName string
|
|
argName string
|
|
argType *j.Statement
|
|
dst *j.Statement
|
|
isAppend bool
|
|
}{
|
|
v1beta1.ComponentDefinitionKind: {
|
|
{funcName: "DependsOn", argName: "dependsOn", argType: j.Index().String()},
|
|
{funcName: "Inputs", argName: "input", argType: j.Qual("common", "StepInputs")},
|
|
{funcName: "Outputs", argName: "output", argType: j.Qual("common", "StepOutputs")},
|
|
{funcName: "AddDependsOn", argName: "dependsOn", argType: j.String(), isAppend: true, dst: j.Dot("DependsOn")},
|
|
// TODO: uncomment this after https://github.com/kubevela/workflow/pull/125 is released.
|
|
// {funcName: "AddInput", argName: "input", argType: Qual("common", "StepInputs"), isAppend: true, dst: "Inputs"},
|
|
// {funcName: "AddOutput", argName: "output", argType: Qual("common", "StepOutputs"), isAppend: true, dst: "Outputs"},
|
|
},
|
|
v1beta1.WorkflowStepDefinitionKind: {
|
|
{funcName: "If", argName: "_if", argType: j.String()},
|
|
{funcName: "Alias", argName: "alias", argType: j.String(), dst: j.Dot("Meta").Dot("Alias")},
|
|
{funcName: "Timeout", argName: "timeout", argType: j.String()},
|
|
{funcName: "DependsOn", argName: "dependsOn", argType: j.Index().String()},
|
|
{funcName: "Inputs", argName: "input", argType: j.Qual("common", "StepInputs")},
|
|
{funcName: "Outputs", argName: "output", argType: j.Qual("common", "StepOutputs")},
|
|
// {funcName: "AddInput", argName: "input", argType: Qual("common", "StepInputs"), isAppend: true, dst: "Inputs"},
|
|
// {funcName: "AddOutput", argName: "output", argType: Qual("common", "StepOutputs"), isAppend: true, dst: "Outputs"},
|
|
},
|
|
}
|
|
baseFuncs := make([]*j.Statement, 0)
|
|
for _, fn := range baseFuncArgs[m.kind] {
|
|
if fn.dst == nil {
|
|
fn.dst = j.Dot(fn.funcName)
|
|
}
|
|
f := j.Func().
|
|
Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).
|
|
Id(fn.funcName).
|
|
Params(j.Id(fn.argName).Add(fn.argType)).
|
|
Add(m.defStructPointer).
|
|
BlockFunc(func(g *j.Group) {
|
|
field := j.Id(m.defFuncReceiver).Dot("Base").Add(fn.dst)
|
|
if fn.isAppend {
|
|
g.Add(field.Clone().Op("=").Append(field.Clone(), j.Id(fn.argName)))
|
|
} else {
|
|
g.Add(field.Clone().Op("=").Id(fn.argName))
|
|
}
|
|
g.Add(j.Return(j.Id(m.defFuncReceiver)))
|
|
})
|
|
baseFuncs = append(baseFuncs, f)
|
|
}
|
|
return baseFuncs
|
|
}
|
|
|
|
func (m *GoDefModifier) genAddSubStepFunc() *j.Statement {
|
|
if m.name != "step-group" || m.kind != v1beta1.WorkflowStepDefinitionKind {
|
|
return j.Null()
|
|
}
|
|
subList := j.Id(m.defFuncReceiver).Dot("Base").Dot("SubSteps")
|
|
return j.Func().
|
|
Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).
|
|
Id("AddSubStep").
|
|
Params(j.Id("subStep").Qual("apis", "WorkflowStep")).
|
|
Add(m.defStructPointer).
|
|
Block(
|
|
subList.Clone().Op("=").Append(subList.Clone(), j.Id("subStep")),
|
|
j.Return(j.Id(m.defFuncReceiver)),
|
|
)
|
|
}
|
|
|
|
// exportMethods will export methods from definition spec struct to definition struct
|
|
func (m *GoDefModifier) exportMethods() error {
|
|
fileLoc := path.Join(m.defDir, m.nameInSnakeCase+".go")
|
|
// nolint:gosec
|
|
file, err := os.ReadFile(fileLoc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var fileStr = string(file)
|
|
from := fmt.Sprintf("*%sSpec", m.nameInPascalCase)
|
|
to := fmt.Sprintf("*%s", m.defStructName)
|
|
// replace all the function receiver but not below functions
|
|
// New{m.nameInPascalCase}SpecWith
|
|
// New{m.nameInPascalCase}Spec
|
|
fileStr = regexp.MustCompile(fmt.Sprintf(`func \(o \%s\) ([()\[\]{}\w ]+)\%s([ )])`, from, from)).ReplaceAllString(fileStr, fmt.Sprintf("func (o %s) $1%s$2", to, to))
|
|
fileStr = strings.ReplaceAll(fileStr, "func (o "+from, "func (o "+to)
|
|
|
|
// replace all function receiver in function body
|
|
// o.foo -> o.Properties.foo
|
|
// o.Base keeps the same
|
|
// seek the MarshalJSON function, replace functions before it
|
|
parts := strings.SplitN(fileStr, "MarshalJSON", 2)
|
|
if len(parts) != 2 {
|
|
return fmt.Errorf("can't find MarshalJSON function")
|
|
}
|
|
parts[0] = strings.ReplaceAll(parts[0], "o.", "o.Properties.")
|
|
parts[0] = strings.ReplaceAll(parts[0], "o.Properties.Base", "o.Base")
|
|
fileStr = parts[0] + "MarshalJSON" + parts[1]
|
|
|
|
return os.WriteFile(fileLoc, []byte(fileStr), 0600)
|
|
}
|
|
|
|
func (m *GoDefModifier) addValidateTraits() error {
|
|
if m.kind != v1beta1.ComponentDefinitionKind {
|
|
return nil
|
|
}
|
|
fileLoc := path.Join(m.defDir, m.nameInSnakeCase+".go")
|
|
// nolint:gosec
|
|
file, err := os.ReadFile(fileLoc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var fileStr = string(file)
|
|
buf := bytes.Buffer{}
|
|
|
|
err = j.For(j.List(j.Id("i"), j.Id("v").Op(":=").Range().Id("o").Dot("Base").Dot("Traits"))).Block(
|
|
j.If(j.Id("err").Op(":=").Id("v").Dot("Validate").Call().Op(";").Id("err").Op("!=").Nil()).Block(
|
|
j.Return(j.Qual("fmt", "Errorf").Call(j.Lit("traits[%d] %s in %s component is invalid: %w"), j.Id("i"), j.Id("v").Dot("DefType").Call(), j.Id(m.typeVarName), j.Id("err"))),
|
|
),
|
|
).Render(&buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// add validate trait part in Validate function
|
|
exp := regexp.MustCompile(`Validate\(\)((.|\n)*?)(return nil)`)
|
|
s := buf.String()
|
|
fileStr = exp.ReplaceAllString(fileStr, fmt.Sprintf("Validate()$1\n%s\n$3", s))
|
|
|
|
return os.WriteFile(fileLoc, []byte(fileStr), 0600)
|
|
}
|
|
func (m *GoModuleModifier) format() error {
|
|
// check if gofmt is installed
|
|
// todo (chivalryq): support go mod tidy for sub-module
|
|
|
|
formatters := []string{"gofmt", "goimports"}
|
|
var formatterPaths []string
|
|
allFormattersInstalled := true
|
|
for _, formatter := range formatters {
|
|
p, err := exec.LookPath(formatter)
|
|
if err != nil {
|
|
allFormattersInstalled = false
|
|
break
|
|
}
|
|
formatterPaths = append(formatterPaths, p)
|
|
}
|
|
if allFormattersInstalled {
|
|
for _, fmter := range formatterPaths {
|
|
if m.Verbose {
|
|
fmt.Printf("Use %s to format code\n", fmter)
|
|
}
|
|
// nolint:gosec
|
|
cmd := exec.Command(fmter, "-w", m.apiDir)
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return errors.Wrap(err, string(output))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
// fallback to use go lib
|
|
if m.Verbose {
|
|
fmt.Println("At least one of linters is not installed, use go/format lib to format code")
|
|
}
|
|
|
|
// format all .go files
|
|
return filepath.Walk(m.apiDir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !strings.HasSuffix(path, ".go") {
|
|
return nil
|
|
}
|
|
// nolint:gosec
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "read file %s", path)
|
|
}
|
|
formatted, err := format.Source(content)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "format file %s", path)
|
|
}
|
|
err = os.WriteFile(path, formatted, 0600)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "write file %s", path)
|
|
}
|
|
return nil
|
|
})
|
|
}
|