Skip to content

Commit 01c5e8d

Browse files
committed
MEDIUM: add code generator server_params_runtime and fix no-check-send-proxy server param option
1 parent 6dc3242 commit 01c5e8d

18 files changed

+588
-4
lines changed

Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ spec:
2929
.PHONY: models
3030
models: gentypes spec swagger-check
3131
./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}
32+
rm -rf models/server_params_prepare_for_runtime.go
3233
rm -rf models/*_compare.go
3334
rm -rf models/*_compare_test.go
3435
go run cmd/struct_equal_generator/*.go -l ${PROJECT_PATH}/specification/copyright.txt ${PROJECT_PATH}/models
3536
go run cmd/struct_tags_checker/*.go ${PROJECT_PATH}/models
3637
go run cmd/kubebuilder_marker_generator/*.go ${PROJECT_PATH}/models
38+
go run cmd/server_params_runtime/*.go ${PROJECT_PATH}/models
3739

3840
.PHONY: swagger-check
3941
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

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

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)