Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix: Fix generating jsonschema #1836

Merged
merged 6 commits into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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