internal/protobuf: add proto definition extraction
Change-Id: Ia356bcb951e30bcc8030b51cbc5d9949391e84dc
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1980
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/internal/protobuf/parse.go b/internal/protobuf/parse.go
new file mode 100644
index 0000000..8b8ede9
--- /dev/null
+++ b/internal/protobuf/parse.go
@@ -0,0 +1,620 @@
+// Copyright 2019 CUE Authors
+//
+// 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 protobuf
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+
+ "cuelang.org/go/cue/ast"
+ "cuelang.org/go/cue/parser"
+ "cuelang.org/go/cue/token"
+ "github.com/emicklei/proto"
+ "golang.org/x/xerrors"
+)
+
+type sharedState struct {
+ paths []string
+}
+
+func (s *sharedState) parse(filename string, r io.Reader) (p *protoConverter, err error) {
+ // Determine files to convert.
+ if r == nil {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, xerrors.Errorf("protobuf: %w", err)
+ }
+ defer f.Close()
+ r = f
+ }
+
+ parser := proto.NewParser(r)
+ if filename != "" {
+ parser.Filename(filename)
+ }
+ d, err := parser.Parse()
+ if err != nil {
+ return nil, xerrors.Errorf("protobuf: %w", err)
+ }
+
+ p = &protoConverter{
+ state: s,
+ used: map[string]bool{},
+ symbols: map[string]bool{},
+ }
+
+ defer func() {
+ switch x := recover().(type) {
+ case nil:
+ case protoError:
+ err = &ProtoError{
+ Filename: filename,
+ Path: strings.Join(p.path, "."),
+ Err: x.error,
+ }
+ default:
+ panic(x)
+ }
+ }()
+
+ p.file = &ast.File{Filename: filename}
+
+ p.addNames(d.Elements)
+
+ // Parse package definitions.
+ for _, e := range d.Elements {
+ switch x := e.(type) {
+ case *proto.Package:
+ p.protoPkg = x.Name
+ case *proto.Option:
+ if x.Name == "go_package" {
+ str, err := strconv.Unquote(x.Constant.SourceRepresentation())
+ if err != nil {
+ failf("unquoting package filed: %v", err)
+ }
+ split := strings.Split(str, ";")
+ p.goPkgPath = split[0]
+ switch len(split) {
+ case 1:
+ p.goPkg = path.Base(str)
+ case 2:
+ p.goPkg = split[1]
+ default:
+ failf("unexpected ';' in %q", str)
+ }
+ p.file.Name = ast.NewIdent(p.goPkg)
+ // name.AddComment(comment(x.Comment, true))
+ // name.AddComment(comment(x.InlineComment, false))
+ }
+ }
+ }
+
+ for _, e := range d.Elements {
+ switch x := e.(type) {
+ case *proto.Import:
+ p.doImport(x)
+ }
+ }
+
+ imports := &ast.ImportDecl{}
+ p.file.Decls = append(p.file.Decls, imports)
+
+ for _, e := range d.Elements {
+ p.topElement(e)
+ }
+
+ used := []string{}
+ for k := range p.used {
+ used = append(used, k)
+ }
+ sort.Strings(used)
+
+ for _, v := range used {
+ imports.Specs = append(imports.Specs, &ast.ImportSpec{
+ Path: &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(v)},
+ })
+ }
+
+ if len(imports.Specs) == 0 {
+ p.file.Decls = p.file.Decls[1:]
+ }
+
+ return p, nil
+}
+
+// A protoConverter converts a proto definition to CUE. Proto files map to
+// CUE files one to one.
+type protoConverter struct {
+ state *sharedState
+
+ proto3 bool
+
+ protoPkg string
+ goPkg string
+ goPkgPath string
+
+ // w bytes.Buffer
+ file *ast.File
+ inBody bool
+
+ imports map[string]string
+ used map[string]bool
+
+ path []string
+ scope []map[string]mapping // for symbols resolution within package.
+ symbols map[string]bool // symbols provided by package
+}
+
+type mapping struct {
+ ref string
+ pkg *protoConverter
+}
+
+type pkgInfo struct {
+ importPath string // the import path
+ goPath string // The Go import path
+ shortName string // Used for the cue package path, default is base of goPath
+}
+
+func (p *protoConverter) addRef(from, to string) {
+ top := p.scope[len(p.scope)-1]
+ if _, ok := top[from]; ok {
+ failf("entity %q already defined", from)
+ }
+ top[from] = mapping{ref: to}
+}
+
+func (p *protoConverter) addNames(elems []proto.Visitee) {
+ p.scope = append(p.scope, map[string]mapping{})
+ for _, e := range elems {
+ var name string
+ switch x := e.(type) {
+ case *proto.Message:
+ if x.IsExtend {
+ continue
+ }
+ name = x.Name
+ case *proto.Enum:
+ name = x.Name
+ default:
+ continue
+ }
+ sym := strings.Join(append(p.path, name), ".")
+ p.symbols[sym] = true
+ p.addRef(name, strings.Join(append(p.path, name), "_"))
+ }
+}
+
+func (p *protoConverter) popNames() {
+ p.scope = p.scope[:len(p.scope)-1]
+}
+
+func (p *protoConverter) resolve(name string, options []*proto.Option) string {
+ if strings.HasPrefix(name, ".") {
+ return p.resolveTopScope(name[1:], options)
+ }
+ for i := len(p.scope) - 1; i > 0; i-- {
+ if m, ok := p.scope[i][name]; ok {
+ return m.ref
+ }
+ }
+ return p.resolveTopScope(name, options)
+}
+
+func (p *protoConverter) resolveTopScope(name string, options []*proto.Option) string {
+ for i := 0; i < len(name); i++ {
+ k := strings.IndexByte(name[i:], '.')
+ i += k
+ if k == -1 {
+ i = len(name)
+ }
+ if m, ok := p.scope[0][name[:i]]; ok {
+ if m.pkg != nil {
+ p.used[m.pkg.goPkgPath] = true
+ }
+ return m.ref + name[i:]
+ }
+ }
+ if s, ok := protoToCUE(name, options); ok {
+ return s
+ }
+ failf("name %q not found", name)
+ return ""
+}
+
+func (p *protoConverter) doImport(v *proto.Import) {
+ if v.Filename == "cuelang/cue.proto" {
+ return
+ }
+
+ filename := ""
+ for _, p := range p.state.paths {
+ name := filepath.Join(p, v.Filename)
+ _, err := os.Stat(name)
+ if err != nil {
+ continue
+ }
+ filename = name
+ break
+ }
+
+ if filename == "" {
+ p.mustBuiltinPackage(v.Filename)
+ return
+ }
+
+ imp, err := p.state.parse(filename, nil)
+ if err != nil {
+ fail(err)
+ }
+
+ prefix := ""
+ if imp.goPkgPath != p.goPkgPath {
+ prefix = imp.goPkg + "."
+ }
+
+ pkgNamespace := strings.Split(imp.protoPkg, ".")
+ curNamespace := strings.Split(p.protoPkg, ".")
+ for {
+ for k := range imp.symbols {
+ ref := k
+ if len(pkgNamespace) > 0 {
+ ref = strings.Join(append(pkgNamespace, k), ".")
+ }
+ if _, ok := p.scope[0][ref]; !ok {
+ pkg := imp
+ if imp.goPkgPath == p.goPkgPath {
+ pkg = nil
+ }
+ p.scope[0][ref] = mapping{prefix + k, pkg}
+ }
+ }
+ if len(pkgNamespace) == 0 {
+ break
+ }
+ if len(curNamespace) == 0 || pkgNamespace[0] != curNamespace[0] {
+ break
+ }
+ pkgNamespace = pkgNamespace[1:]
+ curNamespace = curNamespace[1:]
+ }
+}
+
+func (p *protoConverter) stringLit(s string) *ast.BasicLit {
+ return &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(s)}
+}
+
+func (p *protoConverter) ref() *ast.Ident {
+ return ast.NewIdent(strings.Join(p.path, "_"))
+}
+
+func (p *protoConverter) subref(name string) *ast.Ident {
+ return ast.NewIdent(strings.Join(append(p.path, name), "_"))
+}
+
+func (p *protoConverter) addTag(f *ast.Field, body string) {
+ tag := "@protobuf(" + body + ")"
+ f.Attrs = append(f.Attrs, &ast.Attribute{Text: tag})
+}
+
+func (p *protoConverter) topElement(v proto.Visitee) {
+ switch x := v.(type) {
+ case *proto.Syntax:
+ p.proto3 = x.Value == "proto3"
+
+ case *proto.Comment:
+ if p.inBody {
+ p.file.Decls = append(p.file.Decls, comment(x, true))
+ } else {
+ addComments(p.file, 0, x, nil)
+ }
+
+ case *proto.Enum:
+ p.enum(x)
+
+ case *proto.Package:
+ if doc := x.Doc(); doc != nil {
+ addComments(p.file, 0, doc, nil)
+ }
+ // p.inBody bool
+
+ case *proto.Message:
+ p.message(x)
+
+ case *proto.Option:
+ case *proto.Import:
+ // already handled.
+
+ default:
+ failf("unsupported type %T", x)
+ }
+}
+
+func (p *protoConverter) message(v *proto.Message) {
+ defer func(saved []string) { p.path = saved }(p.path)
+ p.path = append(p.path, v.Name)
+
+ p.addNames(v.Elements)
+ defer p.popNames()
+
+ // TODO: handle IsExtend/ proto2
+
+ s := &ast.StructLit{
+ // TOOD: set proto file position.
+ }
+
+ ref := p.ref()
+ if v.Comment == nil {
+ ref.NamePos = newSection
+ }
+ f := &ast.Field{Label: ref, Value: s}
+ addComments(f, 1, v.Comment, nil)
+
+ // In CUE a message is always defined at the top level.
+ p.file.Decls = append(p.file.Decls, f)
+
+ for i, e := range v.Elements {
+ p.messageField(s, i, e)
+ }
+}
+
+func (p *protoConverter) messageField(s *ast.StructLit, i int, v proto.Visitee) {
+ switch x := v.(type) {
+ case *proto.Comment:
+ s.Elts = append(s.Elts, comment(x, true))
+
+ case *proto.NormalField:
+ f := p.parseField(s, i, x.Field)
+
+ if x.Repeated {
+ f.Value = &ast.ListLit{
+ Ellipsis: token.Pos(token.NoSpace),
+ Type: f.Value,
+ }
+ }
+
+ case *proto.MapField:
+ f := &ast.Field{}
+
+ // All keys are converted to strings.
+ // TODO: support integer keys.
+ f.Label = &ast.TemplateLabel{Ident: ast.NewIdent("_")}
+ f.Value = ast.NewIdent(p.resolve(x.Type, x.Options))
+
+ name := labelName(x.Name)
+ f = &ast.Field{
+ Label: ast.NewIdent(name),
+ Value: &ast.StructLit{Elts: []ast.Decl{f}},
+ }
+ addComments(f, i, x.Comment, x.InlineComment)
+
+ o := optionParser{message: s, field: f}
+ o.tags = fmt.Sprintf("%d,type=map<%s,%s>", x.Sequence, x.KeyType, x.Type)
+ if x.Name != name {
+ o.tags += "," + x.Name
+ }
+ s.Elts = append(s.Elts, f)
+ o.parse(x.Options)
+ p.addTag(f, o.tags)
+
+ case *proto.Enum:
+ p.enum(x)
+
+ case *proto.Message:
+ p.message(x)
+
+ case *proto.Oneof:
+ p.oneOf(x)
+
+ default:
+ failf("unsupported type %T", v)
+ }
+}
+
+// enum converts a proto enum definition to CUE.
+//
+// An enum will generate two top-level definitions:
+//
+// Enum:
+// "Value1" |
+// "Value2" |
+// "Value3"
+//
+// and
+//
+// Enum_value: {
+// "Value1": 0
+// "Value2": 1
+// }
+//
+// Enums are always defined at the top level. The name of a nested enum
+// will be prefixed with the name of its parent and an underscore.
+func (p *protoConverter) enum(x *proto.Enum) {
+ if len(x.Elements) == 0 {
+ failf("empty enum")
+ }
+
+ name := p.subref(x.Name)
+
+ p.addNames(x.Elements)
+
+ if len(p.path) == 0 {
+ defer func() { p.path = p.path[:0] }()
+ p.path = append(p.path, x.Name)
+ }
+
+ // Top-level enum entry.
+ enum := &ast.Field{Label: name}
+ addComments(enum, 1, x.Comment, nil)
+
+ // Top-level enum values entry.
+ valueName := ast.NewIdent(name.Name + "_value")
+ valueName.NamePos = newSection
+ valueMap := &ast.StructLit{}
+ d := &ast.Field{Label: valueName, Value: valueMap}
+ // addComments(valueMap, 1, x.Comment, nil)
+
+ p.file.Decls = append(p.file.Decls, enum, d)
+
+ // The line comments for an enum field need to attach after the '|', which
+ // is only known at the next iteration.
+ var lastComment *proto.Comment
+ for i, v := range x.Elements {
+ switch y := v.(type) {
+ case *proto.EnumField:
+ // Add enum value to map
+ f := &ast.Field{
+ Label: p.stringLit(y.Name),
+ Value: &ast.BasicLit{Value: strconv.Itoa(y.Integer)},
+ }
+ valueMap.Elts = append(valueMap.Elts, f)
+
+ // add to enum disjunction
+ value := p.stringLit(y.Name)
+
+ var e ast.Expr = value
+ // Make the first value the default value.
+ if i == 0 {
+ e = &ast.UnaryExpr{OpPos: newline, Op: token.MUL, X: value}
+ } else {
+ value.ValuePos = newline
+ }
+ addComments(e, i, y.Comment, nil)
+ if enum.Value != nil {
+ e = &ast.BinaryExpr{X: enum.Value, Op: token.OR, Y: e}
+ if cg := comment(lastComment, false); cg != nil {
+ cg.Position = 2
+ e.AddComment(cg)
+ }
+ }
+ enum.Value = e
+
+ if y.Comment != nil {
+ lastComment = nil
+ addComments(f, 0, nil, y.InlineComment)
+ } else {
+ lastComment = y.InlineComment
+ }
+
+ // a := fmt.Sprintf("@protobuf(enum,name=%s)", y.Name)
+ // f.Attrs = append(f.Attrs, &ast.Attribute{Text: a})
+ }
+ }
+ addComments(enum.Value, 1, nil, lastComment)
+}
+
+func (p *protoConverter) oneOf(x *proto.Oneof) {
+ f := &ast.Field{
+ Label: p.ref(),
+ }
+ f.AddComment(comment(x.Comment, true))
+
+ p.file.Decls = append(p.file.Decls, f)
+
+ for _, v := range x.Elements {
+ s := &ast.StructLit{}
+ switch x := v.(type) {
+ case *proto.OneOfField:
+ f := p.parseField(s, 0, x.Field)
+ f.Optional = token.NoPos
+
+ default:
+ p.messageField(s, 1, v)
+ }
+ var e ast.Expr = s
+ if f.Value != nil {
+ e = &ast.BinaryExpr{X: f.Value, Op: token.OR, Y: s}
+ }
+ f.Value = e
+ }
+}
+
+func (p *protoConverter) parseField(s *ast.StructLit, i int, x *proto.Field) *ast.Field {
+ f := &ast.Field{}
+ addComments(f, i, x.Comment, x.InlineComment)
+
+ name := labelName(x.Name)
+ f.Label = ast.NewIdent(name)
+ typ := p.resolve(x.Type, x.Options)
+ f.Value = ast.NewIdent(typ)
+ s.Elts = append(s.Elts, f)
+
+ o := optionParser{message: s, field: f}
+
+ // body of @protobuf tag: sequence[,type][,name=<name>][,...]
+ o.tags += fmt.Sprint(x.Sequence)
+ if x.Type != typ {
+ o.tags += ",type=" + x.Type
+ }
+ if x.Name != name {
+ o.tags += ",name=" + x.Name
+ }
+ o.parse(x.Options)
+ p.addTag(f, o.tags)
+
+ if !o.required {
+ f.Optional = token.Pos(token.NoSpace)
+ }
+ return f
+}
+
+type optionParser struct {
+ message *ast.StructLit
+ field *ast.Field
+ required bool
+ tags string
+}
+
+func (p *optionParser) parse(options []*proto.Option) {
+
+ // TODO: handle options
+ // - translate options to tags
+ // - interpret CUE options.
+ for _, o := range options {
+ switch o.Name {
+ case "(cue_opt).required":
+ p.required = true
+ // TODO: Dropping comments. Maybe add a dummy tag?
+
+ case "(cue.val)":
+ // TODO: set filename and base offset.
+ fset := token.NewFileSet()
+ expr, err := parser.ParseExpr(fset, "", o.Constant.Source)
+ if err != nil {
+ failf("invalid cue.val value: %v", err)
+ }
+ // Any further checks will be done at the end.
+ constraint := &ast.Field{Label: p.field.Label, Value: expr}
+ addComments(constraint, 1, o.Comment, o.InlineComment)
+ p.message.Elts = append(p.message.Elts, constraint)
+ if !p.required {
+ constraint.Optional = token.Pos(token.NoSpace)
+ }
+
+ default:
+ // TODO: dropping comments. Maybe add dummy tag?
+
+ // TODO: should CUE support nested attributes?
+ source := o.Constant.SourceRepresentation()
+ p.tags += "," + quote("option("+o.Name+","+source+")")
+ }
+ }
+}
diff --git a/internal/protobuf/protobuf.go b/internal/protobuf/protobuf.go
new file mode 100644
index 0000000..4280d9c
--- /dev/null
+++ b/internal/protobuf/protobuf.go
@@ -0,0 +1,88 @@
+// Copyright 2019 CUE Authors
+//
+// 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 protobuf defines functionality for parsing protocol buffer
+// definitions and instances.
+//
+// TODO: this package can become public once we have found a good nest for it.
+package protobuf
+
+import (
+ "fmt"
+ "io"
+
+ "cuelang.org/go/cue/ast"
+)
+
+// Config specifies the environment into which to parse a proto definition file.
+type Config struct {
+ Paths []string
+}
+
+// Parse parses a single proto file and returns its contents translated to
+// a CUE file. Imports are resolved using the path define in Config.
+// If body is not nil, it will use this as the contents of the file. Otherwise
+// Parse will open the given file name at the fully qualified path.
+//
+// The following field options are supported:
+// (cue.val) string CUE constraint for this field. The string may
+// refer to other fields in a message definition.
+// (cue.opt) FieldOptions
+// required bool Defines the field is required. Use with
+// caution.
+func Parse(filename string, body io.Reader, c *Config) (f *ast.File, err error) {
+ state := &sharedState{
+ paths: c.Paths,
+ }
+ p, err := state.parse(filename, body)
+ if err != nil {
+ return nil, err
+ }
+ return p.file, nil
+}
+
+// ProtoError describes the location and cause of an error.
+type ProtoError struct {
+ Filename string
+ Path string
+ Err error
+}
+
+func (p *ProtoError) Unwrap() error { return p.Err }
+
+func (p *ProtoError) Error() string {
+ if p.Path == "" {
+ return fmt.Sprintf("parse of file %q failed: %v", p.Filename, p.Err)
+ }
+ return fmt.Sprintf("parse of file %q failed at %s: %v", p.Filename, p.Path, p.Err)
+}
+
+// TODO
+// func GenDefinition
+
+// func MarshalText(cue.Value) (string, error) {
+// return "", nil
+// }
+
+// func MarshalBytes(cue.Value) ([]byte, error) {
+// return nil, nil
+// }
+
+// func UnmarshalText(descriptor cue.Value, b string) (ast.Expr, error) {
+// return nil, nil
+// }
+
+// func UnmarshalBytes(descriptor cue.Value, b []byte) (ast.Expr, error) {
+// return nil, nil
+// }
diff --git a/internal/protobuf/protobuf_test.go b/internal/protobuf/protobuf_test.go
new file mode 100644
index 0000000..d75126d
--- /dev/null
+++ b/internal/protobuf/protobuf_test.go
@@ -0,0 +1,68 @@
+// Copyright 2019 CUE Authors
+//
+// 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 protobuf
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+ "testing"
+
+ "cuelang.org/go/cue/format"
+ "github.com/kr/pretty"
+)
+
+var update *bool = flag.Bool("update", false, "update the test output")
+
+func TestParseDefinitions(t *testing.T) {
+ testCases := []string{
+ "networking/v1alpha3/gateway.proto",
+ "mixer/v1/attributes.proto",
+ "mixer/v1/config/client/client_config.proto",
+ }
+ for _, file := range testCases {
+ t.Run(file, func(t *testing.T) {
+ filename := filepath.Join("testdata", filepath.FromSlash(file))
+ c := &Config{
+ Paths: []string{"testdata"},
+ }
+
+ out := &bytes.Buffer{}
+
+ if f, err := Parse(filename, nil, c); err != nil {
+ fmt.Fprintln(out, err)
+ } else {
+ format.Node(out, f)
+ }
+
+ wantFile := filepath.Join("testdata", filepath.Base(file)+".out.cue")
+ if *update {
+ ioutil.WriteFile(wantFile, out.Bytes(), 0644)
+ return
+ }
+
+ b, err := ioutil.ReadFile(wantFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if desc := pretty.Diff(out.String(), string(b)); len(desc) > 0 {
+ t.Errorf("files differ:\n%v", desc)
+ }
+ })
+ }
+}
diff --git a/internal/protobuf/testdata/attributes.proto.out.cue b/internal/protobuf/testdata/attributes.proto.out.cue
new file mode 100644
index 0000000..73f70ad
--- /dev/null
+++ b/internal/protobuf/testdata/attributes.proto.out.cue
@@ -0,0 +1,136 @@
+
+// Copyright 2016 Istio Authors
+//
+// 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 v1
+
+import "time"
+
+// Attributes represents a set of typed name/value pairs. Many of Mixer's
+// API either consume and/or return attributes.
+//
+// Istio uses attributes to control the runtime behavior of services running in the service mesh.
+// Attributes are named and typed pieces of metadata describing ingress and egress traffic and the
+// environment this traffic occurs in. An Istio attribute carries a specific piece
+// of information such as the error code of an API request, the latency of an API request, or the
+// original IP address of a TCP connection. For example:
+//
+// ```yaml
+// request.path: xyz/abc
+// request.size: 234
+// request.time: 12:34:56.789 04/17/2017
+// source.ip: 192.168.0.1
+// target.service: example
+// ```
+//
+// A given Istio deployment has a fixed vocabulary of attributes that it understands.
+// The specific vocabulary is determined by the set of attribute producers being used
+// in the deployment. The primary attribute producer in Istio is Envoy, although
+// specialized Mixer adapters and services can also generate attributes.
+//
+// The common baseline set of attributes available in most Istio deployments is defined
+// [here](https://istio.io/docs/reference/config/policy-and-telemetry/attribute-vocabulary/).
+//
+// Attributes are strongly typed. The supported attribute types are defined by
+// [ValueType](https://github.com/istio/api/blob/master/policy/v1beta1/value_type.proto).
+// Each type of value is encoded into one of the so-called transport types present
+// in this message.
+//
+// Defines a map of attributes in uncompressed format.
+// Following places may use this message:
+// 1) Configure Istio/Proxy with static per-proxy attributes, such as source.uid.
+// 2) Service IDL definition to extract api attributes for active requests.
+// 3) Forward attributes from client proxy to server proxy for HTTP requests.
+Attributes: {
+ // A map of attribute name to its value.
+ attributes <_>: Attributes_AttributeValue
+}
+
+// Specifies one attribute value with different type.
+Attributes_AttributeValue: {
+}
+// The attribute value.
+Attributes_AttributeValue: {
+ // Used for values of type STRING, DNS_NAME, EMAIL_ADDRESS, and URI
+ stringValue: string @protobuf(2,name=string_value)
+} | {
+ // Used for values of type INT64
+ int64Value: int64 @protobuf(3,name=int64_value)
+} | {
+ // Used for values of type DOUBLE
+ doubleValue: float64 @protobuf(4,type=double,name=double_value)
+} | {
+ // Used for values of type BOOL
+ boolValue: bool @protobuf(5,name=bool_value)
+} | {
+ // Used for values of type BYTES
+ bytesValue: bytes @protobuf(6,name=bytes_value)
+} | {
+ // Used for values of type TIMESTAMP
+ timestampValue: time.Time @protobuf(7,type=google.protobuf.Timestamp,name=timestamp_value)
+} | {
+ // Used for values of type DURATION
+ durationValue: time.Duration @protobuf(8,type=google.protobuf.Duration,name=duration_value)
+} | {
+ // Used for values of type STRING_MAP
+ stringMapValue: Attributes_StringMap @protobuf(9,type=StringMap,name=string_map_value)
+}
+
+// Defines a string map.
+Attributes_StringMap: {
+ // Holds a set of name/value pairs.
+ entries <_>: string
+}
+
+// Defines a list of attributes in compressed format optimized for transport.
+// Within this message, strings are referenced using integer indices into
+// one of two string dictionaries. Positive integers index into the global
+// deployment-wide dictionary, whereas negative integers index into the message-level
+// dictionary instead. The message-level dictionary is carried by the
+// `words` field of this message, the deployment-wide dictionary is determined via
+// configuration.
+CompressedAttributes: {
+ // The message-level dictionary.
+ words?: [...string] @protobuf(1)
+
+ // Holds attributes of type STRING, DNS_NAME, EMAIL_ADDRESS, URI
+ strings <_>: int32
+
+ // Holds attributes of type INT64
+ int64s <_>: int64
+
+ // Holds attributes of type DOUBLE
+ doubles <_>: float64
+
+ // Holds attributes of type BOOL
+ bools <_>: bool
+
+ // Holds attributes of type TIMESTAMP
+ timestamps <_>: time.Time
+
+ // Holds attributes of type DURATION
+ durations <_>: time.Duration
+
+ // Holds attributes of type BYTES
+ bytes <_>: bytes
+
+ // Holds attributes of type STRING_MAP
+ stringMaps <_>: StringMap
+}
+
+// A map of string to string. The keys and values in this map are dictionary
+// indices (see the [Attributes][istio.mixer.v1.CompressedAttributes] message for an explanation)
+StringMap: {
+ // Holds a set of name/value pairs.
+ entries <_>: int32
+}
diff --git a/internal/protobuf/testdata/client_config.proto.out.cue b/internal/protobuf/testdata/client_config.proto.out.cue
new file mode 100644
index 0000000..880309e
--- /dev/null
+++ b/internal/protobuf/testdata/client_config.proto.out.cue
@@ -0,0 +1,193 @@
+
+// Copyright 2017 Istio Authors
+//
+// 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.
+
+// $title: Mixer Client
+// $description: Configuration state for the Mixer client library.
+// $location: https://istio.io/docs/reference/config/policy-and-telemetry/istio.mixer.v1.config.client
+
+// Describes the configuration state for the Mixer client library that's built into Envoy.
+package client
+
+import (
+ "istio.io/api/mixer/v1"
+ "time"
+)
+
+// Specifies the behavior when the client is unable to connect to Mixer.
+NetworkFailPolicy: {
+
+ // Specifies the behavior when the client is unable to connect to Mixer.
+ policy?: NetworkFailPolicy_FailPolicy @protobuf(1,type=FailPolicy)
+
+ // Max retries on transport error.
+ maxRetry?: uint32 @protobuf(2,name=max_retry)
+
+ // Base time to wait between retries. Will be adjusted by exponential
+ // backoff and jitter.
+ baseRetryWait?: time.Duration @protobuf(3,type=google.protobuf.Duration,name=base_retry_wait)
+
+ // Max time to wait between retries.
+ maxRetryWait?: time.Duration @protobuf(4,type=google.protobuf.Duration,name=max_retry_wait)
+}
+
+// Describes the policy.
+NetworkFailPolicy_FailPolicy:
+ // If network connection fails, request is allowed and delivered to the
+ // service.
+ *"FAIL_OPEN" |
+
+ // If network connection fails, request is rejected.
+ "FAIL_CLOSE"
+
+NetworkFailPolicy_FailPolicy_value: {
+ "FAIL_OPEN": 0
+ "FAIL_CLOSE": 1
+}
+
+// Defines the per-service client configuration.
+ServiceConfig: {
+ // If true, do not call Mixer Check.
+ disableCheckCalls?: bool @protobuf(1,name=disable_check_calls)
+
+ // If true, do not call Mixer Report.
+ disableReportCalls?: bool @protobuf(2,name=disable_report_calls)
+
+ // Send these attributes to Mixer in both Check and Report. This
+ // typically includes the "destination.service" attribute.
+ // In case of a per-route override, per-route attributes take precedence
+ // over the attributes supplied in the client configuration.
+ mixerAttributes?: v1.Attributes @protobuf(3,type=Attributes,name=mixer_attributes)
+
+ // HTTP API specifications to generate API attributes.
+ httpApiSpec?: [...HTTPAPISpec] @protobuf(4,name=http_api_spec)
+
+ // Quota specifications to generate quota requirements.
+ quotaSpec?: [...QuotaSpec] @protobuf(5,name=quota_spec)
+
+ // Specifies the behavior when the client is unable to connect to Mixer.
+ // This is the service-level policy. It overrides
+ // [mesh-level
+ // policy][istio.mixer.v1.config.client.TransportConfig.network_fail_policy].
+ networkFailPolicy?: NetworkFailPolicy @protobuf(7,name=network_fail_policy)
+
+ // Default attributes to forward to upstream. This typically
+ // includes the "source.ip" and "source.uid" attributes.
+ // In case of a per-route override, per-route attributes take precedence
+ // over the attributes supplied in the client configuration.
+ //
+ // Forwarded attributes take precedence over the static Mixer attributes.
+ // The full order of application is as follows:
+ // 1. static Mixer attributes from the filter config;
+ // 2. static Mixer attributes from the route config;
+ // 3. forwarded attributes from the source filter config (if any);
+ // 4. forwarded attributes from the source route config (if any);
+ // 5. derived attributes from the request metadata.
+ forwardAttributes?: v1.Attributes @protobuf(8,type=Attributes,name=forward_attributes)
+}
+
+// Defines the transport config on how to call Mixer.
+TransportConfig: {
+ // The flag to disable check cache.
+ disableCheckCache?: bool @protobuf(1,name=disable_check_cache)
+
+ // The flag to disable quota cache.
+ disableQuotaCache?: bool @protobuf(2,name=disable_quota_cache)
+
+ // The flag to disable report batch.
+ disableReportBatch?: bool @protobuf(3,name=disable_report_batch)
+
+ // Specifies the behavior when the client is unable to connect to Mixer.
+ // This is the mesh level policy. The default value for policy is FAIL_OPEN.
+ networkFailPolicy?: NetworkFailPolicy @protobuf(4,name=network_fail_policy)
+
+ // Specify refresh interval to write Mixer client statistics to Envoy share
+ // memory. If not specified, the interval is 10 seconds.
+ statsUpdateInterval?: time.Duration @protobuf(5,type=google.protobuf.Duration,name=stats_update_interval)
+
+ // Name of the cluster that will forward check calls to a pool of mixer
+ // servers. Defaults to "mixer_server". By using different names for
+ // checkCluster and reportCluster, it is possible to have one set of
+ // Mixer servers handle check calls, while another set of Mixer servers
+ // handle report calls.
+ //
+ // NOTE: Any value other than the default "mixer_server" will require the
+ // Istio Grafana dashboards to be reconfigured to use the new name.
+ checkCluster?: string @protobuf(6,name=check_cluster)
+
+ // Name of the cluster that will forward report calls to a pool of mixer
+ // servers. Defaults to "mixer_server". By using different names for
+ // checkCluster and reportCluster, it is possible to have one set of
+ // Mixer servers handle check calls, while another set of Mixer servers
+ // handle report calls.
+ //
+ // NOTE: Any value other than the default "mixer_server" will require the
+ // Istio Grafana dashboards to be reconfigured to use the new name.
+ reportCluster?: string @protobuf(7,name=report_cluster)
+
+ // Default attributes to forward to Mixer upstream. This typically
+ // includes the "source.ip" and "source.uid" attributes. These
+ // attributes are consumed by the proxy in front of mixer.
+ attributesForMixerProxy?: v1.Attributes @protobuf(8,type=Attributes,name=attributes_for_mixer_proxy)
+}
+
+// Defines the client config for HTTP.
+HttpClientConfig: {
+ // The transport config.
+ transport?: TransportConfig @protobuf(1)
+
+ // Map of control configuration indexed by destination.service. This
+ // is used to support per-service configuration for cases where a
+ // mixerclient serves multiple services.
+ serviceConfigs <_>: ServiceConfig
+
+ // Default destination service name if none was specified in the
+ // client request.
+ defaultDestinationService?: string @protobuf(3,name=default_destination_service)
+
+ // Default attributes to send to Mixer in both Check and
+ // Report. This typically includes "destination.ip" and
+ // "destination.uid" attributes.
+ mixerAttributes?: v1.Attributes @protobuf(4,type=Attributes,name=mixer_attributes)
+
+ // Default attributes to forward to upstream. This typically
+ // includes the "source.ip" and "source.uid" attributes.
+ forwardAttributes?: v1.Attributes @protobuf(5,type=Attributes,name=forward_attributes)
+}
+
+// Defines the client config for TCP.
+TcpClientConfig: {
+ // The transport config.
+ transport?: TransportConfig @protobuf(1)
+
+ // Default attributes to send to Mixer in both Check and
+ // Report. This typically includes "destination.ip" and
+ // "destination.uid" attributes.
+ mixerAttributes?: v1.Attributes @protobuf(2,type=Attributes,name=mixer_attributes)
+
+ // If set to true, disables Mixer check calls.
+ disableCheckCalls?: bool @protobuf(3,name=disable_check_calls)
+
+ // If set to true, disables Mixer check calls.
+ disableReportCalls?: bool @protobuf(4,name=disable_report_calls)
+
+ // Quota specifications to generate quota requirements.
+ // It applies on the new TCP connections.
+ connectionQuotaSpec?: QuotaSpec @protobuf(5,name=connection_quota_spec)
+
+ // Specify report interval to send periodical reports for long TCP
+ // connections. If not specified, the interval is 10 seconds. This interval
+ // should not be less than 1 second, otherwise it will be reset to 1 second.
+ reportInterval?: time.Duration @protobuf(6,type=google.protobuf.Duration,name=report_interval)
+}
diff --git a/internal/protobuf/testdata/cue.proto b/internal/protobuf/testdata/cue.proto
new file mode 100644
index 0000000..4bbb948
--- /dev/null
+++ b/internal/protobuf/testdata/cue.proto
@@ -0,0 +1,18 @@
+syntax = "proto3";
+
+package cue;
+
+import "google/protobuf/descriptor.proto";
+
+option go_package = "cuelang.org/cueproto";
+option java_package = "org.cuelang.cueproto";
+
+message FieldOptions {
+ bool required = 1;
+}
+
+extend google.protobuf.FieldOptions {
+ string val = 123456;
+ FieldOptions opt = 1069;
+}
+
diff --git a/internal/protobuf/testdata/cue.proto.out.cue b/internal/protobuf/testdata/cue.proto.out.cue
new file mode 100644
index 0000000..3a46082
--- /dev/null
+++ b/internal/protobuf/testdata/cue.proto.out.cue
@@ -0,0 +1,8 @@
+package proto
+
+FieldOptions required?: bool @protobuf(1)
+
+google.protobuf.FieldOptions: {
+ val?: string @protobuf(123456)
+ opt?: FieldOptions @protobuf(1069)
+}
diff --git a/internal/protobuf/testdata/gateway.proto.out.cue b/internal/protobuf/testdata/gateway.proto.out.cue
new file mode 100644
index 0000000..14e4624
--- /dev/null
+++ b/internal/protobuf/testdata/gateway.proto.out.cue
@@ -0,0 +1,451 @@
+
+// Copyright 2019 CUE Authors
+//
+// 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.
+
+// Copyright 2017 Istio Authors
+//
+// 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.
+
+// $title: Gateway
+// $description: Configuration affecting edge load balancer.
+// $location: https://istio.io/docs/reference/config/networking/v1alpha3/gateway.html
+
+// `Gateway` describes a load balancer operating at the edge of the mesh
+// receiving incoming or outgoing HTTP/TCP connections. The specification
+// describes a set of ports that should be exposed, the type of protocol to
+// use, SNI configuration for the load balancer, etc.
+//
+// For example, the following Gateway configuration sets up a proxy to act
+// as a load balancer exposing port 80 and 9080 (http), 443 (https),
+// 9443(https) and port 2379 (TCP) for ingress. The gateway will be
+// applied to the proxy running on a pod with labels `app:
+// my-gateway-controller`. While Istio will configure the proxy to listen
+// on these ports, it is the responsibility of the user to ensure that
+// external traffic to these ports are allowed into the mesh.
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-gateway
+// namespace: some-config-namespace
+// spec:
+// selector:
+// app: my-gateway-controller
+// servers:
+// - port:
+// number: 80
+// name: http
+// protocol: HTTP
+// hosts:
+// - uk.bookinfo.com
+// - eu.bookinfo.com
+// tls:
+// httpsRedirect: true # sends 301 redirect for http requests
+// - port:
+// number: 443
+// name: https-443
+// protocol: HTTPS
+// hosts:
+// - uk.bookinfo.com
+// - eu.bookinfo.com
+// tls:
+// mode: SIMPLE # enables HTTPS on this port
+// serverCertificate: /etc/certs/servercert.pem
+// privateKey: /etc/certs/privatekey.pem
+// - port:
+// number: 9443
+// name: https-9443
+// protocol: HTTPS
+// hosts:
+// - "bookinfo-namespace/*.bookinfo.com"
+// tls:
+// mode: SIMPLE # enables HTTPS on this port
+// credentialName: bookinfo-secret # fetches certs from Kubernetes secret
+// - port:
+// number: 9080
+// name: http-wildcard
+// protocol: HTTP
+// hosts:
+// - "*"
+// - port:
+// number: 2379 # to expose internal service via external port 2379
+// name: mongo
+// protocol: MONGO
+// hosts:
+// - "*"
+// ```
+//
+// The Gateway specification above describes the L4-L6 properties of a load
+// balancer. A `VirtualService` can then be bound to a gateway to control
+// the forwarding of traffic arriving at a particular host or gateway port.
+//
+// For example, the following VirtualService splits traffic for
+// `https://uk.bookinfo.com/reviews`, `https://eu.bookinfo.com/reviews`,
+// `http://uk.bookinfo.com:9080/reviews`,
+// `http://eu.bookinfo.com:9080/reviews` into two versions (prod and qa) of
+// an internal reviews service on port 9080. In addition, requests
+// containing the cookie "user: dev-123" will be sent to special port 7777
+// in the qa version. The same rule is also applicable inside the mesh for
+// requests to the "reviews.prod.svc.cluster.local" service. This rule is
+// applicable across ports 443, 9080. Note that `http://uk.bookinfo.com`
+// gets redirected to `https://uk.bookinfo.com` (i.e. 80 redirects to 443).
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: VirtualService
+// metadata:
+// name: bookinfo-rule
+// namespace: bookinfo-namespace
+// spec:
+// hosts:
+// - reviews.prod.svc.cluster.local
+// - uk.bookinfo.com
+// - eu.bookinfo.com
+// gateways:
+// - some-config-namespace/my-gateway
+// - mesh # applies to all the sidecars in the mesh
+// http:
+// - match:
+// - headers:
+// cookie:
+// exact: "user=dev-123"
+// route:
+// - destination:
+// port:
+// number: 7777
+// host: reviews.qa.svc.cluster.local
+// - match:
+// - uri:
+// prefix: /reviews/
+// route:
+// - destination:
+// port:
+// number: 9080 # can be omitted if it's the only port for reviews
+// host: reviews.prod.svc.cluster.local
+// weight: 80
+// - destination:
+// host: reviews.qa.svc.cluster.local
+// weight: 20
+// ```
+//
+// The following VirtualService forwards traffic arriving at (external)
+// port 27017 to internal Mongo server on port 5555. This rule is not
+// applicable internally in the mesh as the gateway list omits the
+// reserved name `mesh`.
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: VirtualService
+// metadata:
+// name: bookinfo-Mongo
+// namespace: bookinfo-namespace
+// spec:
+// hosts:
+// - mongosvr.prod.svc.cluster.local # name of internal Mongo service
+// gateways:
+// - some-config-namespace/my-gateway # can omit the namespace if gateway is in same
+// namespace as virtual service.
+// tcp:
+// - match:
+// - port: 27017
+// route:
+// - destination:
+// host: mongo.prod.svc.cluster.local
+// port:
+// number: 5555
+// ```
+//
+// It is possible to restrict the set of virtual services that can bind to
+// a gateway server using the namespace/hostname syntax in the hosts field.
+// For example, the following Gateway allows any virtual service in the ns1
+// namespace to bind to it, while restricting only the virtual service with
+// foo.bar.com host in the ns2 namespace to bind to it.
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-gateway
+// namespace: some-config-namespace
+// spec:
+// selector:
+// app: my-gateway-controller
+// servers:
+// - port:
+// number: 80
+// name: http
+// protocol: HTTP
+// hosts:
+// - "ns1/*"
+// - "ns2/foo.bar.com"
+// ```
+//
+package v1alpha3
+
+Gateway: {
+ // REQUIRED: A list of server specifications.
+ servers?: [...Server] @protobuf(1)
+
+ // REQUIRED: One or more labels that indicate a specific set of pods/VMs
+ // on which this gateway configuration should be applied. The scope of
+ // label search is restricted to the configuration namespace in which the
+ // the resource is present. In other words, the Gateway resource must
+ // reside in the same namespace as the gateway workload instance.
+ selector <_>: string
+ selector?: {<name>: name}
+}
+
+// `Server` describes the properties of the proxy on a given load balancer
+// port. For example,
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-ingress
+// spec:
+// selector:
+// app: my-ingress-gateway
+// servers:
+// - port:
+// number: 80
+// name: http2
+// protocol: HTTP2
+// hosts:
+// - "*"
+// ```
+//
+// Another example
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-tcp-ingress
+// spec:
+// selector:
+// app: my-tcp-ingress-gateway
+// servers:
+// - port:
+// number: 27018
+// name: mongo
+// protocol: MONGO
+// hosts:
+// - "*"
+// ```
+//
+// The following is an example of TLS configuration for port 443
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-tls-ingress
+// spec:
+// selector:
+// app: my-tls-ingress-gateway
+// servers:
+// - port:
+// number: 443
+// name: https
+// protocol: HTTPS
+// hosts:
+// - "*"
+// tls:
+// mode: SIMPLE
+// serverCertificate: /etc/certs/server.pem
+// privateKey: /etc/certs/privatekey.pem
+// ```
+Server: {
+ // REQUIRED: The Port on which the proxy should listen for incoming
+ // connections.
+ port?: Port @protobuf(1)
+ port?: >10 & <100
+
+ // $hide_from_docs
+ // The ip or the Unix domain socket to which the listener should be bound
+ // to. Format: `x.x.x.x` or `unix:///path/to/uds` or `unix://@foobar`
+ // (Linux abstract namespace). When using Unix domain sockets, the port
+ // number should be 0.
+ bind?: string @protobuf(4)
+
+ // REQUIRED. One or more hosts exposed by this gateway.
+ // While typically applicable to
+ // HTTP services, it can also be used for TCP services using TLS with SNI.
+ // A host is specified as a `dnsName` with an optional `namespace/` prefix.
+ // The `dnsName` should be specified using FQDN format, optionally including
+ // a wildcard character in the left-most component (e.g., `prod/*.example.com`).
+ // Set the `dnsName` to `*` to select all `VirtualService` hosts from the
+ // specified namespace (e.g.,`prod/*`). If no `namespace/` is specified,
+ // the `VirtualService` hosts will be selected from any available namespace.
+ // Any associated `DestinationRule` in the same namespace will also be used.
+ //
+ // A `VirtualService` must be bound to the gateway and must have one or
+ // more hosts that match the hosts specified in a server. The match
+ // could be an exact match or a suffix match with the server's hosts. For
+ // example, if the server's hosts specifies `*.example.com`, a
+ // `VirtualService` with hosts `dev.example.com` or `prod.example.com` will
+ // match. However, a `VirtualService` with host `example.com` or
+ // `newexample.com` will not match.
+ //
+ // NOTE: Only virtual services exported to the gateway's namespace
+ // (e.g., `exportTo` value of `*`) can be referenced.
+ // Private configurations (e.g., `exportTo` set to `.`) will not be
+ // available. Refer to the `exportTo` setting in `VirtualService`,
+ // `DestinationRule`, and `ServiceEntry` configurations for details.
+ hosts?: [...string] @protobuf(2)
+
+ // Set of TLS related options that govern the server's behavior. Use
+ // these options to control if all http requests should be redirected to
+ // https, and the TLS modes to use.
+ tls?: Server_TLSOptions @protobuf(3,type=TLSOptions)
+
+ // The loopback IP endpoint or Unix domain socket to which traffic should
+ // be forwarded to by default. Format should be `127.0.0.1:PORT` or
+ // `unix:///path/to/socket` or `unix://@foobar` (Linux abstract namespace).
+ defaultEndpoint?: string @protobuf(5,name=default_endpoint)
+}
+
+Server_TLSOptions: {
+ // If set to true, the load balancer will send a 301 redirect for all
+ // http connections, asking the clients to use HTTPS.
+ httpsRedirect?: bool @protobuf(1,name=https_redirect)
+
+ // Optional: Indicates whether connections to this port should be
+ // secured using TLS. The value of this field determines how TLS is
+ // enforced.
+ mode?: Server_TLSOptions_TLSmode @protobuf(2,type=TLSmode)
+
+ // Extra comment.
+
+ // REQUIRED if mode is `SIMPLE` or `MUTUAL`. The path to the file
+ // holding the server-side TLS certificate to use.
+ serverCertificate?: string @protobuf(3,name=server_certificate)
+
+ // REQUIRED if mode is `SIMPLE` or `MUTUAL`. The path to the file
+ // holding the server's private key.
+ privateKey?: string @protobuf(4,name=private_key)
+
+ // REQUIRED if mode is `MUTUAL`. The path to a file containing
+ // certificate authority certificates to use in verifying a presented
+ // client side certificate.
+ caCertificates?: string @protobuf(5,name=ca_certificates)
+
+ // The credentialName stands for a unique identifier that can be used
+ // to identify the serverCertificate and the privateKey. The
+ // credentialName appended with suffix "-cacert" is used to identify
+ // the CaCertificates associated with this server. Gateway workloads
+ // capable of fetching credentials from a remote credential store such
+ // as Kubernetes secrets, will be configured to retrieve the
+ // serverCertificate and the privateKey using credentialName, instead
+ // of using the file system paths specified above. If using mutual TLS,
+ // gateway workload instances will retrieve the CaCertificates using
+ // credentialName-cacert. The semantics of the name are platform
+ // dependent. In Kubernetes, the default Istio supplied credential
+ // server expects the credentialName to match the name of the
+ // Kubernetes secret that holds the server certificate, the private
+ // key, and the CA certificate (if using mutual TLS). Set the
+ // `ISTIO_META_USER_SDS` metadata variable in the gateway's proxy to
+ // enable the dynamic credential fetching feature.
+ credentialName?: string @protobuf(10,name=credential_name)
+
+ // A list of alternate names to verify the subject identity in the
+ // certificate presented by the client.
+ subjectAltNames?: [...string] @protobuf(6,name=subject_alt_names)
+
+ // Optional: Minimum TLS protocol version.
+ minProtocolVersion?: Server_TLSOptions_TLSProtocol @protobuf(7,type=TLSProtocol,name=min_protocol_version)
+
+ // Optional: Maximum TLS protocol version.
+ maxProtocolVersion?: Server_TLSOptions_TLSProtocol @protobuf(8,type=TLSProtocol,name=max_protocol_version)
+
+ // Optional: If specified, only support the specified cipher list.
+ // Otherwise default to the default cipher list supported by Envoy.
+ cipherSuites?: [...string] @protobuf(9,name=cipher_suites)
+}
+
+// TLS modes enforced by the proxy
+Server_TLSOptions_TLSmode:
+ // The SNI string presented by the client will be used as the match
+ // criterion in a VirtualService TLS route to determine the
+ // destination service from the service registry.
+ *"PASSTHROUGH" |
+
+ // Secure connections with standard TLS semantics.
+ "SIMPLE" |
+
+ // Secure connections to the upstream using mutual TLS by presenting
+ // client certificates for authentication.
+ "MUTUAL" |
+
+ // Similar to the passthrough mode, except servers with this TLS mode
+ // do not require an associated VirtualService to map from the SNI
+ // value to service in the registry. The destination details such as
+ // the service/subset/port are encoded in the SNI value. The proxy
+ // will forward to the upstream (Envoy) cluster (a group of
+ // endpoints) specified by the SNI value. This server is typically
+ // used to provide connectivity between services in disparate L3
+ // networks that otherwise do not have direct connectivity between
+ // their respective endpoints. Use of this mode assumes that both the
+ // source and the destination are using Istio mTLS to secure traffic.
+ "AUTO_PASSTHROUGH"
+
+Server_TLSOptions_TLSmode_value: {
+ "PASSTHROUGH": 0
+ "SIMPLE": 1
+ "MUTUAL": 2
+ "AUTO_PASSTHROUGH": 3
+}
+
+// TLS protocol versions.
+Server_TLSOptions_TLSProtocol:
+ *"TLS_AUTO" | // Automatically choose the optimal TLS version.
+ "TLSV1_0" | // TLS version 1.0
+ "TLSV1_1" | // TLS version 1.1
+ "TLSV1_2" | // TLS version 1.2
+ "TLSV1_3" // TLS version 1.3
+
+Server_TLSOptions_TLSProtocol_value: {
+ "TLS_AUTO": 0
+ "TLSV1_0": 1
+ "TLSV1_1": 2
+ "TLSV1_2": 3
+ "TLSV1_3": 4
+}
+
+// Port describes the properties of a specific port of a service.
+Port: {
+ // REQUIRED: A valid non-negative integer port number.
+ number?: uint32 @protobuf(1)
+
+ // REQUIRED: The protocol exposed on the port.
+ // MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP|TLS.
+ // TLS implies the connection will be routed based on the SNI header to
+ // the destination without terminating the TLS connection.
+ protocol?: string @protobuf(2)
+
+ // Label assigned to the port.
+ name?: string @protobuf(3)
+}
diff --git a/internal/protobuf/testdata/mixer/v1/attributes.proto b/internal/protobuf/testdata/mixer/v1/attributes.proto
new file mode 100644
index 0000000..32fa4fc
--- /dev/null
+++ b/internal/protobuf/testdata/mixer/v1/attributes.proto
@@ -0,0 +1,148 @@
+// Copyright 2016 Istio Authors
+//
+// 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.
+
+syntax = "proto3";
+
+package istio.mixer.v1;
+
+option go_package = "istio.io/api/mixer/v1";
+
+import "gogoproto/gogo.proto";
+import "google/protobuf/duration.proto";
+import "google/protobuf/timestamp.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.equal_all) = false;
+option (gogoproto.gostring_all) = false;
+option (gogoproto.stable_marshaler_all) = true;
+option cc_enable_arenas = true;
+
+// Attributes represents a set of typed name/value pairs. Many of Mixer's
+// API either consume and/or return attributes.
+//
+// Istio uses attributes to control the runtime behavior of services running in the service mesh.
+// Attributes are named and typed pieces of metadata describing ingress and egress traffic and the
+// environment this traffic occurs in. An Istio attribute carries a specific piece
+// of information such as the error code of an API request, the latency of an API request, or the
+// original IP address of a TCP connection. For example:
+//
+// ```yaml
+// request.path: xyz/abc
+// request.size: 234
+// request.time: 12:34:56.789 04/17/2017
+// source.ip: 192.168.0.1
+// target.service: example
+// ```
+//
+// A given Istio deployment has a fixed vocabulary of attributes that it understands.
+// The specific vocabulary is determined by the set of attribute producers being used
+// in the deployment. The primary attribute producer in Istio is Envoy, although
+// specialized Mixer adapters and services can also generate attributes.
+//
+// The common baseline set of attributes available in most Istio deployments is defined
+// [here](https://istio.io/docs/reference/config/policy-and-telemetry/attribute-vocabulary/).
+//
+// Attributes are strongly typed. The supported attribute types are defined by
+// [ValueType](https://github.com/istio/api/blob/master/policy/v1beta1/value_type.proto).
+// Each type of value is encoded into one of the so-called transport types present
+// in this message.
+//
+// Defines a map of attributes in uncompressed format.
+// Following places may use this message:
+// 1) Configure Istio/Proxy with static per-proxy attributes, such as source.uid.
+// 2) Service IDL definition to extract api attributes for active requests.
+// 3) Forward attributes from client proxy to server proxy for HTTP requests.
+message Attributes {
+ // A map of attribute name to its value.
+ map<string, AttributeValue> attributes = 1;
+
+ // Specifies one attribute value with different type.
+ message AttributeValue {
+ // The attribute value.
+ oneof value {
+ // Used for values of type STRING, DNS_NAME, EMAIL_ADDRESS, and URI
+ string string_value = 2;
+
+ // Used for values of type INT64
+ int64 int64_value = 3;
+
+ // Used for values of type DOUBLE
+ double double_value = 4;
+
+ // Used for values of type BOOL
+ bool bool_value = 5;
+
+ // Used for values of type BYTES
+ bytes bytes_value = 6;
+
+ // Used for values of type TIMESTAMP
+ google.protobuf.Timestamp timestamp_value = 7;
+
+ // Used for values of type DURATION
+ google.protobuf.Duration duration_value = 8;
+
+ // Used for values of type STRING_MAP
+ StringMap string_map_value = 9;
+ }
+ }
+
+ // Defines a string map.
+ message StringMap {
+ // Holds a set of name/value pairs.
+ map<string, string> entries = 1;
+ }
+}
+
+// Defines a list of attributes in compressed format optimized for transport.
+// Within this message, strings are referenced using integer indices into
+// one of two string dictionaries. Positive integers index into the global
+// deployment-wide dictionary, whereas negative integers index into the message-level
+// dictionary instead. The message-level dictionary is carried by the
+// `words` field of this message, the deployment-wide dictionary is determined via
+// configuration.
+message CompressedAttributes {
+ // The message-level dictionary.
+ repeated string words = 1;
+
+ // Holds attributes of type STRING, DNS_NAME, EMAIL_ADDRESS, URI
+ map<sint32, sint32> strings = 2;
+
+ // Holds attributes of type INT64
+ map<sint32, int64> int64s = 3;
+
+ // Holds attributes of type DOUBLE
+ map<sint32, double> doubles = 4;
+
+ // Holds attributes of type BOOL
+ map<sint32, bool> bools = 5;
+
+ // Holds attributes of type TIMESTAMP
+ map<sint32, google.protobuf.Timestamp> timestamps = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
+
+ // Holds attributes of type DURATION
+ map<sint32, google.protobuf.Duration> durations = 7 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
+
+ // Holds attributes of type BYTES
+ map<sint32, bytes> bytes = 8;
+
+ // Holds attributes of type STRING_MAP
+ map<sint32, StringMap> string_maps = 9 [(gogoproto.nullable) = false];
+}
+
+// A map of string to string. The keys and values in this map are dictionary
+// indices (see the [Attributes][istio.mixer.v1.CompressedAttributes] message for an explanation)
+message StringMap {
+ // Holds a set of name/value pairs.
+ map<sint32, sint32> entries = 1;
+}
diff --git a/internal/protobuf/testdata/mixer/v1/config/client/api_spec.proto b/internal/protobuf/testdata/mixer/v1/config/client/api_spec.proto
new file mode 100644
index 0000000..92dfefc
--- /dev/null
+++ b/internal/protobuf/testdata/mixer/v1/config/client/api_spec.proto
@@ -0,0 +1,241 @@
+// Copyright 2017 Istio Authors
+//
+// 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.
+
+syntax = "proto3";
+
+package istio.mixer.v1.config.client;
+
+option go_package="istio.io/api/mixer/v1/config/client";
+
+import "gogoproto/gogo.proto";
+
+import "mixer/v1/attributes.proto";
+import "mixer/v1/config/client/service.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.equal_all) = false;
+option (gogoproto.gostring_all) = false;
+option (gogoproto.stable_marshaler_all) = true;
+
+// HTTPAPISpec defines the canonical configuration for generating
+// API-related attributes from HTTP requests based on the method and
+// uri templated path matches. It is sufficient for defining the API
+// surface of a service for the purposes of API attribute
+// generation. It is not intended to represent auth, quota,
+// documentation, or other information commonly found in other API
+// specifications, e.g. OpenAPI.
+//
+// Existing standards that define operations (or methods) in terms of
+// HTTP methods and paths can be normalized to this format for use in
+// Istio. For example, a simple petstore API described by OpenAPIv2
+// [here](https://github.com/googleapis/gnostic/blob/master/examples/v2.0/yaml/petstore-simple.yaml)
+// can be represented with the following HTTPAPISpec.
+//
+// ```yaml
+// apiVersion: config.istio.io/v1alpha2
+// kind: HTTPAPISpec
+// metadata:
+// name: petstore
+// namespace: default
+// spec:
+// attributes:
+// attributes:
+// api.service:
+// stringValue: petstore.swagger.io
+// api.version:
+// stringValue: 1.0.0
+// patterns:
+// - attributes:
+// attributes:
+// api.operation:
+// stringValue: findPets
+// httpMethod: GET
+// uriTemplate: /api/pets
+// - attributes:
+// attributes:
+// api.operation:
+// stringValue: addPet
+// httpMethod: POST
+// uriTemplate: /api/pets
+// - attributes:
+// attributes:
+// api.operation:
+// stringValue: findPetById
+// httpMethod: GET
+// uriTemplate: /api/pets/{id}
+// - attributes:
+// attributes:
+// api.operation:
+// stringValue: deletePet
+// httpMethod: DELETE
+// uriTemplate: /api/pets/{id}
+// api_keys:
+// - query: api-key
+// ```
+message HTTPAPISpec {
+ // List of attributes that are generated when *any* of the HTTP
+ // patterns match. This list typically includes the "api.service"
+ // and "api.version" attributes.
+ Attributes attributes = 1;
+
+ // List of HTTP patterns to match.
+ repeated HTTPAPISpecPattern patterns = 2;
+
+ // List of APIKey that describes how to extract an API-KEY from an
+ // HTTP request. The first API-Key match found in the list is used,
+ // i.e. 'OR' semantics.
+ //
+ // The following default policies are used to generate the
+ // `request.api_key` attribute if no explicit APIKey is defined.
+ //
+ // `query: key, `query: api_key`, and then `header: x-api-key`
+ //
+ repeated APIKey api_keys = 3;
+}
+
+// HTTPAPISpecPattern defines a single pattern to match against
+// incoming HTTP requests. The per-pattern list of attributes is
+// generated if both the http_method and uri_template match. In
+// addition, the top-level list of attributes in the HTTPAPISpec is also
+// generated.
+//
+// ```yaml
+// pattern:
+// - attributes
+// api.operation: doFooBar
+// httpMethod: GET
+// uriTemplate: /foo/bar
+// ```
+message HTTPAPISpecPattern {
+ // List of attributes that are generated if the HTTP request matches
+ // the specified http_method and uri_template. This typically
+ // includes the "api.operation" attribute.
+ Attributes attributes = 1;
+
+ // HTTP request method to match against as defined by
+ // [rfc7231](https://tools.ietf.org/html/rfc7231#page-21). For
+ // example: GET, HEAD, POST, PUT, DELETE.
+ string http_method = 2;
+
+ oneof pattern {
+ // URI template to match against as defined by
+ // [rfc6570](https://tools.ietf.org/html/rfc6570). For example, the
+ // following are valid URI templates:
+ //
+ // /pets
+ // /pets/{id}
+ // /dictionary/{term:1}/{term}
+ // /search{?q*,lang}
+ //
+ string uri_template = 3;
+
+ // EXPERIMENTAL:
+ //
+ // ecmascript style regex-based match as defined by
+ // [EDCA-262](http://en.cppreference.com/w/cpp/regex/ecmascript). For
+ // example,
+ //
+ // "^/pets/(.*?)?"
+ //
+ string regex = 4;
+ }
+}
+
+// APIKey defines the explicit configuration for generating the
+// `request.api_key` attribute from HTTP requests.
+//
+// See [API Keys](https://swagger.io/docs/specification/authentication/api-keys)
+// for a general overview of API keys as defined by OpenAPI.
+message APIKey {
+ oneof key {
+ // API Key is sent as a query parameter. `query` represents the
+ // query string parameter name.
+ //
+ // For example, `query=api_key` should be used with the
+ // following request:
+ //
+ // GET /something?api_key=abcdef12345
+ //
+ string query = 1;
+
+ // API key is sent in a request header. `header` represents the
+ // header name.
+ //
+ // For example, `header=X-API-KEY` should be used with the
+ // following request:
+ //
+ // GET /something HTTP/1.1
+ // X-API-Key: abcdef12345
+ //
+ string header = 2;
+
+ // API key is sent in a
+ // [cookie](https://swagger.io/docs/specification/authentication/cookie-authentication),
+ //
+ // For example, `cookie=X-API-KEY` should be used for the
+ // following request:
+ //
+ // GET /something HTTP/1.1
+ // Cookie: X-API-KEY=abcdef12345
+ //
+ string cookie = 3;
+ }
+}
+
+// HTTPAPISpecReference defines a reference to an HTTPAPISpec. This is
+// typically used for establishing bindings between an HTTPAPISpec and an
+// IstioService. For example, the following defines an
+// HTTPAPISpecReference for service `foo` in namespace `bar`.
+//
+// ```yaml
+// - name: foo
+// namespace: bar
+// ```
+message HTTPAPISpecReference {
+ // REQUIRED. The short name of the HTTPAPISpec. This is the resource
+ // name defined by the metadata name field.
+ string name = 1;
+
+ // Optional namespace of the HTTPAPISpec. Defaults to the encompassing
+ // HTTPAPISpecBinding's metadata namespace field.
+ string namespace = 2;
+}
+
+// HTTPAPISpecBinding defines the binding between HTTPAPISpecs and one or more
+// IstioService. For example, the following establishes a binding
+// between the HTTPAPISpec `petstore` and service `foo` in namespace `bar`.
+//
+// ```yaml
+// apiVersion: config.istio.io/v1alpha2
+// kind: HTTPAPISpecBinding
+// metadata:
+// name: my-binding
+// namespace: default
+// spec:
+// services:
+// - name: foo
+// namespace: bar
+// api_specs:
+// - name: petstore
+// namespace: default
+// ```
+message HTTPAPISpecBinding {
+ // REQUIRED. One or more services to map the listed HTTPAPISpec onto.
+ repeated IstioService services = 1;
+
+ // REQUIRED. One or more HTTPAPISpec references that should be mapped to
+ // the specified service(s). The aggregate collection of match
+ // conditions defined in the HTTPAPISpecs should not overlap.
+ repeated HTTPAPISpecReference api_specs = 2;
+}
diff --git a/internal/protobuf/testdata/mixer/v1/config/client/client_config.proto b/internal/protobuf/testdata/mixer/v1/config/client/client_config.proto
new file mode 100644
index 0000000..a229699
--- /dev/null
+++ b/internal/protobuf/testdata/mixer/v1/config/client/client_config.proto
@@ -0,0 +1,198 @@
+// Copyright 2017 Istio Authors
+//
+// 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.
+
+syntax = "proto3";
+
+import "gogoproto/gogo.proto";
+import "google/protobuf/duration.proto";
+
+import "mixer/v1/attributes.proto";
+import "mixer/v1/config/client/api_spec.proto";
+import "mixer/v1/config/client/quota.proto";
+
+// $title: Mixer Client
+// $description: Configuration state for the Mixer client library.
+// $location: https://istio.io/docs/reference/config/policy-and-telemetry/istio.mixer.v1.config.client
+
+// Describes the configuration state for the Mixer client library that's built into Envoy.
+package istio.mixer.v1.config.client;
+
+option go_package = "istio.io/api/mixer/v1/config/client";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.equal_all) = false;
+option (gogoproto.gostring_all) = false;
+option (gogoproto.stable_marshaler_all) = true;
+
+// Specifies the behavior when the client is unable to connect to Mixer.
+message NetworkFailPolicy {
+ // Describes the policy.
+ enum FailPolicy {
+ // If network connection fails, request is allowed and delivered to the
+ // service.
+ FAIL_OPEN = 0;
+
+ // If network connection fails, request is rejected.
+ FAIL_CLOSE = 1;
+ }
+
+ // Specifies the behavior when the client is unable to connect to Mixer.
+ FailPolicy policy = 1;
+
+ // Max retries on transport error.
+ uint32 max_retry = 2;
+
+ // Base time to wait between retries. Will be adjusted by exponential
+ // backoff and jitter.
+ google.protobuf.Duration base_retry_wait = 3;
+
+ // Max time to wait between retries.
+ google.protobuf.Duration max_retry_wait = 4;
+}
+
+// Defines the per-service client configuration.
+message ServiceConfig {
+ // If true, do not call Mixer Check.
+ bool disable_check_calls = 1;
+
+ // If true, do not call Mixer Report.
+ bool disable_report_calls = 2;
+
+ // Send these attributes to Mixer in both Check and Report. This
+ // typically includes the "destination.service" attribute.
+ // In case of a per-route override, per-route attributes take precedence
+ // over the attributes supplied in the client configuration.
+ Attributes mixer_attributes = 3;
+
+ // HTTP API specifications to generate API attributes.
+ repeated HTTPAPISpec http_api_spec = 4;
+
+ // Quota specifications to generate quota requirements.
+ repeated QuotaSpec quota_spec = 5;
+
+ // Specifies the behavior when the client is unable to connect to Mixer.
+ // This is the service-level policy. It overrides
+ // [mesh-level
+ // policy][istio.mixer.v1.config.client.TransportConfig.network_fail_policy].
+ NetworkFailPolicy network_fail_policy = 7;
+
+ // Default attributes to forward to upstream. This typically
+ // includes the "source.ip" and "source.uid" attributes.
+ // In case of a per-route override, per-route attributes take precedence
+ // over the attributes supplied in the client configuration.
+ //
+ // Forwarded attributes take precedence over the static Mixer attributes.
+ // The full order of application is as follows:
+ // 1. static Mixer attributes from the filter config;
+ // 2. static Mixer attributes from the route config;
+ // 3. forwarded attributes from the source filter config (if any);
+ // 4. forwarded attributes from the source route config (if any);
+ // 5. derived attributes from the request metadata.
+ Attributes forward_attributes = 8;
+}
+
+// Defines the transport config on how to call Mixer.
+message TransportConfig {
+ // The flag to disable check cache.
+ bool disable_check_cache = 1;
+
+ // The flag to disable quota cache.
+ bool disable_quota_cache = 2;
+
+ // The flag to disable report batch.
+ bool disable_report_batch = 3;
+
+ // Specifies the behavior when the client is unable to connect to Mixer.
+ // This is the mesh level policy. The default value for policy is FAIL_OPEN.
+ NetworkFailPolicy network_fail_policy = 4;
+
+ // Specify refresh interval to write Mixer client statistics to Envoy share
+ // memory. If not specified, the interval is 10 seconds.
+ google.protobuf.Duration stats_update_interval = 5;
+
+ // Name of the cluster that will forward check calls to a pool of mixer
+ // servers. Defaults to "mixer_server". By using different names for
+ // checkCluster and reportCluster, it is possible to have one set of
+ // Mixer servers handle check calls, while another set of Mixer servers
+ // handle report calls.
+ //
+ // NOTE: Any value other than the default "mixer_server" will require the
+ // Istio Grafana dashboards to be reconfigured to use the new name.
+ string check_cluster = 6;
+
+ // Name of the cluster that will forward report calls to a pool of mixer
+ // servers. Defaults to "mixer_server". By using different names for
+ // checkCluster and reportCluster, it is possible to have one set of
+ // Mixer servers handle check calls, while another set of Mixer servers
+ // handle report calls.
+ //
+ // NOTE: Any value other than the default "mixer_server" will require the
+ // Istio Grafana dashboards to be reconfigured to use the new name.
+ string report_cluster = 7;
+
+ // Default attributes to forward to Mixer upstream. This typically
+ // includes the "source.ip" and "source.uid" attributes. These
+ // attributes are consumed by the proxy in front of mixer.
+ Attributes attributes_for_mixer_proxy = 8;
+}
+
+// Defines the client config for HTTP.
+message HttpClientConfig {
+ // The transport config.
+ TransportConfig transport = 1;
+
+ // Map of control configuration indexed by destination.service. This
+ // is used to support per-service configuration for cases where a
+ // mixerclient serves multiple services.
+ map<string, ServiceConfig> service_configs = 2;
+
+ // Default destination service name if none was specified in the
+ // client request.
+ string default_destination_service = 3;
+
+ // Default attributes to send to Mixer in both Check and
+ // Report. This typically includes "destination.ip" and
+ // "destination.uid" attributes.
+ Attributes mixer_attributes = 4;
+
+ // Default attributes to forward to upstream. This typically
+ // includes the "source.ip" and "source.uid" attributes.
+ Attributes forward_attributes = 5;
+}
+
+// Defines the client config for TCP.
+message TcpClientConfig {
+ // The transport config.
+ TransportConfig transport = 1;
+
+ // Default attributes to send to Mixer in both Check and
+ // Report. This typically includes "destination.ip" and
+ // "destination.uid" attributes.
+ Attributes mixer_attributes = 2;
+
+ // If set to true, disables Mixer check calls.
+ bool disable_check_calls = 3;
+
+ // If set to true, disables Mixer check calls.
+ bool disable_report_calls = 4;
+
+ // Quota specifications to generate quota requirements.
+ // It applies on the new TCP connections.
+ QuotaSpec connection_quota_spec = 5;
+
+ // Specify report interval to send periodical reports for long TCP
+ // connections. If not specified, the interval is 10 seconds. This interval
+ // should not be less than 1 second, otherwise it will be reset to 1 second.
+ google.protobuf.Duration report_interval = 6;
+}
diff --git a/internal/protobuf/testdata/mixer/v1/config/client/quota.proto b/internal/protobuf/testdata/mixer/v1/config/client/quota.proto
new file mode 100644
index 0000000..5ea594c
--- /dev/null
+++ b/internal/protobuf/testdata/mixer/v1/config/client/quota.proto
@@ -0,0 +1,145 @@
+// Copyright 2017 Istio Authors
+//
+// 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.
+
+syntax = "proto3";
+
+package istio.mixer.v1.config.client;
+
+option go_package="istio.io/api/mixer/v1/config/client";
+
+import "gogoproto/gogo.proto";
+import "mixer/v1/config/client/service.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.equal_all) = false;
+option (gogoproto.gostring_all) = false;
+option (gogoproto.stable_marshaler_all) = true;
+
+// Specifies runtime quota rules.
+// * Uses Istio attributes to match individual requests
+// * Specifies list of quotas to use for matched requests.
+//
+// Example1:
+// Charge "request_count" quota with 1 amount for all requests.
+//
+// QuotaSpec:
+// - rules
+// - quotas:
+// quota: request_count
+// charge: 1
+//
+// Example2:
+// For HTTP POST requests with path are prefixed with /books or
+// api.operation is create_books, charge two quotas:
+// * write_count of 1 amount
+// * request_count of 5 amount.
+//
+// ```yaml
+// QuotaSpec:
+// - rules:
+// - match:
+// clause:
+// request.path:
+// string_prefix: /books
+// request.http_method:
+// string_exact: POST
+// - match:
+// clause:
+// api.operation:
+// string_exact: create_books
+// - quotas:
+// quota: write_count
+// charge: 1
+// - quotas:
+// quota: request_count
+// charge: 5
+// ```
+
+// Determines the quotas used for individual requests.
+message QuotaSpec {
+ // A list of Quota rules.
+ repeated QuotaRule rules = 1;
+}
+
+// Specifies a rule with list of matches and list of quotas.
+// If any clause matched, the list of quotas will be used.
+message QuotaRule {
+ // If empty, match all request.
+ // If any of match is true, it is matched.
+ repeated AttributeMatch match = 1;
+
+ // The list of quotas to charge.
+ repeated Quota quotas = 2;
+}
+
+// Describes how to match a given string in HTTP headers. Match is
+// case-sensitive.
+message StringMatch {
+ oneof match_type {
+ // exact string match
+ string exact = 1;
+ // prefix-based match
+ string prefix = 2;
+ // ECMAscript style regex-based match
+ string regex = 3;
+ }
+}
+
+// Specifies a match clause to match Istio attributes
+message AttributeMatch {
+ // Map of attribute names to StringMatch type.
+ // Each map element specifies one condition to match.
+ //
+ // Example:
+ //
+ // clause:
+ // source.uid:
+ // exact: SOURCE_UID
+ // request.http_method:
+ // exact: POST
+ map<string, StringMatch> clause = 1;
+}
+
+// Specifies a quota to use with quota name and amount.
+message Quota {
+ // The quota name to charge
+ string quota = 1;
+
+ // The quota amount to charge
+ int64 charge = 2;
+}
+
+// QuotaSpecBinding defines the binding between QuotaSpecs and one or more
+// IstioService.
+message QuotaSpecBinding {
+ // REQUIRED. One or more services to map the listed QuotaSpec onto.
+ repeated IstioService services = 1;
+
+ // QuotaSpecReference uniquely identifies the QuotaSpec used in the
+ // Binding.
+ message QuotaSpecReference {
+ // REQUIRED. The short name of the QuotaSpec. This is the resource
+ // name defined by the metadata name field.
+ string name = 1;
+
+ // Optional namespace of the QuotaSpec. Defaults to the value of the
+ // metadata namespace field.
+ string namespace = 2;
+ }
+
+ // REQUIRED. One or more QuotaSpec references that should be mapped to
+ // the specified service(s). The aggregate collection of match
+ // conditions defined in the QuotaSpecs should not overlap.
+ repeated QuotaSpecReference quota_specs = 2;
+}
diff --git a/internal/protobuf/testdata/mixer/v1/config/client/service.proto b/internal/protobuf/testdata/mixer/v1/config/client/service.proto
new file mode 100644
index 0000000..01de2e9
--- /dev/null
+++ b/internal/protobuf/testdata/mixer/v1/config/client/service.proto
@@ -0,0 +1,58 @@
+// Copyright 2017 Istio Authors
+//
+// 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.
+
+syntax = "proto3";
+
+package istio.mixer.v1.config.client;
+
+option go_package="istio.io/api/mixer/v1/config/client";
+
+import "gogoproto/gogo.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.equal_all) = false;
+option (gogoproto.gostring_all) = false;
+option (gogoproto.stable_marshaler_all) = true;
+
+// NOTE: this is a duplicate of proxy.v1.config.IstioService from
+// proxy/v1alpha1/config/route_rules.proto.
+//
+// Mixer protobufs have gogoproto specific options which are not
+// compatiable with the proxy's vanilla protobufs. Ideally, these
+// protobuf options be reconciled so fundamental Istio concepts and
+// types can be shared by components. Until then, make a copy of
+// IstioService for mixerclient to use.
+
+// IstioService identifies a service and optionally service version.
+// The FQDN of the service is composed from the name, namespace, and implementation-specific domain suffix
+// (e.g. on Kubernetes, "reviews" + "default" + "svc.cluster.local" -> "reviews.default.svc.cluster.local").
+message IstioService {
+ // The short name of the service such as "foo".
+ string name = 1;
+
+ // Optional namespace of the service. Defaults to value of metadata namespace field.
+ string namespace = 2;
+
+ // Domain suffix used to construct the service FQDN in implementations that support such specification.
+ string domain = 3;
+
+ // The service FQDN.
+ string service = 4;
+
+ // Optional one or more labels that uniquely identify the service version.
+ //
+ // *Note:* When used for a VirtualService destination, labels MUST be empty.
+ //
+ map<string, string> labels = 5;
+}
diff --git a/internal/protobuf/testdata/mixer/v1/mixer.proto b/internal/protobuf/testdata/mixer/v1/mixer.proto
new file mode 100644
index 0000000..04ace67
--- /dev/null
+++ b/internal/protobuf/testdata/mixer/v1/mixer.proto
@@ -0,0 +1,265 @@
+// Copyright 2016 Istio Authors
+//
+// 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.
+
+syntax = "proto3";
+
+// This package defines the Mixer API that the sidecar proxy uses to perform
+// precondition checks, manage quotas, and report telemetry.
+package istio.mixer.v1;
+
+option go_package = "istio.io/api/mixer/v1";
+option cc_generic_services = true;
+
+import "gogoproto/gogo.proto";
+import "google/protobuf/duration.proto";
+import "google/rpc/status.proto";
+import "mixer/v1/attributes.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.equal_all) = false;
+option (gogoproto.gostring_all) = false;
+option cc_enable_arenas = true;
+
+// Mixer provides three core features:
+//
+// - *Precondition Checking*. Enables callers to verify a number of preconditions
+// before responding to an incoming request from a service consumer.
+// Preconditions can include whether the service consumer is properly
+// authenticated, is on the service’s whitelist, passes ACL checks, and more.
+//
+// - *Quota Management*. Enables services to allocate and free quota on a number
+// of dimensions, Quotas are used as a relatively simple resource management tool
+// to provide some fairness between service consumers when contending for limited
+// resources. Rate limits are examples of quotas.
+//
+// - *Telemetry Reporting*. Enables services to report logging and monitoring.
+// In the future, it will also enable tracing and billing streams intended for
+// both the service operator as well as for service consumers.
+service Mixer {
+ // Checks preconditions and allocate quota before performing an operation.
+ // The preconditions enforced depend on the set of supplied attributes and
+ // the active configuration.
+ rpc Check(CheckRequest) returns (CheckResponse) {}
+
+ // Reports telemetry, such as logs and metrics.
+ // The reported information depends on the set of supplied attributes and the
+ // active configuration.
+ rpc Report(ReportRequest) returns (ReportResponse) {}
+}
+
+// Used to get a thumbs-up/thumbs-down before performing an action.
+message CheckRequest {
+ // parameters for a quota allocation
+ message QuotaParams {
+ // Amount of quota to allocate
+ int64 amount = 1;
+
+ // When true, supports returning less quota than what was requested.
+ bool best_effort = 2;
+ }
+
+ // The attributes to use for this request.
+ //
+ // Mixer's configuration determines how these attributes are used to
+ // establish the result returned in the response.
+ CompressedAttributes attributes = 1 [(gogoproto.nullable) = false];
+
+ // The number of words in the global dictionary, used with to populate the attributes.
+ // This value is used as a quick way to determine whether the client is using a dictionary that
+ // the server understands.
+ uint32 global_word_count = 2;
+
+ // Used for deduplicating `Check` calls in the case of failed RPCs and retries. This should be a UUID
+ // per call, where the same UUID is used for retries of the same call.
+ string deduplication_id = 3;
+
+ // The individual quotas to allocate
+ map<string, QuotaParams> quotas = 4 [(gogoproto.nullable) = false];
+}
+
+// The response generated by the Check method.
+message CheckResponse {
+
+ // Expresses the result of a precondition check.
+ message PreconditionResult {
+ reserved 4;
+
+ // A status code of OK indicates all preconditions were satisfied. Any other code indicates not
+ // all preconditions were satisfied and details describe why.
+ google.rpc.Status status = 1 [(gogoproto.nullable) = false];
+
+ // The amount of time for which this result can be considered valid.
+ google.protobuf.Duration valid_duration = 2 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
+
+ // The number of uses for which this result can be considered valid.
+ int32 valid_use_count = 3;
+
+ // The total set of attributes that were used in producing the result
+ // along with matching conditions.
+ ReferencedAttributes referenced_attributes = 5;
+
+ // An optional routing directive, used to manipulate the traffic metadata
+ // whenever all preconditions are satisfied.
+ RouteDirective route_directive = 6;
+ }
+
+ // Expresses the result of a quota allocation.
+ message QuotaResult {
+ // The amount of time for which this result can be considered valid.
+ google.protobuf.Duration valid_duration = 1 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
+
+ // The amount of granted quota. When `QuotaParams.best_effort` is true, this will be >= 0.
+ // If `QuotaParams.best_effort` is false, this will be either 0 or >= `QuotaParams.amount`.
+ int64 granted_amount = 2;
+
+ // The total set of attributes that were used in producing the result
+ // along with matching conditions.
+ ReferencedAttributes referenced_attributes = 5 [(gogoproto.nullable) = false];
+ }
+
+ // The precondition check results.
+ PreconditionResult precondition = 2 [(gogoproto.nullable) = false];
+
+ // The resulting quota, one entry per requested quota.
+ map<string, QuotaResult> quotas = 3 [(gogoproto.nullable) = false];
+}
+
+// Describes the attributes that were used to determine the response.
+// This can be used to construct a response cache.
+message ReferencedAttributes {
+ // How an attribute's value was matched
+ enum Condition {
+ CONDITION_UNSPECIFIED = 0; // should not occur
+ ABSENCE = 1; // match when attribute doesn't exist
+ EXACT = 2; // match when attribute value is an exact byte-for-byte match
+ REGEX = 3; // match when attribute value matches the included regex
+ }
+
+ // Describes a single attribute match.
+ message AttributeMatch {
+ // The name of the attribute. This is a dictionary index encoded in a manner identical
+ // to all strings in the [CompressedAttributes][istio.mixer.v1.CompressedAttributes] message.
+ sint32 name = 1;
+
+ // The kind of match against the attribute value.
+ Condition condition = 2;
+
+ // If a REGEX condition is provided for a STRING_MAP attribute,
+ // clients should use the regex value to match against map keys.
+ string regex = 3;
+
+ // A key in a STRING_MAP. When multiple keys from a STRING_MAP
+ // attribute were referenced, there will be multiple AttributeMatch
+ // messages with different map_key values. Values for map_key SHOULD
+ // be ignored for attributes that are not STRING_MAP.
+ //
+ // Indices for the keys are used (taken either from the
+ // message dictionary from the `words` field or the global dictionary).
+ //
+ // If no map_key value is provided for a STRING_MAP attribute, the
+ // entire STRING_MAP will be used.
+ sint32 map_key = 4;
+ }
+
+ // The message-level dictionary. Refer to [CompressedAttributes][istio.mixer.v1.CompressedAttributes] for information
+ // on using dictionaries.
+ repeated string words = 1;
+
+ // Describes a set of attributes.
+ repeated AttributeMatch attribute_matches = 2 [(gogoproto.nullable) = false];
+}
+
+// Operation on HTTP headers to replace, append, or remove a header. Header
+// names are normalized to lower-case with dashes, e.g. "x-request-id".
+// Pseudo-headers ":path", ":authority", and ":method" are supported to modify
+// the request headers.
+message HeaderOperation {
+ // Operation type.
+ enum Operation {
+ REPLACE = 0; // replaces the header with the given name
+ REMOVE = 1; // removes the header with the given name (the value is ignored)
+ APPEND = 2; // appends the value to the header value, or sets it if not present
+ }
+ // Header name.
+ string name = 1;
+
+ // Header value.
+ string value = 2;
+
+ // Header operation.
+ Operation operation = 3;
+}
+
+// Expresses the routing manipulation actions to be performed on behalf of
+// Mixer in response to a precondition check.
+message RouteDirective {
+ // Operations on the request headers.
+ repeated HeaderOperation request_header_operations = 1 [(gogoproto.nullable) = false];
+
+ // Operations on the response headers.
+ repeated HeaderOperation response_header_operations = 2 [(gogoproto.nullable) = false];
+
+ // If set, enables a direct response without proxying the request to the routing
+ // destination. Required to be a value in the 2xx or 3xx range.
+ uint32 direct_response_code = 3;
+
+ // Supplies the response body for the direct response.
+ // If this setting is omitted, no body is included in the generated response.
+ string direct_response_body = 4;
+}
+
+// Used to report telemetry after performing one or more actions.
+message ReportRequest {
+ // next value: 5
+
+ // Used to signal how the sets of compressed attributes should be reconstitued server-side.
+ enum RepeatedAttributesSemantics {
+ // Use delta encoding between sets of compressed attributes to reduce the overall on-wire
+ // request size. Each individual set of attributes is used to modify the previous set.
+ // NOTE: There is no way with this encoding to specify attribute value deletion. This
+ // option should be used with extreme caution.
+ DELTA_ENCODING = 0;
+
+ // Treat each set of compressed attributes as complete - independent from other sets
+ // in this request. This will result in on-wire duplication of attributes and values, but
+ // will allow for proper accounting of absent values in overall encoding.
+ INDEPENDENT_ENCODING = 1;
+ }
+
+ // The attributes to use for this request.
+ //
+ // Each `Attributes` element represents the state of a single action. Multiple actions
+ // can be provided in a single message in order to improve communication efficiency. The
+ // client can accumulate a set of actions and send them all in one single message.
+ repeated CompressedAttributes attributes = 1 [(gogoproto.nullable) = false];
+
+ // Indicates how to decode the attributes sets in this request.
+ RepeatedAttributesSemantics repeated_attributes_semantics = 4;
+
+ // The default message-level dictionary for all the attributes.
+ // Individual attribute messages can have their own dictionaries, but if they don't
+ // then this set of words, if it is provided, is used instead.
+ //
+ // This makes it possible to share the same dictionary for all attributes in this
+ // request, which can substantially reduce the overall request size.
+ repeated string default_words = 2;
+
+ // The number of words in the global dictionary.
+ // To detect global dictionary out of sync between client and server.
+ uint32 global_word_count = 3;
+}
+
+// Used to carry responses to telemetry reports
+message ReportResponse {
+}
diff --git a/internal/protobuf/testdata/networking/v1alpha3/gateway.proto b/internal/protobuf/testdata/networking/v1alpha3/gateway.proto
new file mode 100644
index 0000000..e06fe5d
--- /dev/null
+++ b/internal/protobuf/testdata/networking/v1alpha3/gateway.proto
@@ -0,0 +1,448 @@
+// Copyright 2019 CUE Authors
+//
+// 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.
+
+// Copyright 2017 Istio Authors
+//
+// 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.
+
+
+syntax = "proto3";
+
+// $title: Gateway
+// $description: Configuration affecting edge load balancer.
+// $location: https://istio.io/docs/reference/config/networking/v1alpha3/gateway.html
+
+// `Gateway` describes a load balancer operating at the edge of the mesh
+// receiving incoming or outgoing HTTP/TCP connections. The specification
+// describes a set of ports that should be exposed, the type of protocol to
+// use, SNI configuration for the load balancer, etc.
+//
+// For example, the following Gateway configuration sets up a proxy to act
+// as a load balancer exposing port 80 and 9080 (http), 443 (https),
+// 9443(https) and port 2379 (TCP) for ingress. The gateway will be
+// applied to the proxy running on a pod with labels `app:
+// my-gateway-controller`. While Istio will configure the proxy to listen
+// on these ports, it is the responsibility of the user to ensure that
+// external traffic to these ports are allowed into the mesh.
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-gateway
+// namespace: some-config-namespace
+// spec:
+// selector:
+// app: my-gateway-controller
+// servers:
+// - port:
+// number: 80
+// name: http
+// protocol: HTTP
+// hosts:
+// - uk.bookinfo.com
+// - eu.bookinfo.com
+// tls:
+// httpsRedirect: true # sends 301 redirect for http requests
+// - port:
+// number: 443
+// name: https-443
+// protocol: HTTPS
+// hosts:
+// - uk.bookinfo.com
+// - eu.bookinfo.com
+// tls:
+// mode: SIMPLE # enables HTTPS on this port
+// serverCertificate: /etc/certs/servercert.pem
+// privateKey: /etc/certs/privatekey.pem
+// - port:
+// number: 9443
+// name: https-9443
+// protocol: HTTPS
+// hosts:
+// - "bookinfo-namespace/*.bookinfo.com"
+// tls:
+// mode: SIMPLE # enables HTTPS on this port
+// credentialName: bookinfo-secret # fetches certs from Kubernetes secret
+// - port:
+// number: 9080
+// name: http-wildcard
+// protocol: HTTP
+// hosts:
+// - "*"
+// - port:
+// number: 2379 # to expose internal service via external port 2379
+// name: mongo
+// protocol: MONGO
+// hosts:
+// - "*"
+// ```
+//
+// The Gateway specification above describes the L4-L6 properties of a load
+// balancer. A `VirtualService` can then be bound to a gateway to control
+// the forwarding of traffic arriving at a particular host or gateway port.
+//
+// For example, the following VirtualService splits traffic for
+// `https://uk.bookinfo.com/reviews`, `https://eu.bookinfo.com/reviews`,
+// `http://uk.bookinfo.com:9080/reviews`,
+// `http://eu.bookinfo.com:9080/reviews` into two versions (prod and qa) of
+// an internal reviews service on port 9080. In addition, requests
+// containing the cookie "user: dev-123" will be sent to special port 7777
+// in the qa version. The same rule is also applicable inside the mesh for
+// requests to the "reviews.prod.svc.cluster.local" service. This rule is
+// applicable across ports 443, 9080. Note that `http://uk.bookinfo.com`
+// gets redirected to `https://uk.bookinfo.com` (i.e. 80 redirects to 443).
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: VirtualService
+// metadata:
+// name: bookinfo-rule
+// namespace: bookinfo-namespace
+// spec:
+// hosts:
+// - reviews.prod.svc.cluster.local
+// - uk.bookinfo.com
+// - eu.bookinfo.com
+// gateways:
+// - some-config-namespace/my-gateway
+// - mesh # applies to all the sidecars in the mesh
+// http:
+// - match:
+// - headers:
+// cookie:
+// exact: "user=dev-123"
+// route:
+// - destination:
+// port:
+// number: 7777
+// host: reviews.qa.svc.cluster.local
+// - match:
+// - uri:
+// prefix: /reviews/
+// route:
+// - destination:
+// port:
+// number: 9080 # can be omitted if it's the only port for reviews
+// host: reviews.prod.svc.cluster.local
+// weight: 80
+// - destination:
+// host: reviews.qa.svc.cluster.local
+// weight: 20
+// ```
+//
+// The following VirtualService forwards traffic arriving at (external)
+// port 27017 to internal Mongo server on port 5555. This rule is not
+// applicable internally in the mesh as the gateway list omits the
+// reserved name `mesh`.
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: VirtualService
+// metadata:
+// name: bookinfo-Mongo
+// namespace: bookinfo-namespace
+// spec:
+// hosts:
+// - mongosvr.prod.svc.cluster.local # name of internal Mongo service
+// gateways:
+// - some-config-namespace/my-gateway # can omit the namespace if gateway is in same
+// namespace as virtual service.
+// tcp:
+// - match:
+// - port: 27017
+// route:
+// - destination:
+// host: mongo.prod.svc.cluster.local
+// port:
+// number: 5555
+// ```
+//
+// It is possible to restrict the set of virtual services that can bind to
+// a gateway server using the namespace/hostname syntax in the hosts field.
+// For example, the following Gateway allows any virtual service in the ns1
+// namespace to bind to it, while restricting only the virtual service with
+// foo.bar.com host in the ns2 namespace to bind to it.
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-gateway
+// namespace: some-config-namespace
+// spec:
+// selector:
+// app: my-gateway-controller
+// servers:
+// - port:
+// number: 80
+// name: http
+// protocol: HTTP
+// hosts:
+// - "ns1/*"
+// - "ns2/foo.bar.com"
+// ```
+//
+package istio.networking.v1alpha3;
+
+import "cuelang/cue.proto";
+
+// GO PACKAGE
+option go_package = "istio.io/api/networking/v1alpha3"; // INLINE
+
+message Gateway {
+ // REQUIRED: A list of server specifications.
+ repeated Server servers = 1;
+
+ // REQUIRED: One or more labels that indicate a specific set of pods/VMs
+ // on which this gateway configuration should be applied. The scope of
+ // label search is restricted to the configuration namespace in which the
+ // the resource is present. In other words, the Gateway resource must
+ // reside in the same namespace as the gateway workload instance.
+ map<string, string> selector = 2 [ (cue.val) = "{<name>: name}"];
+}
+
+// `Server` describes the properties of the proxy on a given load balancer
+// port. For example,
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-ingress
+// spec:
+// selector:
+// app: my-ingress-gateway
+// servers:
+// - port:
+// number: 80
+// name: http2
+// protocol: HTTP2
+// hosts:
+// - "*"
+// ```
+//
+// Another example
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-tcp-ingress
+// spec:
+// selector:
+// app: my-tcp-ingress-gateway
+// servers:
+// - port:
+// number: 27018
+// name: mongo
+// protocol: MONGO
+// hosts:
+// - "*"
+// ```
+//
+// The following is an example of TLS configuration for port 443
+//
+// ```yaml
+// apiVersion: networking.istio.io/v1alpha3
+// kind: Gateway
+// metadata:
+// name: my-tls-ingress
+// spec:
+// selector:
+// app: my-tls-ingress-gateway
+// servers:
+// - port:
+// number: 443
+// name: https
+// protocol: HTTPS
+// hosts:
+// - "*"
+// tls:
+// mode: SIMPLE
+// serverCertificate: /etc/certs/server.pem
+// privateKey: /etc/certs/privatekey.pem
+// ```
+message Server {
+ // REQUIRED: The Port on which the proxy should listen for incoming
+ // connections.
+ Port port = 1 [(cue.val) = ">10 & <100"];
+
+ // $hide_from_docs
+ // The ip or the Unix domain socket to which the listener should be bound
+ // to. Format: `x.x.x.x` or `unix:///path/to/uds` or `unix://@foobar`
+ // (Linux abstract namespace). When using Unix domain sockets, the port
+ // number should be 0.
+ string bind = 4;
+
+ // REQUIRED. One or more hosts exposed by this gateway.
+ // While typically applicable to
+ // HTTP services, it can also be used for TCP services using TLS with SNI.
+ // A host is specified as a `dnsName` with an optional `namespace/` prefix.
+ // The `dnsName` should be specified using FQDN format, optionally including
+ // a wildcard character in the left-most component (e.g., `prod/*.example.com`).
+ // Set the `dnsName` to `*` to select all `VirtualService` hosts from the
+ // specified namespace (e.g.,`prod/*`). If no `namespace/` is specified,
+ // the `VirtualService` hosts will be selected from any available namespace.
+ // Any associated `DestinationRule` in the same namespace will also be used.
+ //
+ // A `VirtualService` must be bound to the gateway and must have one or
+ // more hosts that match the hosts specified in a server. The match
+ // could be an exact match or a suffix match with the server's hosts. For
+ // example, if the server's hosts specifies `*.example.com`, a
+ // `VirtualService` with hosts `dev.example.com` or `prod.example.com` will
+ // match. However, a `VirtualService` with host `example.com` or
+ // `newexample.com` will not match.
+ //
+ // NOTE: Only virtual services exported to the gateway's namespace
+ // (e.g., `exportTo` value of `*`) can be referenced.
+ // Private configurations (e.g., `exportTo` set to `.`) will not be
+ // available. Refer to the `exportTo` setting in `VirtualService`,
+ // `DestinationRule`, and `ServiceEntry` configurations for details.
+ repeated string hosts = 2;
+
+ message TLSOptions {
+ // If set to true, the load balancer will send a 301 redirect for all
+ // http connections, asking the clients to use HTTPS.
+ bool https_redirect = 1;
+
+ // TLS modes enforced by the proxy
+ enum TLSmode {
+ // The SNI string presented by the client will be used as the match
+ // criterion in a VirtualService TLS route to determine the
+ // destination service from the service registry.
+ PASSTHROUGH = 0;
+
+ // Secure connections with standard TLS semantics.
+ SIMPLE = 1;
+
+ // Secure connections to the upstream using mutual TLS by presenting
+ // client certificates for authentication.
+ MUTUAL = 2;
+
+ // Similar to the passthrough mode, except servers with this TLS mode
+ // do not require an associated VirtualService to map from the SNI
+ // value to service in the registry. The destination details such as
+ // the service/subset/port are encoded in the SNI value. The proxy
+ // will forward to the upstream (Envoy) cluster (a group of
+ // endpoints) specified by the SNI value. This server is typically
+ // used to provide connectivity between services in disparate L3
+ // networks that otherwise do not have direct connectivity between
+ // their respective endpoints. Use of this mode assumes that both the
+ // source and the destination are using Istio mTLS to secure traffic.
+ AUTO_PASSTHROUGH = 3;
+ };
+
+ // Optional: Indicates whether connections to this port should be
+ // secured using TLS. The value of this field determines how TLS is
+ // enforced.
+ TLSmode mode = 2;
+
+ // Extra comment.
+
+ // REQUIRED if mode is `SIMPLE` or `MUTUAL`. The path to the file
+ // holding the server-side TLS certificate to use.
+ string server_certificate = 3;
+
+ // REQUIRED if mode is `SIMPLE` or `MUTUAL`. The path to the file
+ // holding the server's private key.
+ string private_key = 4;
+
+ // REQUIRED if mode is `MUTUAL`. The path to a file containing
+ // certificate authority certificates to use in verifying a presented
+ // client side certificate.
+ string ca_certificates = 5;
+
+ // The credentialName stands for a unique identifier that can be used
+ // to identify the serverCertificate and the privateKey. The
+ // credentialName appended with suffix "-cacert" is used to identify
+ // the CaCertificates associated with this server. Gateway workloads
+ // capable of fetching credentials from a remote credential store such
+ // as Kubernetes secrets, will be configured to retrieve the
+ // serverCertificate and the privateKey using credentialName, instead
+ // of using the file system paths specified above. If using mutual TLS,
+ // gateway workload instances will retrieve the CaCertificates using
+ // credentialName-cacert. The semantics of the name are platform
+ // dependent. In Kubernetes, the default Istio supplied credential
+ // server expects the credentialName to match the name of the
+ // Kubernetes secret that holds the server certificate, the private
+ // key, and the CA certificate (if using mutual TLS). Set the
+ // `ISTIO_META_USER_SDS` metadata variable in the gateway's proxy to
+ // enable the dynamic credential fetching feature.
+ string credential_name = 10;
+
+ // A list of alternate names to verify the subject identity in the
+ // certificate presented by the client.
+ repeated string subject_alt_names = 6;
+
+ // TLS protocol versions.
+ enum TLSProtocol {
+ TLS_AUTO = 0; // Automatically choose the optimal TLS version.
+
+ TLSV1_0 = 1; // TLS version 1.0
+
+ TLSV1_1 = 2; // TLS version 1.1
+
+ TLSV1_2 = 3; // TLS version 1.2
+
+ TLSV1_3 = 4; // TLS version 1.3
+ }
+
+ // Optional: Minimum TLS protocol version.
+ TLSProtocol min_protocol_version = 7;
+
+ // Optional: Maximum TLS protocol version.
+ TLSProtocol max_protocol_version = 8;
+
+ // Optional: If specified, only support the specified cipher list.
+ // Otherwise default to the default cipher list supported by Envoy.
+ repeated string cipher_suites = 9;
+ }
+
+ // Set of TLS related options that govern the server's behavior. Use
+ // these options to control if all http requests should be redirected to
+ // https, and the TLS modes to use.
+ TLSOptions tls = 3;
+
+ // The loopback IP endpoint or Unix domain socket to which traffic should
+ // be forwarded to by default. Format should be `127.0.0.1:PORT` or
+ // `unix:///path/to/socket` or `unix://@foobar` (Linux abstract namespace).
+ string default_endpoint = 5;
+}
+
+
+// Port describes the properties of a specific port of a service.
+message Port {
+ // REQUIRED: A valid non-negative integer port number.
+ uint32 number = 1;
+
+ // REQUIRED: The protocol exposed on the port.
+ // MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP|TLS.
+ // TLS implies the connection will be routed based on the SNI header to
+ // the destination without terminating the TLS connection.
+ string protocol = 2;
+
+ // Label assigned to the port.
+ string name = 3;
+}
diff --git a/internal/protobuf/types.go b/internal/protobuf/types.go
new file mode 100644
index 0000000..99e5af4
--- /dev/null
+++ b/internal/protobuf/types.go
@@ -0,0 +1,76 @@
+// Copyright 2019 CUE Authors
+//
+// 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 protobuf
+
+import "github.com/emicklei/proto"
+
+func protoToCUE(typ string, options []*proto.Option) (ref string, ok bool) {
+ t, ok := scalars[typ]
+ return t, ok
+}
+
+var scalars = map[string]string{
+ // Differing
+ "sint32": "int32",
+ "sint64": "int64",
+ "fixed32": "uint32",
+ "fixed64": "uint64",
+ "sfixed32": "int32",
+ "sfixed64": "int64",
+
+ // Identical to CUE
+ "int32": "int32",
+ "int64": "int64",
+ "uint32": "uint32",
+ "uint64": "uint64",
+
+ "double": "float64",
+ "float": "float32",
+
+ "bool": "bool",
+ "string": "string",
+ "bytes": "bytes",
+}
+
+var timePkg = &protoConverter{
+ goPkg: "time",
+ goPkgPath: "time",
+}
+
+func (p *protoConverter) setBuiltin(from, to string, pkg *protoConverter) {
+ p.scope[0][from] = mapping{to, pkg}
+}
+
+func (p *protoConverter) mustBuiltinPackage(file string) {
+ // Map some builtin types to their JSON/CUE mappings.
+ switch file {
+ case "gogoproto/gogo.proto":
+
+ case "google/protobuf/duration.proto":
+ p.setBuiltin("google.protobuf.Duration", "time.Duration", timePkg)
+
+ case "google/protobuf/timestamp.proto":
+ p.setBuiltin("google.protobuf.Timestamp", "time.Time", timePkg)
+
+ case "google/protobuf/any.proto":
+ p.setBuiltin("google.protobuf.Any", "_", nil)
+
+ case "google/protobuf/empty.proto":
+ p.setBuiltin("google.protobuf.Empty", "{}", nil)
+
+ default:
+ failf("import %q not found", file)
+ }
+}
diff --git a/internal/protobuf/util.go b/internal/protobuf/util.go
new file mode 100644
index 0000000..e001fa8
--- /dev/null
+++ b/internal/protobuf/util.go
@@ -0,0 +1,89 @@
+// Copyright 2019 CUE Authors
+//
+// 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 protobuf
+
+import (
+ "strings"
+
+ "cuelang.org/go/cue/ast"
+ "cuelang.org/go/cue/token"
+ "github.com/emicklei/proto"
+ "golang.org/x/xerrors"
+)
+
+// failf panics with a marked error that can be intercepted upon returning
+// from parsing.
+func failf(format string, args ...interface{}) {
+ panic(protoError{xerrors.Errorf(format, args...)})
+}
+
+func fail(err error) {
+ panic(protoError{err})
+}
+
+type protoError struct {
+ error
+}
+
+var (
+ newline = token.Pos(token.Newline)
+ newSection = token.Pos(token.NewSection)
+)
+
+func addComments(f ast.Node, i int, doc, inline *proto.Comment) bool {
+ cg := comment(doc, true)
+ if cg != nil && len(cg.List) > 0 && i > 0 {
+ cg.List[0].Slash = newSection
+ }
+ f.AddComment(cg)
+ f.AddComment(comment(inline, false))
+ return doc != nil
+}
+
+func comment(c *proto.Comment, doc bool) *ast.CommentGroup {
+ if c == nil || len(c.Lines) == 0 {
+ return nil
+ }
+ cg := &ast.CommentGroup{}
+ if doc {
+ cg.Doc = true
+ } else {
+ cg.Line = true
+ cg.Position = 10
+ }
+ for _, s := range c.Lines {
+ cg.List = append(cg.List, &ast.Comment{Text: "// " + s})
+ }
+ return cg
+}
+
+func quote(s string) string {
+ if !strings.ContainsAny(s, `"\`) {
+ return s
+ }
+ esc := `\#`
+ for strings.Contains(s, esc) {
+ esc += "#"
+ }
+ return esc[1:] + `"` + s + `"` + esc[1:]
+}
+
+func labelName(s string) string {
+ split := strings.Split(s, "_")
+ for i := 1; i < len(split); i++ {
+ split[i] = strings.Title(split[i])
+ }
+ return strings.Join(split, "")
+}