Skip to content

Commit

Permalink
Add generation for getting objects from requests
Browse files Browse the repository at this point in the history
In order to validate objects, the new and old objects, if they exist,
should be pull from the webhook request. This process is identical for
all objects except that the return types would be different. This is a
candidate for code generation, and this generation is added.
  • Loading branch information
Donnie Adams committed Jul 27, 2021
1 parent 166af4f commit 42c30d0
Show file tree
Hide file tree
Showing 8 changed files with 547 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
/dist
*.swp
.idea
.vscode
/webhook
3 changes: 3 additions & 0 deletions .golangci.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
]
},
"run": {
"skip-dirs": [
"pkg/generated"
],
"skip-files": [
"/zz_generated_"
],
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ require (
github.com/rancher/rancher/pkg/apis v0.0.0-20210628154046-7a2fc74f9598
github.com/rancher/wrangler v0.8.3
github.com/sirupsen/logrus v1.8.1
golang.org/x/tools v0.1.0
k8s.io/api v0.21.0
k8s.io/apiextensions-apiserver v0.21.0
k8s.io/apimachinery v0.21.0
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:generate go run pkg/codegen/cleanup/main.go
//go:generate go run pkg/codegen/main.go
//go:generate go run pkg/codegen/main.go pkg/codegen/template.go
package main

import (
Expand Down
96 changes: 96 additions & 0 deletions pkg/codegen/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
package main

import (
"bytes"
"fmt"
"os"
"path"
"reflect"
"strings"
"text/template"

v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
v1 "github.com/rancher/rancher/pkg/apis/provisioning.cattle.io/v1"
controllergen "github.com/rancher/wrangler/pkg/controller-gen"
"github.com/rancher/wrangler/pkg/controller-gen/args"
"golang.org/x/tools/imports"
)

type typeInfo struct {
Name, Type, Package string
}

func main() {
os.Unsetenv("GOPATH")
controllergen.Run(args.Options{
Expand All @@ -23,4 +35,88 @@ func main() {
},
},
})

// Generate the <TYPE>FromRequest and <TYPE>OldAndNewFromRequest functions to get the new and old objects from the webhook request.
if err := generateObjectsFromRequest("pkg/generated/objects", map[string]args.Group{
"management.cattle.io": {
Types: []interface{}{
&v3.Cluster{},
&v3.ClusterRoleTemplateBinding{},
&v3.FleetWorkspace{},
&v3.GlobalRole{},
&v3.GlobalRoleBinding{},
&v3.RoleTemplate{},
&v3.ProjectRoleTemplateBinding{},
},
},
"provisioning.cattle.io": {
Types: []interface{}{
&v1.Cluster{},
},
}}); err != nil {
fmt.Printf("ERROR: %v\n", err)
}
}

func generateObjectsFromRequest(outputDir string, groups map[string]args.Group) error {
temp := template.Must(template.New("objectsFromRequest").Funcs(template.FuncMap{
"replace": strings.ReplaceAll,
}).Parse(objectsFromRequestTemplate))

for groupName, group := range groups {
var packageName string
types := make([]typeInfo, 0, len(group.Types))

for _, t := range group.Types {
rt := reflect.TypeOf(t)
ti := typeInfo{
Type: rt.String(),
}
if rt.Kind() == reflect.Ptr {
// PkgPath returns an empty string for pointers
// Elem returns a Type associated to the dereferenced type.
rt = rt.Elem()
}
ti.Package = rt.PkgPath()
ti.Name = rt.Name()
packageName = path.Base(ti.Package)
types = append(types, ti)
}

groupDir := path.Join(outputDir, groupName, packageName)
if err := os.MkdirAll(groupDir, 0755); err != nil {
return err
}

data := map[string]interface{}{
"types": types,
"package": packageName,
}

var content bytes.Buffer
if err := temp.Execute(&content, data); err != nil {
return err
}

if err := gofmtAndWriteToFile(path.Join(groupDir, "objects.go"), content.Bytes()); err != nil {
return err
}
}

return nil
}

func gofmtAndWriteToFile(path string, content []byte) error {
formatted, err := imports.Process(path, content, &imports.Options{FormatOnly: true, Comments: true})
if err != nil {
return err
}
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()

_, err = f.Write(formatted)
return err
}
61 changes: 61 additions & 0 deletions pkg/codegen/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

const objectsFromRequestTemplate = `
package {{ .package }}
import (
{{ range .types }}
"{{ .Package }}"{{ end }}
"github.com/rancher/wrangler/pkg/webhook"
admissionv1 "k8s.io/api/admission/v1"
"k8s.io/apimachinery/pkg/runtime"
)
{{ range .types }}
// {{ .Name }}OldAndNewFromRequest gets the old and new {{ .Name }} objects, respectively, from the webhook request.
// If the request is a Delete operation, then the new object is the zero value for {{ .Name }}.
// Similarly, if the request is a Create operation, then the old object is the zero value for {{ .Name }}.
func {{ .Name }}OldAndNewFromRequest(request *webhook.Request) ({{ .Type }}, {{ .Type }}, error) {
var object runtime.Object
var err error
if request.Operation != admissionv1.Delete {
object, err = request.DecodeObject()
if err != nil {
return nil, nil, err
}
} else {
object = {{ replace .Type "*" "&" }}{}
}
if request.Operation == admissionv1.Create {
return {{ replace .Type "*" "&" }}{}, object.({{ .Type }}), nil
}
oldObject, err := request.DecodeOldObject()
if err != nil {
return nil, nil, err
}
return oldObject.({{ .Type }}), object.({{ .Type }}), nil
}
// {{ .Name }}FromRequest returns a {{ .Name }} object from the webhook request.
// If the operation is a Delete operation, then the old object is returned.
// Otherwise, the new object is returned.
func {{ .Name }}FromRequest(request *webhook.Request) ({{ .Type }}, error) {
var object runtime.Object
var err error
if request.Operation == admissionv1.Delete {
object, err = request.DecodeOldObject()
} else {
object, err = request.DecodeObject()
}
if err != nil {
return nil, err
}
return object.({{ .Type }}), nil
}
{{ end }}
`
Loading

0 comments on commit 42c30d0

Please sign in to comment.