diff --git a/go/vt/vtadmin/testutil/authztestgen/functions.go b/go/vt/vtadmin/testutil/authztestgen/functions.go index 99bc2e55f84..0f0d5c40002 100644 --- a/go/vt/vtadmin/testutil/authztestgen/functions.go +++ b/go/vt/vtadmin/testutil/authztestgen/functions.go @@ -17,11 +17,30 @@ limitations under the License. package main import ( + "fmt" "strings" "vitess.io/vitess/go/vt/vtadmin/rbac" ) +func formatDocRow(m *DocMethod) string { + var buf strings.Builder + buf.WriteString("| `") + buf.WriteString(m.Name) + buf.WriteString("` | ") + + for i, r := range m.Rules { + fmt.Fprintf(&buf, "`(%s, %s)`", r.Action, r.Resource) + if i != len(m.Rules)-1 { + buf.WriteString(", ") + } + } + + buf.WriteString(" |") + + return buf.String() +} + func getActor(actor *rbac.Actor) string { var buf strings.Builder switch actor { diff --git a/go/vt/vtadmin/testutil/authztestgen/main.go b/go/vt/vtadmin/testutil/authztestgen/main.go index ac50ce8979f..6a265aac730 100644 --- a/go/vt/vtadmin/testutil/authztestgen/main.go +++ b/go/vt/vtadmin/testutil/authztestgen/main.go @@ -18,6 +18,7 @@ package main import ( "encoding/json" + "fmt" "io" "os" "text/template" @@ -71,16 +72,78 @@ type FakeVtctldClientResult struct { Value string `json:"value"` } +type DocConfig struct { + Methods []*DocMethod +} + +type DocMethod struct { + Name string + Rules []*struct { + Resource rbac.Resource + Action rbac.Action + } +} + +func transformConfigForDocs(in Config) DocConfig { + cfg := DocConfig{} + + for _, t := range in.Tests { + m := &DocMethod{ + Name: t.Method, + } + + resourceActions := map[string]struct{}{} + for _, r := range t.Rules { + for _, a := range r.Actions { + k := fmt.Sprintf("%s.%s", r.Resource, a) + if _, ok := resourceActions[k]; ok { + continue + } + + resourceActions[k] = struct{}{} + + m.Rules = append(m.Rules, &struct { + Resource rbac.Resource + Action rbac.Action + }{ + Resource: rbac.Resource(r.Resource), + Action: rbac.Action(a), + }) + } + } + + cfg.Methods = append(cfg.Methods, m) + } + + return cfg +} + func panicIf(err error) { if err != nil { panic(err) } } +func open(path string) (output io.Writer, closer func()) { + output = os.Stdout + closer = func() {} + + if path != "" { + f, err := os.Create(path) + panicIf(err) + + closer = func() { f.Close() } + output = f + } + + return output, closer +} + func main() { path := pflag.StringP("config", "c", "config.json", "authztest configuration (see the Config type in this package for the spec)") pflag.StringVarP(path, "config-path", "p", "config.json", "alias for --config") outputPath := pflag.StringP("output", "o", "", "destination to write generated code. if empty, defaults to os.Stdout") + docgen := pflag.Bool("docgen", false, "generate docs table from authztest config instead of authz tests themselves") pflag.Parse() @@ -91,20 +154,30 @@ func main() { err = json.Unmarshal(data, &cfg) panicIf(err) + if *docgen { + cfg := transformConfigForDocs(cfg) + tmpl, err := template.New("docs").Funcs(map[string]any{ + "formatDocRow": formatDocRow, + }).Parse(_doct) + panicIf(err) + + output, closer := open(*outputPath) + defer closer() + + err = tmpl.Execute(output, &cfg) + panicIf(err) + + return + } + tmpl, err := template.New("tests").Funcs(map[string]any{ "getActor": getActor, "writeAssertion": writeAssertion, }).Parse(_t) panicIf(err) - var output io.Writer = os.Stdout - if *outputPath != "" { - f, err := os.Create(*outputPath) - panicIf(err) - - defer f.Close() - output = f - } + output, closer := open(*outputPath) + defer closer() err = tmpl.Execute(output, &cfg) panicIf(err) diff --git a/go/vt/vtadmin/testutil/authztestgen/template.go b/go/vt/vtadmin/testutil/authztestgen/template.go index 698922885b4..bcf8439bdce 100644 --- a/go/vt/vtadmin/testutil/authztestgen/template.go +++ b/go/vt/vtadmin/testutil/authztestgen/template.go @@ -166,3 +166,8 @@ func testClusters(t testing.TB) []*cluster.Cluster { return testutil.BuildClusters(t, configs...) } ` + +const _doct = `{{ range .Methods -}} +{{ formatDocRow . }} +{{ end -}} +`