Skip to content

Commit

Permalink
MEDIUM: add code generator server_params_runtime and fix no-check-sen…
Browse files Browse the repository at this point in the history
…d-proxy server param option
  • Loading branch information
hdurand0710 committed Nov 27, 2024
1 parent 6dc3242 commit 01c5e8d
Show file tree
Hide file tree
Showing 18 changed files with 588 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ spec:
.PHONY: models
models: gentypes spec swagger-check
./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}
rm -rf models/server_params_prepare_for_runtime.go
rm -rf models/*_compare.go
rm -rf models/*_compare_test.go
go run cmd/struct_equal_generator/*.go -l ${PROJECT_PATH}/specification/copyright.txt ${PROJECT_PATH}/models
go run cmd/struct_tags_checker/*.go ${PROJECT_PATH}/models
go run cmd/kubebuilder_marker_generator/*.go ${PROJECT_PATH}/models
go run cmd/server_params_runtime/*.go ${PROJECT_PATH}/models

.PHONY: swagger-check
swagger-check:
Expand Down
60 changes: 60 additions & 0 deletions cmd/server_params_runtime/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# ![HAProxy](../../assets/images/haproxy-weblogo-210x49.png "HAProxy")

## Generator for: `models/server_params_prepare_for_runtime.go`

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.

This file will contain 3 functions:

```
func (p *ServerParams) prepareForRuntimeDoNotSendDisabledFields()
func (p *ServerParams) prepareForRuntimeDoNotSendEnabledFields()
func (p *ServerParams) PrepareFieldsForRuntimeAddServer()
```

They are used for example in Ingress Controller the following way:
```
params.PrepareFieldsForRuntimeAddServer()
serverParams := configuration.SerializeServerParams(defaultServer.ServerParams)
res := cp_params.ServerOptionsString(serverParams)
```

### func (p *ServerParams) prepareForRuntimeDoNotSendDisabledFields()
For example for `Check` that has the values [enabled disabled].

- if the value is `enabled` we must send `add server check`
- if the value is `disabled` we must not send `add server no-check` as `no-check` is not allowed on a dynamic server

`no-check` is the default value.

The purpose is to set `Check` to "" when the value was `disabled` so the commands sent are:
- `add server check` if value is `enabled`
- `add server` if value is `disabled`


### func (p *ServerParams) prepareForRuntimeDoNotSendEnabledFields()
It's just the opposite.

For example for `NoSslv3`

- if `enabled` we must send `no-sslv3`
- if `disabled` we must not sent an option


### func (p *ServerParams) PrepareFieldsForRuntimeAddServer()`
is just calling both `PrepareForRuntimeDoNotSendDisabledFields` and `PrepareForRuntimeDoNotSendEnabledFields`


## WHAT TO DO

Just fill in `server_params_prepare_for_runtime.go` the map:
- `ServerParamsPrepareForRuntimeMap`

for each field that has an `// Enum: [enabled disabled]"`
with the correct function to use

## Ensure that the map is always filled in a field is added into ServerParams: CI check

The generator checks all fields in ServerParams that have `// Enum: [enabled disabled]"` have an entry in the `ServerParamsPrepareForRuntimeMap`.

If a new field is added and not declared in `ServerParamsPrepareForRuntimeMap`, the generator fails with an error and this will make the CI fail.
96 changes: 96 additions & 0 deletions cmd/server_params_runtime/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package main

import (
_ "embed"
"fmt"
"io/fs"
"log"
"os"
"path"
"path/filepath"
"strings"
)

type Args struct {
Licence string
LicencePath string
Directory string
Selector string
Files []string
}

func (a *Args) Parse() error { //nolint:gocognit
selector, err := os.Getwd()
if err != nil {
log.Panic(err)
}
for i := 1; i < len(os.Args); i++ {
val := os.Args[i]
switch {
case val == "-l":
if i+1 >= len(os.Args) {
return fmt.Errorf("missing licence file after -l")
}
a.LicencePath = os.Args[i+1]
i++
case strings.HasPrefix(val, "--licence="):
a.LicencePath = strings.TrimPrefix(val, "--licence=")
default:
selector = val
}
}

if a.LicencePath != "" {
var licence []byte
licence, err = os.ReadFile(a.LicencePath)
if err != nil {
return err
}
lines := strings.Split(string(licence), "\n")
var s strings.Builder
for _, line := range lines {
s.WriteString("// ")
s.WriteString(line)
s.WriteString("\n")
}
a.Licence = s.String()
}
isDirectory := false
file, err := os.Open(selector)
if err == nil {
var fileInfo fs.FileInfo
fileInfo, err = file.Stat()
if err == nil {
isDirectory = fileInfo.IsDir()
}
}
if selector == "*" || selector == "." || isDirectory {
err = filepath.Walk(selector, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}

if strings.HasSuffix(path, serverParamFileName) {
fmt.Println(path) //nolint:forbidigo
a.Files = append(a.Files, path)
}
return nil
})
if err != nil {
log.Panic(err)
}
} else {
a.Files = append(a.Files, selector)
}
a.Selector = selector
if isDirectory {
a.Directory = selector
} else {
a.Directory = path.Dir(selector)
}

return nil
}
8 changes: 8 additions & 0 deletions cmd/server_params_runtime/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

import (
_ "embed"
)

//go:embed generate.tmpl
var tmplResetEnumDisabledFields string
33 changes: 33 additions & 0 deletions cmd/server_params_runtime/formating.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"bytes"
"fmt"
"go/format"
"go/parser"
"go/token"
"os"

"golang.org/x/tools/imports"
)

func fmtFile(fileName string) error {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
if err != nil {
return err
}

// gofmt the modified file
var buf bytes.Buffer
if err = format.Node(&buf, fset, node); err != nil {
return err
}

formattedCode, err := imports.Process(fileName, buf.Bytes(), nil)
if err != nil {
return fmt.Errorf("failed to perform goimports: %w", err)
}

return os.WriteFile(fileName, formattedCode, 0o600)
}
36 changes: 36 additions & 0 deletions cmd/server_params_runtime/generate.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Code generated by server_params_runtime; DO NOT EDIT.

{{ .Licence }}

package {{ .Package }}


func (s ServerParams){{ .PrepareFieldsForRuntimeAddServer }}()* ServerParams {
serverParams := new(ServerParams)
// *defaultServer = p
a, _ := s.MarshalBinary()
_ = serverParams.UnmarshalBinary(a)

serverParams.{{ .DoNotSendDisabledFieldsFunc }}()
serverParams.{{ .DoNotSendEnabledFieldsFunc }}()

return serverParams
}


func (s *ServerParams){{ .DoNotSendDisabledFieldsFunc }}() {
{{- range .DoNotSendDisabledFields }}
if s.{{ . }} == "disabled" {
s.{{ . }} = ""
}
{{- end }}
}


func (s *ServerParams){{ .DoNotSendEnabledFieldsFunc }}() {
{{- range .DoNotSendEnabledFields }}
if s.{{ . }} == "enabled" {
s.{{ . }} = ""
}
{{- end }}
}
22 changes: 22 additions & 0 deletions cmd/server_params_runtime/header.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Code generated by server_params_runtime; DO NOT EDIT.

{{ .Licence }}

package {{ .Package }}

import (
cryptorand "crypto/rand"
"fmt"
"math"
"math/rand"
"strconv"
"testing"
"time"
"bytes"
"encoding/gob"

"github.com/go-faker/faker/v4"
"github.com/go-openapi/strfmt"

jsoniter "github.com/json-iterator/go"
)
117 changes: 117 additions & 0 deletions cmd/server_params_runtime/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
_ "embed"
"go/token"
"log"
"os"
"strings"
"text/template"

"github.com/sirkon/dst"
"github.com/sirkon/dst/decorator"
)

const (
serverParamFileName = "server_params.go"
commentEnumFieldsToReset = "// Enum: [enabled disabled]"
)

func main() {
var allEnumFields []string
var packageName string
args := Args{}
err := args.Parse()
if err != nil {
log.Panic(err)
}
for _, fileName := range args.Files {
packageName, allEnumFields = enumFieldsEnabledDisabled(fileName)
if err != nil {
log.Panic(err)
}

// check that all enum
// Enum: [enabled disabled]"
// fields have an entry in the ServerParamsPrepareForRuntimeMap
missingFields, errMissingField := checkMissingEnumFields(allEnumFields)
if errMissingField != nil {
// Exit with error if any new enum field is found and its behavior is not defined in ServerParamsPrepareForRuntimeMap
log.Printf("There are some fields in models/server_params.go that are Enum[enabled disabled] but missing in `ServerParamsPrepareForRuntimeMap`")
log.Printf(" File location `cmd/server_params_runtime/server_parans_runtime_fields_behavior.go`")
log.Printf("ACTION: Please add them to `ServerParamsPrepareForRuntimeMap`")
log.Printf("Missing fields %v", missingFields)
log.Printf("\t For doc, read cmd/server_params_runtime/README.md")

os.Exit(1)
}

// Generate the reset function using the template
tmpl, errTmpl := template.New("generate.tmpl").Parse(tmplResetEnumDisabledFields)
// ParseFiles(path.Join(templatePath))
if errTmpl != nil {
log.Panic(errTmpl)
}

generatedFileName := strings.TrimSuffix(fileName, ".go") + "_prepare_for_runtime.go"
_ = os.Truncate(generatedFileName, 0)
file, errFile := os.OpenFile(generatedFileName, os.O_CREATE|os.O_WRONLY, 0o600)
if errFile != nil {
log.Panic(errFile)
}
defer file.Close()

doNotSendDisabledFields := listEmptyDisabledFields(allEnumFields)
doNotSendEnabledFields := listEmtpyEnabledFields(allEnumFields)

errTmpl = tmpl.Execute(file, map[string]interface{}{
"PrepareFieldsForRuntimeAddServer": FuncPrepareFieldsForRuntimeAddServer,
"DoNotSendDisabledFields": doNotSendDisabledFields,
"DoNotSendDisabledFieldsFunc": FuncDoNotSendDisabledFields,
"DoNotSendEnabledFields": doNotSendEnabledFields,
"DoNotSendEnabledFieldsFunc": FuncDoNotSendEnabledFields,
"Package": packageName,
"Licence": args.Licence,
})
if errTmpl != nil {
log.Panic(errTmpl)
}

errFmt := fmtFile(generatedFileName)
if errFmt != nil {
log.Panic(errFmt)
}
}
}

func enumFieldsEnabledDisabled(filename string) (string, []string) { //nolint:gocognit
var fieldsWithComment []string
f, err := decorator.ParseFile(token.NewFileSet(), filename, nil, 0)
if err != nil {
log.Fatal(err)
}

for _, decl := range f.Decls {
if genDecl, ok := decl.(*dst.GenDecl); ok && genDecl.Tok == token.TYPE {
for _, spec := range genDecl.Specs {
if typeSpec, ok := spec.(*dst.TypeSpec); ok {
if structType, ok := typeSpec.Type.(*dst.StructType); ok {
for _, field := range structType.Fields.List {
comments := field.Decorations().Start.All()

for _, comment := range comments {
if comment == commentEnumFieldsToReset {
// Add field name to the list
if len(field.Names) > 0 {
fieldsWithComment = append(fieldsWithComment, field.Names[0].Name)
}
}
}
}
}
}
}
}
}
return f.Name.Name, fieldsWithComment
}
1 change: 1 addition & 0 deletions cmd/server_params_runtime/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package main
Loading

0 comments on commit 01c5e8d

Please sign in to comment.