Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add protoc generator to emit resource type variables #18957

Merged
merged 1 commit into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions build-support/scripts/devtools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ function proto_tools_install {
"${mog_version}" \
'github.com/hashicorp/mog'

install_protoc_gen_consul_rate_limit
install_local_protoc_generator "${SOURCE_DIR}/internal/tools/protoc-gen-consul-rate-limit"

install_local_protoc_generator "${SOURCE_DIR}/internal/resource/protoc-gen-resource-types"

return 0
}
Expand Down Expand Up @@ -265,9 +267,10 @@ function install_versioned_tool {
return 0
}

function install_protoc_gen_consul_rate_limit {
echo "installing tool protoc-gen-consul-rate-limit from local source"
pushd -- "${SOURCE_DIR}/internal/tools/protoc-gen-consul-rate-limit" > /dev/null
function install_local_protoc_generator {
local src=$1
echo "installing tool $(basename $src) from local source"
pushd -- "$src" > /dev/null
go install
popd > /dev/null
}
Expand Down
25 changes: 11 additions & 14 deletions docs/resources/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ $ mkdir proto-public/pbfoo/v1alpha1
syntax = "proto3";

import "pbresource/resource.proto";
import "pbresource/annotations.proto";

package hashicorp.consul.foo.v1alpha1;

message Bar {
option (hashicorp.consul.resource.spec) = {scope: SCOPE_NAMESPACE};

string baz = 1;
hashicorp.consul.resource.ID qux = 2;
}
Expand All @@ -47,15 +50,9 @@ import (
"github.com/hashicorp/consul/proto-public/pbresource"
)

var BarV1Alpha1Type = &pbresource.Type{
Group: "foo",
GroupVersion: "v1alpha1",
Kind: "bar",
}

func RegisterTypes(r resource.Registry) {
r.Register(resource.Registration{
Type: BarV1Alpha1Type,
Type: pbv1alpha1.BarType,
Scope: resource.ScopePartition,
Proto: &pbv1alpha1.Bar{},
})
Expand Down Expand Up @@ -143,7 +140,7 @@ using a validation hook provided in the type registration:
```Go
func RegisterTypes(r resource.Registry) {
r.Register(resource.Registration{
Type: BarV1Alpha1Type,
Type: pbv1alpha1.BarType,
Proto: &pbv1alpha1.Bar{},
Scope: resource.ScopeNamespace,
Validate: validateBar,
Expand Down Expand Up @@ -176,7 +173,7 @@ a set of ACL hooks:
```Go
func RegisterTypes(r resource.Registry) {
r.Register(resource.Registration{
Type: BarV1Alpha1Type,
Type: pbv1alpha1.BarType,
Proto: &pbv1alpha1.Bar{},
Scope: resource.ScopeNamespace,
ACLs: &resource.ACLHooks{,
Expand Down Expand Up @@ -215,7 +212,7 @@ by providing a mutation hook:
```Go
func RegisterTypes(r resource.Registry) {
r.Register(resource.Registration{
Type: BarV1Alpha1Type,
Type: pbv1alpha1.BarType,
Proto: &pbv1alpha1.Bar{},
Scope: resource.ScopeNamespace,
Mutate: mutateBar,
Expand Down Expand Up @@ -254,7 +251,7 @@ import (
)

func barController() controller.Controller {
return controller.ForType(BarV1Alpha1Type).
return controller.ForType(pbv1alpha1.BarType).
WithReconciler(barReconciler{})
}

Expand Down Expand Up @@ -390,8 +387,8 @@ controller also watches workloads and services.

```Go
func barController() controller.Controller {
return controller.ForType(BarV1Alpha1Type).
WithWatch(BazV1Alpha1Type, controller.MapOwner)
return controller.ForType(pbv1alpha1.BarType).
WithWatch(pbv1alpha1.BazType, controller.MapOwner)
WithReconciler(barReconciler{})
}
```
Expand All @@ -416,7 +413,7 @@ the controller's placement.

```Go
func barController() controller.Controller {
return controller.ForType(BarV1Alpha1Type).
return controller.ForType(pbv1alpha1.BarType).
WithPlacement(controller.PlacementEachServer)
WithReconciler(barReconciler{})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package generate

import (
"fmt"
"path/filepath"
"sort"
"strings"
"text/template"

"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"

"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/proto-public/pbresource"
)

func Generate(gp *protogen.Plugin) error {
g := newGenerator()

for _, file := range gp.Files {
err := g.addResourceKindsFromFile(file)
if err != nil {
return err
}
}

return g.generateTypes(gp)
}

type apiGroupVersion struct {
Group string
Version string
}

type groupInfo struct {
Name string
Version string

Kinds []kind
ImportPath protogen.GoImportPath
PackageName protogen.GoPackageName
Directory string
}

type kind struct {
Name string
Comments protogen.CommentSet
}

type generator struct {
resources map[apiGroupVersion]*groupInfo
}

func newGenerator() *generator {
return &generator{
resources: make(map[apiGroupVersion]*groupInfo),
}
}

func (g *generator) addResourceKindsFromFile(f *protogen.File) error {
if !f.Generate {
return nil
}

for _, m := range f.Messages {
ext := proto.GetExtension(m.Desc.Options(), pbresource.E_Spec).(*pbresource.ResourceTypeSpec)
if ext == nil {
continue
}

gvkString := strings.TrimPrefix(string(m.Desc.FullName()), "hashicorp.consul.")
rtype, err := resource.ParseGVK(gvkString)
if err != nil {
return err
}

apiGroupDir := filepath.Dir(f.Proto.GetName())

gv := apiGroupVersion{
Group: rtype.Group,
Version: rtype.GroupVersion,
}

grp, err := g.ensureAPIGroup(gv, f.GoImportPath, f.GoPackageName, apiGroupDir)
if err != nil {
return err
}

grp.Kinds = append(grp.Kinds, kind{Name: rtype.Kind, Comments: m.Comments})
}

return nil
}

func (g *generator) ensureAPIGroup(gv apiGroupVersion, importPath protogen.GoImportPath, pkg protogen.GoPackageName, dir string) (*groupInfo, error) {
grp, found := g.resources[gv]
if !found {
grp = &groupInfo{
Name: gv.Group,
Version: gv.Version,

ImportPath: importPath,
PackageName: pkg,
Directory: dir,
}
g.resources[gv] = grp
} else if grp.ImportPath != importPath {
return nil, fmt.Errorf("resources from the same api group must share the same import path")
}

return grp, nil
}

func (g *generator) generateTypes(gp *protogen.Plugin) error {
for _, info := range g.resources {
f := gp.NewGeneratedFile(filepath.Join(info.Directory, "resource_types.gen.go"), info.ImportPath)

sort.Slice(info.Kinds, func(a, b int) bool {
return info.Kinds[a].Name < info.Kinds[b].Name
})

if err := typesTemplate.Execute(f, info); err != nil {
return err
}
}
return nil
}

var (
typesTemplate = template.Must(template.New("types").Parse(`
// Code generated by protoc-gen-resource-types. DO NOT EDIT.

package {{.PackageName}}

import (
"github.com/hashicorp/consul/proto-public/pbresource"
)

const (
GroupName = "{{.Name}}"
Version = "{{.Version}}"

{{range $kind := .Kinds}}
{{$kind.Name}}Kind = "{{$kind.Name}}"
{{- end}}
)

var (
{{range $kind := .Kinds}}
{{$kind.Name}}Type = &pbresource.Type{
Group: GroupName,
GroupVersion: Version,
Kind: {{$kind.Name}}Kind,
}
{{end}}
)
`))
)
27 changes: 27 additions & 0 deletions internal/resource/protoc-gen-resource-types/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package main

import (
"flag"

"github.com/hashicorp/consul/internal/resource/protoc-gen-resource-types/internal/generate"
"google.golang.org/protobuf/compiler/protogen"
plugin "google.golang.org/protobuf/types/pluginpb"
)

var (
file = flag.String("file", "-", "where to load data from")
)

func main() {
flag.Parse()

protogen.Options{
ParamFunc: flag.CommandLine.Set,
}.Run(func(gp *protogen.Plugin) error {
gp.SupportedFeatures = uint64(plugin.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
return generate.Generate(gp)
})
}
2 changes: 1 addition & 1 deletion internal/tools/protoc-gen-consul-rate-limit/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func main() {
}

if len(specs) == 0 {
return nil
continue
}

outputPath := filepath.Join(filepath.Dir(path), outputFileName)
Expand Down
5 changes: 5 additions & 0 deletions proto-public/buf.gen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ plugins:
out: .
opt:
- paths=source_relative
- name: resource-types
out: .
opt:
- paths=source_relative
strategy: all
- name: consul-rate-limit
out: .
opt:
Expand Down
Loading