1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package main
17
18 import (
19 "fmt"
20 "go/ast"
21 "go/parser"
22 "go/token"
23 "os"
24 "path/filepath"
25 "strings"
26
27 "github.com/go-logr/zapr"
28 "github.com/pingcap/errors"
29 "go.uber.org/zap"
30 )
31
32 var (
33 zapLogger, _ = zap.NewDevelopment()
34 log = zapr.NewLogger(zapLogger)
35 )
36
37 type metadata struct {
38 Type string
39 OneShotExp string
40 IsExperiment bool
41 EnableUpdate bool
42 }
43
44 func main() {
45 implCode := boilerplate + implImport
46
47 testCode := boilerplate + testImport
48 initImpl := ""
49 scheduleImpl := ""
50
51 workflowGenerator := newWorkflowCodeGenerator(nil)
52 workflowTestGenerator := newWorkflowTestCodeGenerator(nil)
53
54 scheduleGenerator := newScheduleCodeGenerator(nil)
55
56 frontendGenerator := newFrontendCodeGenerator(nil)
57
58 filepath.Walk("./api/v1alpha1", func(path string, info os.FileInfo, err error) error {
59 log := log.WithValues("file", path)
60
61 if err != nil {
62 log.Error(err, "fail to walk in directory")
63 return err
64 }
65 if info.IsDir() {
66 return nil
67 }
68 if !strings.HasSuffix(info.Name(), ".go") {
69 return nil
70 }
71 if strings.HasPrefix(info.Name(), "zz_generated") {
72 return nil
73 }
74
75 fset := token.NewFileSet()
76 file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
77 if err != nil {
78 log.Error(err, "fail to parse file")
79 return err
80 }
81
82 cmap := ast.NewCommentMap(fset, file, file.Comments)
83
84 out:
85 for node, commentGroups := range cmap {
86 for _, commentGroup := range commentGroups {
87 var oneShotExp string
88 var enableUpdate bool
89
90 for _, comment := range commentGroup.List {
91 if strings.Contains(comment.Text, "+chaos-mesh:webhook:enableUpdate") {
92 enableUpdate = true
93 break
94 }
95 }
96
97 for _, comment := range commentGroup.List {
98 if strings.Contains(comment.Text, "+chaos-mesh:base") {
99 baseType, err := getType(fset, node, comment)
100 if err != nil {
101 return err
102 }
103 if strings.Contains(comment.Text, "+chaos-mesh:base") {
104 if baseType.Name.Name != "Workflow" {
105 implCode += generateImpl(baseType.Name.Name, oneShotExp, false, enableUpdate)
106 initImpl += generateInit(baseType.Name.Name, false)
107 }
108 }
109 continue out
110 }
111 }
112
113 for _, comment := range commentGroup.List {
114 if strings.Contains(comment.Text, "+chaos-mesh:oneshot") {
115 oneShotExp = strings.TrimPrefix(comment.Text, "// +chaos-mesh:oneshot=")
116 log.Info("decode oneshot expression", "expression", oneShotExp)
117 }
118 }
119 for _, comment := range commentGroup.List {
120 if strings.Contains(comment.Text, "+chaos-mesh:experiment") {
121 baseType, err := getType(fset, node, comment)
122 if err != nil {
123 return err
124 }
125
126 if baseType.Name.Name != "Workflow" {
127 implCode += generateImpl(baseType.Name.Name, oneShotExp, true, enableUpdate)
128 initImpl += generateInit(baseType.Name.Name, true)
129 testCode += generateTest(baseType.Name.Name)
130 workflowGenerator.AppendTypes(baseType.Name.Name)
131 workflowTestGenerator.AppendTypes(baseType.Name.Name)
132 frontendGenerator.AppendTypes(baseType.Name.Name)
133 }
134 scheduleImpl += generateScheduleRegister(baseType.Name.Name)
135 scheduleGenerator.AppendTypes(baseType.Name.Name)
136 continue out
137 }
138 }
139 }
140 }
141
142 return nil
143 })
144
145 implCode += fmt.Sprintf(`
146 func init() {
147 %s
148 %s
149 }
150 `, initImpl, scheduleImpl)
151 file, err := os.Create("./api/v1alpha1/zz_generated.chaosmesh.go")
152 if err != nil {
153 log.Error(err, "fail to create file")
154 os.Exit(1)
155 }
156 fmt.Fprint(file, implCode)
157
158 testCode += testInit
159 file, err = os.Create("./api/v1alpha1/zz_generated.chaosmesh_test.go")
160 if err != nil {
161 log.Error(err, "fail to create file")
162 os.Exit(1)
163 }
164 fmt.Fprint(file, testCode)
165
166 file, err = os.Create("./api/v1alpha1/zz_generated.workflow.chaosmesh.go")
167 if err != nil {
168 log.Error(err, "fail to create file")
169 os.Exit(1)
170 }
171 fmt.Fprint(file, workflowGenerator.Render())
172
173 file, err = os.Create("./api/v1alpha1/zz_generated.workflow.chaosmesh_test.go")
174 if err != nil {
175 log.Error(err, "fail to create file")
176 os.Exit(1)
177 }
178 fmt.Fprint(file, workflowTestGenerator.Render())
179
180 file, err = os.Create("./api/v1alpha1/zz_generated.schedule.chaosmesh.go")
181 if err != nil {
182 log.Error(err, "fail to create file")
183 os.Exit(1)
184 }
185 fmt.Fprint(file, scheduleGenerator.Render())
186
187 file, err = os.Create("./ui/app/src/api/zz_generated.frontend.chaos-mesh.ts")
188 if err != nil {
189 log.Error(err, "fail to create file")
190 os.Exit(1)
191 }
192 fmt.Fprint(file, frontendGenerator.Render())
193 }
194
195 func getType(fset *token.FileSet, node ast.Node, comment *ast.Comment) (*ast.TypeSpec, error) {
196 log.Info("build", "pos", fset.Position(comment.Pos()))
197 decl, ok := node.(*ast.GenDecl)
198 if !ok {
199 err := errors.New("node is not a *ast.GenDecl")
200 log.Error(err, "fail to get type")
201 return nil, err
202 }
203
204 if decl.Tok != token.TYPE {
205 err := errors.New("node.Tok is not token.TYPE")
206 log.Error(err, "fail to get type")
207 return nil, err
208 }
209
210 baseType, ok := decl.Specs[0].(*ast.TypeSpec)
211 if !ok {
212 err := errors.New("node is not a *ast.TypeSpec")
213 log.Error(err, "fail to get type")
214 return nil, err
215 }
216 return baseType, nil
217 }
218