...

Source file src/github.com/chaos-mesh/chaos-mesh/cmd/chaos-builder/workflow.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  	"bytes"
    20  	"fmt"
    21  	"strings"
    22  	"text/template"
    23  
    24  	"github.com/iancoleman/strcase"
    25  )
    26  
    27  // struct workflowCodeGenerator will render content of one file contains code blocks that required by workflow
    28  type workflowCodeGenerator struct {
    29  	// name of each Kind of chaos, for example: PodChaos, IOChaos, DNSChaos
    30  	chaosTypes []string
    31  }
    32  
    33  func newWorkflowCodeGenerator(types []string) *workflowCodeGenerator {
    34  	return &workflowCodeGenerator{chaosTypes: types}
    35  }
    36  
    37  func (it *workflowCodeGenerator) AppendTypes(typeName string) {
    38  	it.chaosTypes = append(it.chaosTypes, typeName)
    39  }
    40  
    41  func (it *workflowCodeGenerator) Render() string {
    42  
    43  	workflowTemplateTypesEntries := ""
    44  	for _, item := range it.chaosTypes {
    45  		workflowTemplateTypesEntries += generateTemplateTypes(item)
    46  	}
    47  
    48  	embedChaosEntries := ""
    49  	for _, item := range it.chaosTypes {
    50  		embedChaosEntries += generateEmbedChaos(item)
    51  	}
    52  
    53  	spawnObjectMethod := ""
    54  	for _, item := range it.chaosTypes {
    55  		spawnObjectMethod += generateMethodItem(item, spawnObjectEntryTemplate)
    56  	}
    57  	restoreObjectMethod := ""
    58  	for _, item := range it.chaosTypes {
    59  		restoreObjectMethod += generateMethodItem(item, restoreObjectEntryTemplate)
    60  	}
    61  	spawnListMethod := ""
    62  	for _, item := range it.chaosTypes {
    63  		spawnListMethod += generateSpawnListMethodItem(item)
    64  	}
    65  	allChaosTemplateTypeEntries := ""
    66  	for _, item := range it.chaosTypes {
    67  		allChaosTemplateTypeEntries += fmt.Sprintf(`	Type%s,
    68  `, item)
    69  	}
    70  
    71  	genericChaosListImplementations := ""
    72  	for _, item := range it.chaosTypes {
    73  		genericChaosListImplementations += generateGenericChaosList(item)
    74  	}
    75  
    76  	imports := `import (
    77  	"github.com/pkg/errors"
    78  )
    79  `
    80  
    81  	workflowTemplateTypesCodes := fmt.Sprintf(`%s
    82  
    83  %s
    84  
    85  const (
    86  %s
    87  )
    88  
    89  var allChaosTemplateType = []TemplateType{
    90  	TypeSchedule,
    91  %s
    92  }
    93  
    94  type EmbedChaos struct {
    95  %s
    96  }
    97  
    98  func (it *EmbedChaos) SpawnNewObject(templateType TemplateType) (GenericChaos, error) {
    99  	switch templateType {
   100  %s
   101  	default:
   102  		return nil, errors.Wrapf(errInvalidValue, "unknown template type %%s", templateType)
   103  	}
   104  }
   105  
   106  func (it *EmbedChaos) RestoreChaosSpec(root interface{}) error {
   107  	switch chaos := root.(type) {
   108  %s
   109  	default:
   110  		return errors.Wrapf(errInvalidValue, "unknown chaos %%#v", root)
   111  	}
   112  }
   113  
   114  func (it *EmbedChaos) SpawnNewList(templateType TemplateType) (GenericChaosList, error) {
   115  	switch templateType {
   116  %s
   117  	default:
   118  		return nil, errors.Wrapf(errInvalidValue, "unknown template type %%s", templateType)
   119  	}
   120  }
   121  
   122  %s
   123  `,
   124  		boilerplate,
   125  		imports,
   126  		workflowTemplateTypesEntries,
   127  		allChaosTemplateTypeEntries,
   128  		embedChaosEntries,
   129  		spawnObjectMethod,
   130  		restoreObjectMethod,
   131  		spawnListMethod,
   132  		genericChaosListImplementations,
   133  	)
   134  
   135  	return workflowTemplateTypesCodes
   136  }
   137  
   138  const workflowTemplateTypeEntryTemplate = `	Type{{.Type}} TemplateType = "{{.Type}}"
   139  `
   140  
   141  func generateTemplateTypes(typeName string) string {
   142  	tmpl, err := template.New("workflowTemplates").Parse(workflowTemplateTypeEntryTemplate)
   143  	if err != nil {
   144  		log.Error(err, "fail to build template")
   145  		return ""
   146  	}
   147  
   148  	buf := new(bytes.Buffer)
   149  	err = tmpl.Execute(buf, &metadata{
   150  		Type: typeName,
   151  	})
   152  	if err != nil {
   153  		log.Error(err, "fail to execute template")
   154  		return ""
   155  	}
   156  
   157  	return buf.String()
   158  }
   159  
   160  const embedChaosEntryTemplate = `	// +optional
   161  	{{.Type}} *{{.Type}}Spec ` + "`" + `json:"{{.JsonField}},omitempty"` + "`" + `
   162  `
   163  
   164  func generateEmbedChaos(typeName string) string {
   165  	value := struct {
   166  		Type      string
   167  		JsonField string
   168  	}{
   169  		Type:      typeName,
   170  		JsonField: lowercaseCamelCase(typeName),
   171  	}
   172  	tmpl, err := template.New("workflowTemplates").Parse(embedChaosEntryTemplate)
   173  	if err != nil {
   174  		log.Error(err, "fail to build template")
   175  		return ""
   176  	}
   177  
   178  	buf := new(bytes.Buffer)
   179  	err = tmpl.Execute(buf, &value)
   180  	if err != nil {
   181  		log.Error(err, "fail to execute template")
   182  		return ""
   183  	}
   184  
   185  	return buf.String()
   186  }
   187  
   188  func lowercaseCamelCase(str string) string {
   189  	// here are some name thing issue about the acronyms, we used ALLCAP name in chaos kind, like DNSChaos or JVMChaos,
   190  	// library could not resolve that well, so we just manually do it.
   191  	if strings.Contains(str, "Chaos") {
   192  		position := strings.Index(str, "Chaos")
   193  		return strings.ToLower(str[:position]) + str[position:]
   194  	}
   195  	return strcase.ToLowerCamel(str)
   196  }
   197  
   198  const spawnObjectEntryTemplate = `	case Type{{.Type}}:
   199  		result := {{.Type}}{}
   200  		result.Spec = *it.{{.Type}}
   201  		return &result, nil
   202  `
   203  
   204  const restoreObjectEntryTemplate = `	case *{{.Type}}:
   205  		*it.{{.Type}} = chaos.Spec
   206  		return nil
   207  `
   208  
   209  func generateMethodItem(typeName, methodTemplate string) string {
   210  	tmpl, err := template.New("fillMethodEntry").Parse(methodTemplate)
   211  	if err != nil {
   212  		log.Error(err, "fail to build template")
   213  		return ""
   214  	}
   215  
   216  	buf := new(bytes.Buffer)
   217  	err = tmpl.Execute(buf, &metadata{
   218  		Type: typeName,
   219  	})
   220  	if err != nil {
   221  		log.Error(err, "fail to execute template")
   222  		return ""
   223  	}
   224  
   225  	return buf.String()
   226  }
   227  
   228  const spawnListEntryTemplate = `	case Type{{.Type}}:
   229  		result := {{.Type}}List{}
   230  		return &result, nil
   231  `
   232  
   233  func generateSpawnListMethodItem(typeName string) string {
   234  	tmpl, err := template.New("fillingMethod").Parse(spawnListEntryTemplate)
   235  	if err != nil {
   236  		log.Error(err, "fail to build template")
   237  		return ""
   238  	}
   239  
   240  	buf := new(bytes.Buffer)
   241  	err = tmpl.Execute(buf, &metadata{
   242  		Type: typeName,
   243  	})
   244  	if err != nil {
   245  		log.Error(err, "fail to execute template")
   246  		return ""
   247  	}
   248  
   249  	return buf.String()
   250  }
   251  
   252  const genericChaosList = `func (in *{{.Type}}List) GetItems() []GenericChaos {
   253  	var result []GenericChaos
   254  	for _, item := range in.Items {
   255  		item := item
   256  		result = append(result, &item)
   257  	}
   258  	return result
   259  }
   260  `
   261  
   262  func generateGenericChaosList(typeName string) string {
   263  	tmpl, err := template.New("genericChaosList").Parse(genericChaosList)
   264  	if err != nil {
   265  		log.Error(err, "fail to build template")
   266  		return ""
   267  	}
   268  
   269  	buf := new(bytes.Buffer)
   270  	err = tmpl.Execute(buf, &metadata{
   271  		Type: typeName,
   272  	})
   273  	if err != nil {
   274  		log.Error(err, "fail to execute template")
   275  		return ""
   276  	}
   277  
   278  	return buf.String()
   279  }
   280