istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/msg/generate.main.go (about) 1 // Copyright Istio 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 //go:build ignore 16 // +build ignore 17 18 package main 19 20 import ( 21 "bytes" 22 "fmt" 23 "os" 24 "regexp" 25 "text/template" 26 27 "sigs.k8s.io/yaml" 28 ) 29 30 const ( 31 codeRegex = `^IST\d\d\d\d$` 32 nameRegex = `^[[:upper:]]\w*$` 33 ) 34 35 // Utility for generating messages.gen.go. Called from gen.go 36 func main() { 37 if len(os.Args) != 3 { 38 fmt.Println("Invalid args:", os.Args) 39 os.Exit(-1) 40 } 41 42 input := os.Args[1] 43 output := os.Args[2] 44 45 m, err := read(input) 46 if err != nil { 47 fmt.Println("Error reading metadata:", err) 48 os.Exit(-2) 49 } 50 51 err = validate(m) 52 if err != nil { 53 fmt.Println("Error validating messages:", err) 54 os.Exit(-3) 55 } 56 57 code, err := generate(m) 58 if err != nil { 59 fmt.Println("Error generating code:", err) 60 os.Exit(-4) 61 } 62 63 if err = os.WriteFile(output, []byte(code), os.ModePerm); err != nil { 64 fmt.Println("Error writing output file:", err) 65 os.Exit(-5) 66 } 67 } 68 69 func read(path string) (*messages, error) { 70 b, err := os.ReadFile(path) 71 if err != nil { 72 return nil, fmt.Errorf("unable to read input file: %v", err) 73 } 74 75 m := &messages{} 76 77 if err := yaml.Unmarshal(b, m); err != nil { 78 return nil, err 79 } 80 81 return m, nil 82 } 83 84 // Enforce that names and codes follow expected regex and are unique 85 func validate(ms *messages) error { 86 codes := make(map[string]bool) 87 names := make(map[string]bool) 88 89 for _, m := range ms.Messages { 90 matched, err := regexp.MatchString(codeRegex, m.Code) 91 if err != nil { 92 return err 93 } 94 if !matched { 95 return fmt.Errorf("Error code for message %q must follow the regex %s", m.Name, codeRegex) 96 } 97 98 if codes[m.Code] { 99 return fmt.Errorf("Error codes must be unique, %q defined more than once", m.Code) 100 } 101 codes[m.Code] = true 102 103 matched, err = regexp.MatchString(nameRegex, m.Name) 104 if err != nil { 105 return err 106 } 107 if !matched { 108 return fmt.Errorf("Name for message %q must follow the regex %s", m.Name, nameRegex) 109 } 110 111 if names[m.Name] { 112 return fmt.Errorf("Message names must be unique, %q defined more than once", m.Name) 113 } 114 names[m.Name] = true 115 } 116 return nil 117 } 118 119 var tmpl = ` 120 // Code generated by generate.main.go. DO NOT EDIT. 121 122 package msg 123 124 import ( 125 "istio.io/istio/pkg/config/analysis/diag" 126 "istio.io/istio/pkg/config/resource" 127 ) 128 129 var ( 130 {{- range .Messages}} 131 // {{.Name}} defines a diag.MessageType for message "{{.Name}}". 132 // Description: {{.Description}} 133 {{.Name}} = diag.NewMessageType(diag.{{.Level}}, "{{.Code}}", "{{.Template}}") 134 {{end}} 135 ) 136 137 // All returns a list of all known message types. 138 func All() []*diag.MessageType { 139 return []*diag.MessageType{ 140 {{- range .Messages}} 141 {{.Name}}, 142 {{- end}} 143 } 144 } 145 146 {{range .Messages}} 147 // New{{.Name}} returns a new diag.Message based on {{.Name}}. 148 func New{{.Name}}(r *resource.Instance{{range .Args}}, {{.Name}} {{.Type}}{{end}}) diag.Message { 149 return diag.NewMessage( 150 {{.Name}}, 151 r, 152 {{- range .Args}} 153 {{.Name}}, 154 {{- end}} 155 ) 156 } 157 {{end}} 158 ` 159 160 func generate(m *messages) (string, error) { 161 t := template.Must(template.New("code").Parse(tmpl)) 162 163 var b bytes.Buffer 164 if err := t.Execute(&b, m); err != nil { 165 return "", err 166 } 167 return b.String(), nil 168 } 169 170 type messages struct { 171 Messages []message `json:"messages"` 172 } 173 174 type message struct { 175 Name string `json:"name"` 176 Code string `json:"code"` 177 Level string `json:"level"` 178 Description string `json:"description"` 179 Template string `json:"template"` 180 Url string `json:"url"` 181 Args []arg `json:"args"` 182 } 183 184 type arg struct { 185 Name string `json:"name"` 186 Type string `json:"type"` 187 }