...

Source file src/github.com/chaos-mesh/chaos-mesh/cmd/chaos-builder/main.go

Documentation: github.com/chaos-mesh/chaos-mesh/cmd/chaos-builder

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    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