Skip to content

Commit

Permalink
Generate event schema from proto files (gravitational#30194)
Browse files Browse the repository at this point in the history
* Event Schema protoc plugin, initial commit

* Generate mappings + dump table and schema views

* Add license headers

* Add license headers pt.2

* go mod tidy

* go mod tidy pt.2

* goimports

* Address feedback: consistent order, document rebuild, drop eventype mapping

* Reduce the list of queryable events

* Remove stale comment

* fixup! Address feedback: consistent order, document rebuild, drop eventype mapping

* Fix view generation

- support nested dmlTypes (`array(map(...))`)
- support fields with `.` in their names
- use same function to generate field names for view and table
- use leading commas when generating schema views

* display event_date and event_table in tableSchema + add tests

* Add docstrings

* Address jakule's feedback

* Address marco's feedback

* Convert Fields from a map to a list of fields

* lint
  • Loading branch information
hugoShaka authored Aug 21, 2023
1 parent 9268e97 commit 097e50f
Show file tree
Hide file tree
Showing 17 changed files with 20,904 additions and 3 deletions.
13 changes: 13 additions & 0 deletions build.assets/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -568,3 +568,16 @@ build-multiarch:
--build-arg GID=$(shell id -g) \
--tag $(BUILDBOX) \
--file Dockerfile-multiarch .

.PHONY:generate-eventschema
generate-eventschema:
$(eval PROTOBUF_MOD_PATH := $(shell go mod download --json github.com/gogo/protobuf | awk -F: '/"Dir"/ { print $$2 }' | tr -d ' ",'))

cd tooling && go build -o ./bin/protoc-gen-eventschema ./cmd/protoc-gen-eventschema/

protoc \
-I ../api/proto \
-I=$(PROTOBUF_MOD_PATH) \
--plugin=./tooling/bin/protoc-gen-eventschema \
--eventschema_out="../gen/go/eventschema" \
"teleport/legacy/types/events/events.proto";
70 changes: 70 additions & 0 deletions build.assets/tooling/cmd/protoc-gen-eventschema/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//go:build debug

/*
Copyright 2023 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

// When built with this debug tag, the protoc plugin reads its input
// from a file instead of stdin. This allows to easily attach a debugger and
// inspect what is happening inside the plugin.

import (
"io"
"os"

"github.com/gogo/protobuf/proto"
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
plugin "github.com/gogo/protobuf/protoc-gen-gogo/plugin"
"github.com/gravitational/trace"
log "github.com/sirupsen/logrus"
)

const pluginInputPathEnvironment = "TELEPORT_PROTOC_READ_FILE"

func readRequest() (*plugin.CodeGeneratorRequest, error) {
inputPath := os.Getenv(pluginInputPathEnvironment)
if inputPath == "" {
log.Error(trace.BadParameter("When built with the 'debug' tag, the input path must be set through the environment variable: %s", pluginInputPathEnvironment))
os.Exit(-1)
}
log.Infof("This is a debug build, the protoc request is read from the file: '%s'", inputPath)

req, err := readRequestFromFile(inputPath)
if err != nil {
log.WithError(err).Error("error reading request from file")
os.Exit(-1)
}
return req, trace.Wrap(err)
}

func readRequestFromFile(inputPath string) (*plugin.CodeGeneratorRequest, error) {
g := generator.New()
inputFile, err := os.Open(inputPath)
if err != nil {
return nil, trace.Wrap(err)
}
data, err := io.ReadAll(inputFile)
if err != nil {
return nil, trace.WrapWithMessage(err, "failed to read input")
}

if err := proto.Unmarshal(data, g.Request); err != nil {
return nil, trace.WrapWithMessage(err, "failed to parse input proto")
}

if len(g.Request.FileToGenerate) == 0 {
return nil, trace.BadParameter("no files to generate")
}
return g.Request, nil
}
86 changes: 86 additions & 0 deletions build.assets/tooling/cmd/protoc-gen-eventschema/handlerequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2023 Gravitational, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
gogoplugin "github.com/gogo/protobuf/protoc-gen-gogo/plugin"
"github.com/gogo/protobuf/vanity/command"
"github.com/gravitational/trace"

"github.com/gravitational/teleport/build.assets/tooling/lib/eventschema"
tree "github.com/gravitational/teleport/build.assets/tooling/lib/protobuf-tree"
)

const outputFileName = "zz_generated.eventschema.go"

func handleRequest(req *gogoplugin.CodeGeneratorRequest) error {
switch inputFileCount := len(req.FileToGenerate); {
case inputFileCount == 0:
return trace.BadParameter("no input file provided")
case inputFileCount > 1:
return trace.BadParameter("too many input files")
}

gen, err := newGenerator(req)
if err != nil {
return trace.Wrap(err)
}

rootFileName := req.FileToGenerate[0]
gen.SetFile(rootFileName)
for _, fileDesc := range gen.AllFiles().File {
file := gen.AddFile(fileDesc)
if fileDesc.GetName() == rootFileName {
if err := generateSchema(file, gen.Response); err != nil {
return trace.Wrap(err)
}
}
}

command.Write(gen.Response)

return nil
}

func newGenerator(req *gogoplugin.CodeGeneratorRequest) (*tree.Forest, error) {
gen := generator.New()

gen.Request = req
gen.CommandLineParameters(gen.Request.GetParameter())
gen.WrapTypes()
gen.SetPackageNames()
gen.BuildTypeNameMap()

return tree.NewForest(gen), nil
}

func generateSchema(file *tree.File, resp *gogoplugin.CodeGeneratorResponse) error {
gen := eventschema.NewSchemaGenerator()

err := gen.Process(file)
if err != nil {
return trace.Wrap(err)
}

name := outputFileName
content, err := gen.Render()
if err != nil {
return trace.Wrap(err)
}
resp.File = append(resp.File, &gogoplugin.CodeGeneratorResponse_File{Name: &name, Content: &content})

return nil
}
37 changes: 37 additions & 0 deletions build.assets/tooling/cmd/protoc-gen-eventschema/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Copyright 2023 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"os"

log "github.com/sirupsen/logrus"
)

func main() {
log.SetLevel(log.DebugLevel)
log.SetOutput(os.Stderr)
req, err := readRequest()
if err != nil {
log.WithError(err).Error("Failed to read request")
os.Exit(-1)
}
if err := handleRequest(req); err != nil {
log.WithError(err).Error("Failed to generate schema")
os.Exit(-1)
}
}
29 changes: 29 additions & 0 deletions build.assets/tooling/cmd/protoc-gen-eventschema/readrequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//go:build !debug

/*
Copyright 2023 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
plugin "github.com/gogo/protobuf/protoc-gen-gogo/plugin"
"github.com/gogo/protobuf/vanity/command"
)

func readRequest() (*plugin.CodeGeneratorRequest, error) {
req := command.Read()
return req, nil
}
25 changes: 25 additions & 0 deletions build.assets/tooling/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module github.com/gravitational/teleport/build.assets/tooling
go 1.18

require (
github.com/Masterminds/sprig/v3 v3.2.3
github.com/alecthomas/kingpin/v2 v2.3.2 // replaced
github.com/bmatcuk/doublestar/v4 v4.6.0
github.com/bradleyfalzon/ghinstallation/v2 v2.6.0
github.com/gogo/protobuf v1.3.2
github.com/google/go-github/v41 v41.0.0
github.com/google/uuid v1.3.0
github.com/gravitational/trace v1.3.1
Expand All @@ -18,35 +20,58 @@ require (
golang.org/x/mod v0.12.0
golang.org/x/oauth2 v0.11.0
howett.net/plist v1.0.0
k8s.io/apiextensions-apiserver v0.27.4
)

require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-github/v53 v53.2.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/gon v0.2.5
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/term v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apimachinery v0.27.4 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
)

replace github.com/alecthomas/kingpin/v2 => github.com/gravitational/kingpin/v2 v2.1.11-0.20230515143221-4ec6b70ecd33
Loading

0 comments on commit 097e50f

Please sign in to comment.