Skip to content

Commit

Permalink
fix: Fix generating jsonschema (#1836)
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrej-fabry authored Feb 11, 2022
1 parent f907618 commit 23b8b9b
Show file tree
Hide file tree
Showing 15 changed files with 438 additions and 298 deletions.
10 changes: 7 additions & 3 deletions client/dynamic_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,13 @@ func createFileDescRegistry(knownModels []*models.ModelInfo) (protodesc.Resolver
for _, knownModel := range knownModels {
fileDesc := knownModel.MessageDescriptor.ParentFile()
if _, err := reg.FindDescriptorByName(fileDesc.FullName()); err == protoregistry.NotFound {
reg.RegisterFile(fileDesc)
logrus.DefaultLogger().Debugf("Proto file %v was successfully "+
"added to dependency registry.", fileDesc.Path())
if e := reg.RegisterFile(fileDesc); e != nil {
logrus.DefaultLogger().Warnf("Failed to add Proto file %v "+
"to dependency registry: %v.", fileDesc.Path(), e)
} else {
logrus.DefaultLogger().Debugf("Proto file %v was successfully "+
"added to dependency registry.", fileDesc.Path())
}
}
}
return reg, nil
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ require (
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df
github.com/xeipuuv/gojsonschema v1.1.0
go.etcd.io/etcd/client/v3 v3.5.1
go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220120170329-049260adfdff
go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220211111933-3d9ff310b1fa
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
google.golang.org/grpc v1.38.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -684,8 +684,8 @@ go.etcd.io/etcd/raft/v3 v3.5.1 h1:nthXrxmATKB9OZ9C64sk3QZaJ1WZ8tmlI0VwzpJSZwg=
go.etcd.io/etcd/raft/v3 v3.5.1/go.mod h1:WIlKzH/rjc54LDZ8SOa7GObrrdX3z96MkP1WDfODBeA=
go.etcd.io/etcd/server/v3 v3.5.1 h1:u8risUH348DmLy2XD3krH/S3GWk2ljuCrs8V3hd4584=
go.etcd.io/etcd/server/v3 v3.5.1/go.mod h1:yBKYw++NWu6ciuWoKuL7UXgGKDP7ICBCuVQrIcYbPdw=
go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220120170329-049260adfdff h1:1b+H5kMrYlCd2M2Zi6Rb5MQ926mN9kxEG36xk82ZN8Y=
go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220120170329-049260adfdff/go.mod h1:N4rrtufgHchultyJuN8ClAS7szH3IhcfDcC3LX9cRRo=
go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220211111933-3d9ff310b1fa h1:PbrmPqtS8K7jVVXbs9w2n0AzEqT3KGW/0bXWrSgEoOo=
go.ligato.io/cn-infra/v2 v2.5.0-alpha.0.20220211111933-3d9ff310b1fa/go.mod h1:N4rrtufgHchultyJuN8ClAS7szH3IhcfDcC3LX9cRRo=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
Expand Down
6 changes: 5 additions & 1 deletion plugins/kvscheduler/internal/utils/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ func ProtoToString(msg proto.Message) string {
if _, isEmpty := msg.(*emptypb.Empty); isEmpty {
return "<EMPTY>"
}
b, err := prototext.Marshal(msg)
if err != nil {
return err.Error()
}
// wrap with curly braces, it is easier to read
return "{ " + prototext.Format(msg) + " }"
return "{ " + string(b) + " }"
}

// ErrorToString converts error to string.
Expand Down
136 changes: 78 additions & 58 deletions plugins/restapi/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"runtime"
"strings"

yaml2 "github.com/ghodss/yaml"
"github.com/goccy/go-yaml"
"github.com/unrolled/render"
"go.ligato.io/cn-infra/v2/logging"
"go.ligato.io/cn-infra/v2/logging/logrus"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/encoding/prototext"
Expand Down Expand Up @@ -375,67 +377,15 @@ func (p *Plugin) registerHTTPHandler(key, method string, f func() (interface{},
// URL query parameter value).
func (p *Plugin) jsonSchemaHandler(formatter *render.Render) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
// create FileDescriptorProto for dynamic Config holding all VPP-Agent configuration
knownModels, err := client.LocalClient.KnownModels("config") // locally registered models
if err != nil {
p.internalError("can't get registered models", err, w, formatter)
return
}
config, err := client.NewDynamicConfig(knownModels)
if err != nil {
p.internalError("can't create dynamic config", err, w, formatter)
return
}
dynConfigFileDescProto := protodesc.ToFileDescriptorProto(config.ProtoReflect().Descriptor().ParentFile())

// create list of all FileDescriptorProtos (imports should be before converted proto file -> dynConfig is last)
fileDescriptorProtos := allFileDescriptorProtos(knownModels)
fileDescriptorProtos = append(fileDescriptorProtos, dynConfigFileDescProto)

// creating input for protoc's plugin (code extracted in plugins/restapi/jsonschema) that can convert
// FileDescriptorProtos to JSONSchema
params := []string{
"messages=[Dynamic_config]", // targeting only the main config message (proto file has also other messages)
"disallow_additional_properties", // additional unknown json fields makes configuration applying fail
}
fieldNamesConverterParam := "proto_and_json_fieldnames" // create proto and json named fields by default
if fieldNames, found := req.URL.Query()[URLFieldNamingParamName]; found && len(fieldNames) > 0 {
// converting REST API request params to 3rd party tool params
switch fieldNames[0] {
case OnlyProtoFieldNames:
fieldNamesConverterParam = ""
case OnlyJSONFieldNames:
fieldNamesConverterParam = "json_fieldnames"
}
}
if fieldNamesConverterParam != "" {
params = append(params, fieldNamesConverterParam)
}
paramsStr := strings.Join(params, ",")
cgReq := &pluginpb.CodeGeneratorRequest{
ProtoFile: fileDescriptorProtos,
FileToGenerate: []string{dynConfigFileDescProto.GetName()},
Parameter: &paramsStr,
CompilerVersion: nil, // compiler version is not need in this protoc plugin
}
cgReqMarshalled, err := proto.Marshal(cgReq)
res, err := buildJsonSchema(req.URL.Query())
if err != nil {
p.internalError("can't proto marshal CodeGeneratorRequest", err, w, formatter)
return
}

// use JSON schema converter and handle error cases
p.Log.Debug("Processing code generator request")
protoConverter := converter.New(logrus.DefaultLogger().Logger)
res, err := protoConverter.ConvertFrom(bytes.NewReader(cgReqMarshalled))
if err != nil {
if res == nil {
p.internalError("failed to read registered model configuration input", err, w, formatter)
if res != nil {
errMsg := fmt.Sprintf("failed generate JSON schema: %v (%v)\n", res.Error, err)
p.Log.Error(internalErrorLogPrefix + errMsg)
p.logError(formatter.JSON(w, http.StatusInternalServerError, errMsg))
return
}
errMsg := fmt.Sprintf("failed generate JSON schema: %v (%v)\n", res.Error, err)
p.Log.Error(internalErrorLogPrefix + errMsg)
p.logError(formatter.JSON(w, http.StatusInternalServerError, errMsg))
p.internalError("", err, w, formatter)
return
}

Expand All @@ -456,6 +406,76 @@ func (p *Plugin) jsonSchemaHandler(formatter *render.Render) http.HandlerFunc {
}
}

func buildJsonSchema(query url.Values) (*pluginpb.CodeGeneratorResponse, error) {
logging.Debugf("=======================================================")
logging.Debugf(" BUILDING JSON SCHEMA ")
logging.Debugf("=======================================================")

// create FileDescriptorProto for dynamic Config holding all VPP-Agent configuration
knownModels, err := client.LocalClient.KnownModels("config") // locally registered models
if err != nil {
return nil, fmt.Errorf("can't get registered models: %w", err)
}
config, err := client.NewDynamicConfig(knownModels)
if err != nil {
return nil, fmt.Errorf("can't create dynamic config: %w", err)
}
dynConfigFileDescProto := protodesc.ToFileDescriptorProto(config.ProtoReflect().Descriptor().ParentFile())

// create list of all FileDescriptorProtos (imports should be before converted proto file -> dynConfig is last)
fileDescriptorProtos := allFileDescriptorProtos(knownModels)
fileDescriptorProtos = append(fileDescriptorProtos, dynConfigFileDescProto)

// creating input for protoc's plugin (code extracted in plugins/restapi/jsonschema) that can convert
// FileDescriptorProtos to JSONSchema
params := []string{
"messages=[Dynamic_config]", // targeting only the main config message (proto file has also other messages)
"disallow_additional_properties", // additional unknown json fields makes configuration applying fail
}
fieldNamesConverterParam := "proto_and_json_fieldnames" // create proto and json named fields by default
if fieldNames, found := query[URLFieldNamingParamName]; found && len(fieldNames) > 0 {
// converting REST API request params to 3rd party tool params
switch fieldNames[0] {
case OnlyProtoFieldNames:
fieldNamesConverterParam = ""
case OnlyJSONFieldNames:
fieldNamesConverterParam = "json_fieldnames"
}
}
if fieldNamesConverterParam != "" {
params = append(params, fieldNamesConverterParam)
}
paramsStr := strings.Join(params, ",")
cgReq := &pluginpb.CodeGeneratorRequest{
ProtoFile: fileDescriptorProtos,
FileToGenerate: []string{dynConfigFileDescProto.GetName()},
Parameter: &paramsStr,
CompilerVersion: nil, // compiler version is not need in this protoc plugin
}
cgReqMarshalled, err := proto.Marshal(cgReq)
if err != nil {
return nil, fmt.Errorf("can't proto marshal CodeGeneratorRequest: %w", err)
}

logging.Debugf("-------------------------------------------------------")
logging.Debugf(" CONVERTING SCHEMA ")
logging.Debugf("-------------------------------------------------------")

// use JSON schema converter and handle error cases
logging.Debug("Processing code generator request")
protoConverter := converter.New(logrus.DefaultLogger().Logger)
res, err := protoConverter.ConvertFrom(bytes.NewReader(cgReqMarshalled))
if err != nil {
if res == nil {
// p.internalError("failed to read registered model configuration input", err, w, formatter)
return nil, fmt.Errorf("failed to read registered model configuration input: %w", err)
}
return res, err
}

return res, nil
}

// allImports retrieves all imports from given FileDescriptor including transitive imports (import
// duplication can occur)
func allImports(fileDesc protoreflect.FileDescriptor) []protoreflect.FileDescriptor {
Expand Down
19 changes: 11 additions & 8 deletions plugins/restapi/jsonschema/converter/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,21 @@ func (c *Converter) convertField(curPkg *ProtoPackage, desc *descriptorpb.FieldD
jsonSchemaType.Description = formatDescription(src)
}

c.logger.Tracef("(PKG: %v) CONVERT FIELD %v", curPkg.name, desc)

// get field annotations
var fieldAnnotations *ligato.LigatoOptions
val := proto.GetExtension(desc, ligato.E_LigatoOptions)
/*if err != nil {

if proto.HasExtension(desc.Options, ligato.E_LigatoOptions) {
val := proto.GetExtension(desc.Options, ligato.E_LigatoOptions)
var ok bool
if fieldAnnotations, ok = val.(*ligato.LigatoOptions); !ok {
c.logger.Debugf("Field %s.%s have ligato option extension, but its value has "+
"unexpected type (%T)", msg.GetName(), desc.GetName(), val)
}
} else {
c.logger.Debugf("Field %s.%s doesn't have ligato option extension", msg.GetName(), desc.GetName())
} else {*/
var ok bool
if fieldAnnotations, ok = val.(*ligato.LigatoOptions); !ok {
c.logger.Debugf("Field %s.%s have ligato option extension, but its value has "+
"unexpected type (%T)", msg.GetName(), desc.GetName(), val)
}
// }

// Switch the types, and pick a JSONSchema equivalent:
switch desc.GetType() {
Expand Down
37 changes: 26 additions & 11 deletions proto/ligato/configurator/configurator_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 22 additions & 7 deletions proto/ligato/configurator/statspoller_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 23b8b9b

Please sign in to comment.