Skip to content

Commit 19679c6

Browse files
committed
MEDIUM: add code generator server_params_runtime and fix no-check-send-proxy server param option
1 parent e798826 commit 19679c6

19 files changed

+588
-922
lines changed

Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ spec:
3030
.PHONY: models
3131
models: gentypes spec swagger-check
3232
./bin/swagger generate model --additional-initialism=FCGI -f ${PROJECT_PATH}/specification/build/haproxy_spec.yaml -r ${PROJECT_PATH}/specification/copyright.txt -m models -t ${PROJECT_PATH}
33+
rm -rf models/server_params_prepare_for_runtime.go
3334
rm -rf models/*_compare.go
3435
rm -rf models/*_compare_test.go
3536
go run cmd/struct_equal_generator/*.go -l ${PROJECT_PATH}/specification/copyright.txt ${PROJECT_PATH}/models
3637
go run cmd/struct_tags_checker/*.go ${PROJECT_PATH}/models
3738
go run cmd/kubebuilder_marker_generator/*.go ${PROJECT_PATH}/models
39+
go run cmd/server_params_runtime/*.go ${PROJECT_PATH}/models
3840

3941
.PHONY: swagger-check
4042
swagger-check:

cmd/server_params_runtime/README.md

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# ![HAProxy](../../assets/images/haproxy-weblogo-210x49.png "HAProxy")
2+
3+
## Generator for: `models/server_params_prepare_for_runtime.go`
4+
5+
This genetator generates a file that will contains the needed functions to prepare a ServerParams to use in combination with `add server` command on the runtime socket.
6+
7+
This file will contain 3 functions:
8+
9+
```
10+
func (p *ServerParams) prepareForRuntimeDoNotSendDisabledFields()
11+
func (p *ServerParams) prepareForRuntimeDoNotSendEnabledFields()
12+
func (p *ServerParams) PrepareFieldsForRuntimeAddServer()
13+
```
14+
15+
They are used for example in Ingress Controller the following way:
16+
```
17+
params.PrepareFieldsForRuntimeAddServer()
18+
serverParams := configuration.SerializeServerParams(defaultServer.ServerParams)
19+
res := cp_params.ServerOptionsString(serverParams)
20+
```
21+
22+
### func (p *ServerParams) prepareForRuntimeDoNotSendDisabledFields()
23+
For example for `Check` that has the values [enabled disabled].
24+
25+
- if the value is `enabled` we must send `add server check`
26+
- if the value is `disabled` we must not send `add server no-check` as `no-check` is not allowed on a dynamic server
27+
28+
`no-check` is the default value.
29+
30+
The purpose is to set `Check` to "" when the value was `disabled` so the commands sent are:
31+
- `add server check` if value is `enabled`
32+
- `add server` if value is `disabled`
33+
34+
35+
### func (p *ServerParams) prepareForRuntimeDoNotSendEnabledFields()
36+
It's just the opposite.
37+
38+
For example for `NoSslv3`
39+
40+
- if `enabled` we must send `no-sslv3`
41+
- if `disabled` we must not sent an option
42+
43+
44+
### func (p *ServerParams) PrepareFieldsForRuntimeAddServer()`
45+
is just calling both `PrepareForRuntimeDoNotSendDisabledFields` and `PrepareForRuntimeDoNotSendEnabledFields`
46+
47+
48+
## WHAT TO DO
49+
50+
Just fill in `server_params_prepare_for_runtime.go` the map:
51+
- `ServerParamsPrepareForRuntimeMap`
52+
53+
for each field that has an `// Enum: [enabled disabled]"`
54+
with the correct function to use
55+
56+
## Ensure that the map is always filled in a field is added into ServerParams: CI check
57+
58+
The generator checks all fields in ServerParams that have `// Enum: [enabled disabled]"` have an entry in the `ServerParamsPrepareForRuntimeMap`.
59+
60+
If a new field is added and not declared in `ServerParamsPrepareForRuntimeMap`, the generator fails with an error and this will make the CI fail.

cmd/server_params_runtime/args.go

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"errors"
6+
"fmt"
7+
"io/fs"
8+
"log"
9+
"os"
10+
"path"
11+
"path/filepath"
12+
"strings"
13+
)
14+
15+
type Args struct {
16+
Licence string
17+
LicencePath string
18+
Directory string
19+
Selector string
20+
Files []string
21+
}
22+
23+
func (a *Args) Parse() error { //nolint:gocognit
24+
selector, err := os.Getwd()
25+
if err != nil {
26+
log.Panic(err)
27+
}
28+
for i := 1; i < len(os.Args); i++ {
29+
val := os.Args[i]
30+
switch {
31+
case val == "-l":
32+
if i+1 >= len(os.Args) {
33+
return errors.New("missing licence file after -l")
34+
}
35+
a.LicencePath = os.Args[i+1]
36+
i++
37+
case strings.HasPrefix(val, "--licence="):
38+
a.LicencePath = strings.TrimPrefix(val, "--licence=")
39+
default:
40+
selector = val
41+
}
42+
}
43+
44+
if a.LicencePath != "" {
45+
var licence []byte
46+
licence, err = os.ReadFile(a.LicencePath)
47+
if err != nil {
48+
return err
49+
}
50+
lines := strings.Split(string(licence), "\n")
51+
var s strings.Builder
52+
for _, line := range lines {
53+
s.WriteString("// ")
54+
s.WriteString(line)
55+
s.WriteString("\n")
56+
}
57+
a.Licence = s.String()
58+
}
59+
isDirectory := false
60+
file, err := os.Open(selector)
61+
if err == nil {
62+
var fileInfo fs.FileInfo
63+
fileInfo, err = file.Stat()
64+
if err == nil {
65+
isDirectory = fileInfo.IsDir()
66+
}
67+
}
68+
if selector == "*" || selector == "." || isDirectory {
69+
err = filepath.Walk(selector, func(path string, info os.FileInfo, err error) error {
70+
if err != nil {
71+
return err
72+
}
73+
if info.IsDir() {
74+
return nil
75+
}
76+
77+
if strings.HasSuffix(path, serverParamFileName) {
78+
fmt.Println(path) //nolint:forbidigo
79+
a.Files = append(a.Files, path)
80+
}
81+
return nil
82+
})
83+
if err != nil {
84+
log.Panic(err)
85+
}
86+
} else {
87+
a.Files = append(a.Files, selector)
88+
}
89+
a.Selector = selector
90+
if isDirectory {
91+
a.Directory = selector
92+
} else {
93+
a.Directory = path.Dir(selector)
94+
}
95+
96+
return nil
97+
}

cmd/server_params_runtime/embed.go

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
)
6+
7+
//go:embed generate.tmpl
8+
var tmplResetEnumDisabledFields string
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"go/format"
7+
"go/parser"
8+
"go/token"
9+
"os"
10+
11+
"golang.org/x/tools/imports"
12+
)
13+
14+
func fmtFile(fileName string) error {
15+
fset := token.NewFileSet()
16+
node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
17+
if err != nil {
18+
return err
19+
}
20+
21+
// gofmt the modified file
22+
var buf bytes.Buffer
23+
if err = format.Node(&buf, fset, node); err != nil {
24+
return err
25+
}
26+
27+
formattedCode, err := imports.Process(fileName, buf.Bytes(), nil)
28+
if err != nil {
29+
return fmt.Errorf("failed to perform goimports: %w", err)
30+
}
31+
32+
return os.WriteFile(fileName, formattedCode, 0o600)
33+
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Code generated by server_params_runtime; DO NOT EDIT.
2+
3+
{{ .Licence }}
4+
5+
package {{ .Package }}
6+
7+
8+
func (s ServerParams){{ .PrepareFieldsForRuntimeAddServer }}()* ServerParams {
9+
serverParams := new(ServerParams)
10+
// *defaultServer = p
11+
a, _ := s.MarshalBinary()
12+
_ = serverParams.UnmarshalBinary(a)
13+
14+
serverParams.{{ .DoNotSendDisabledFieldsFunc }}()
15+
serverParams.{{ .DoNotSendEnabledFieldsFunc }}()
16+
17+
return serverParams
18+
}
19+
20+
21+
func (s *ServerParams){{ .DoNotSendDisabledFieldsFunc }}() {
22+
{{- range .DoNotSendDisabledFields }}
23+
if s.{{ . }} == "disabled" {
24+
s.{{ . }} = ""
25+
}
26+
{{- end }}
27+
}
28+
29+
30+
func (s *ServerParams){{ .DoNotSendEnabledFieldsFunc }}() {
31+
{{- range .DoNotSendEnabledFields }}
32+
if s.{{ . }} == "enabled" {
33+
s.{{ . }} = ""
34+
}
35+
{{- end }}
36+
}

cmd/server_params_runtime/header.tmpl

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Code generated by server_params_runtime; DO NOT EDIT.
2+
3+
{{ .Licence }}
4+
5+
package {{ .Package }}
6+
7+
import (
8+
cryptorand "crypto/rand"
9+
"fmt"
10+
"math"
11+
"math/rand"
12+
"strconv"
13+
"testing"
14+
"time"
15+
"bytes"
16+
"encoding/gob"
17+
18+
"github.com/go-faker/faker/v4"
19+
"github.com/go-openapi/strfmt"
20+
21+
jsoniter "github.com/json-iterator/go"
22+
)

cmd/server_params_runtime/main.go

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"go/token"
6+
"log"
7+
"os"
8+
"strings"
9+
"text/template"
10+
11+
"github.com/sirkon/dst"
12+
"github.com/sirkon/dst/decorator"
13+
)
14+
15+
const (
16+
serverParamFileName = "server_params.go"
17+
commentEnumFieldsToReset = `// Enum: ["enabled","disabled"]`
18+
)
19+
20+
func main() {
21+
var allEnumFields []string
22+
var packageName string
23+
args := Args{}
24+
err := args.Parse()
25+
if err != nil {
26+
log.Panic(err)
27+
}
28+
for _, fileName := range args.Files {
29+
packageName, allEnumFields = enumFieldsEnabledDisabled(fileName)
30+
if err != nil {
31+
log.Panic(err)
32+
}
33+
34+
// check that all enum
35+
// Enum: [enabled disabled]"
36+
// fields have an entry in the ServerParamsPrepareForRuntimeMap
37+
missingFields, errMissingField := checkMissingEnumFields(allEnumFields)
38+
if errMissingField != nil {
39+
// Exit with error if any new enum field is found and its behavior is not defined in ServerParamsPrepareForRuntimeMap
40+
log.Printf("There are some fields in models/server_params.go that are Enum[enabled disabled] but missing in `ServerParamsPrepareForRuntimeMap`")
41+
log.Printf(" File location `cmd/server_params_runtime/server_parans_runtime_fields_behavior.go`")
42+
log.Printf("ACTION: Please add them to `ServerParamsPrepareForRuntimeMap`")
43+
log.Printf("Missing fields %v", missingFields)
44+
log.Printf("\t For doc, read cmd/server_params_runtime/README.md")
45+
46+
os.Exit(1)
47+
}
48+
49+
// Generate the reset function using the template
50+
tmpl, errTmpl := template.New("generate.tmpl").Parse(tmplResetEnumDisabledFields)
51+
// ParseFiles(path.Join(templatePath))
52+
if errTmpl != nil {
53+
log.Panic(errTmpl)
54+
}
55+
56+
generatedFileName := strings.TrimSuffix(fileName, ".go") + "_prepare_for_runtime.go"
57+
_ = os.Truncate(generatedFileName, 0)
58+
file, errFile := os.OpenFile(generatedFileName, os.O_CREATE|os.O_WRONLY, 0o600)
59+
if errFile != nil {
60+
log.Panic(errFile)
61+
}
62+
defer file.Close()
63+
64+
doNotSendDisabledFields := listEmptyDisabledFields(allEnumFields)
65+
doNotSendEnabledFields := listEmtpyEnabledFields(allEnumFields)
66+
67+
errTmpl = tmpl.Execute(file, map[string]interface{}{
68+
"PrepareFieldsForRuntimeAddServer": FuncPrepareFieldsForRuntimeAddServer,
69+
"DoNotSendDisabledFields": doNotSendDisabledFields,
70+
"DoNotSendDisabledFieldsFunc": FuncDoNotSendDisabledFields,
71+
"DoNotSendEnabledFields": doNotSendEnabledFields,
72+
"DoNotSendEnabledFieldsFunc": FuncDoNotSendEnabledFields,
73+
"Package": packageName,
74+
"Licence": args.Licence,
75+
})
76+
if errTmpl != nil {
77+
log.Panic(errTmpl)
78+
}
79+
80+
errFmt := fmtFile(generatedFileName)
81+
if errFmt != nil {
82+
log.Panic(errFmt)
83+
}
84+
}
85+
}
86+
87+
func enumFieldsEnabledDisabled(filename string) (string, []string) { //nolint:gocognit
88+
var fieldsWithComment []string
89+
f, err := decorator.ParseFile(token.NewFileSet(), filename, nil, 0)
90+
if err != nil {
91+
log.Fatal(err)
92+
}
93+
94+
for _, decl := range f.Decls {
95+
if genDecl, ok := decl.(*dst.GenDecl); ok && genDecl.Tok == token.TYPE {
96+
for _, spec := range genDecl.Specs {
97+
if typeSpec, ok := spec.(*dst.TypeSpec); ok {
98+
if structType, ok := typeSpec.Type.(*dst.StructType); ok {
99+
for _, field := range structType.Fields.List {
100+
comments := field.Decorations().Start.All()
101+
102+
for _, comment := range comments {
103+
if comment == commentEnumFieldsToReset {
104+
// Add field name to the list
105+
if len(field.Names) > 0 {
106+
fieldsWithComment = append(fieldsWithComment, field.Names[0].Name)
107+
}
108+
}
109+
}
110+
}
111+
}
112+
}
113+
}
114+
}
115+
}
116+
return f.Name.Name, fieldsWithComment
117+
}

cmd/server_params_runtime/params.go

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package main

0 commit comments

Comments
 (0)