forked from openconfig/featureprofiles
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NOSImageProfile Validation for ocrpcs (openconfig#2529)
* Fix nosimage.textproto rpcs examples * Fix lint * Validate ocrpcs * debug * Use HTTPS * Retrigger CI * fix * test don't expect failure * revert to good * Improve readability * Enforce that examples are re-generated * Fix staticcheck * Run at 00:49 to avoid peak * gofmt
- Loading branch information
Showing
9 changed files
with
292 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
name: NOSImage validation script | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
schedule: | ||
- cron: "49 0 * * *" | ||
|
||
jobs: | ||
integration-test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check out code | ||
uses: actions/checkout@v3 | ||
- name: Set up Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version: stable | ||
cache: false | ||
- name: Generate Examples and Check No Diff | ||
run: | | ||
cd tools/nosimage | ||
go run example/generate_example.go -file-path example/example_nosimageprofile.textproto | ||
go run example/generate_example.go -file-path example/example_nosimageprofile_invalid.textproto -invalid | ||
git diff --exit-code --ignore-all-space --ignore-blank-lines | ||
- name: Validate Good Example | ||
run: | | ||
cd tools/nosimage | ||
go run validate/validate.go -file example/example_nosimageprofile.textproto; rm -rf tmp | ||
- name: Validate Bad Example | ||
run: | | ||
cd tools/nosimage | ||
if go run validate/validate.go -file example/example_nosimageprofile_invalid.textproto; then | ||
echo "Validation passed, but failure expected" | ||
exit 1 | ||
fi | ||
rm -rf tmp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Package ocrpcs contains utilities related to ocrpcs.proto. | ||
package ocrpcs | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/yoheimuta/go-protoparser/v4" | ||
|
||
rpb "github.com/openconfig/featureprofiles/proto/ocrpcs_go_proto" | ||
"github.com/openconfig/gnmi/errlist" | ||
) | ||
|
||
func cloneAPIRepo(downloadPath, api string) (string, error) { | ||
if downloadPath == "" { | ||
return "", fmt.Errorf("must provide download path") | ||
} | ||
repoPath := filepath.Join(downloadPath, api) | ||
|
||
cmd := exec.Command("git", "clone", "--single-branch", "--depth", "1", fmt.Sprintf("https://github.com/openconfig/%s.git", api), repoPath) | ||
stderr, err := cmd.StderrPipe() | ||
if err != nil { | ||
return "", err | ||
} | ||
if err := cmd.Start(); err != nil { | ||
return "", fmt.Errorf("failed to clone %s repo: %v, command failed to start: %q", api, err, cmd.String()) | ||
} | ||
stderrOutput, _ := io.ReadAll(stderr) | ||
if err := cmd.Wait(); err != nil { | ||
return "", fmt.Errorf("failed to clone %s repo: %v, command failed during execution: %q\n%s", api, err, cmd.String(), stderrOutput) | ||
} | ||
return repoPath, nil | ||
} | ||
|
||
func readAllRPCs(downloadPath, api string) (map[string]struct{}, error) { | ||
repoPath, err := cloneAPIRepo(downloadPath, api) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rpcs := map[string]struct{}{} | ||
|
||
if err := filepath.Walk(repoPath, | ||
func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if strings.HasSuffix(info.Name(), ".proto") { | ||
reader, err := os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
got, err := protoparser.Parse(reader) | ||
if err != nil { | ||
return fmt.Errorf("failed to parse, err %v", err) | ||
} | ||
visitor := &rpcServiceAccumulator{} | ||
got.Accept(visitor) | ||
for _, rpc := range visitor.rpcs { | ||
rpcs[rpc] = struct{}{} | ||
} | ||
} | ||
return nil | ||
}); err != nil { | ||
return nil, err | ||
} | ||
|
||
return rpcs, nil | ||
} | ||
|
||
// ValidateRPCs verifies whether the RPCs listed in protocols are valid. | ||
// | ||
// It returns the number of valid RPCs found, and an error if any RPC is | ||
// invalid, or there was an issue downloading or parsing OpenConfig protobuf | ||
// files. | ||
// | ||
// - downloadPath is a path to the folder which will contain OpenConfig | ||
// repositories that will be downloaded in order to validate the existence of | ||
// provided RPCs. | ||
func ValidateRPCs(downloadPath string, protocols map[string]*rpb.OCProtocol) (uint, error) { | ||
var validCount uint | ||
|
||
var errs errlist.List | ||
errs.Separator = "\n" | ||
for api, protocol := range protocols { | ||
rpcs, err := readAllRPCs(downloadPath, api) | ||
if err != nil { | ||
return 0, err | ||
} | ||
for _, name := range protocol.GetMethodName() { | ||
if _, ok := rpcs[name]; !ok { | ||
errs.Add(fmt.Errorf("RPC not found in openconfig repo %v: %v", api, name)) | ||
continue | ||
} | ||
validCount++ | ||
} | ||
} | ||
|
||
return validCount, errs.Err() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package ocrpcs | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/yoheimuta/go-protoparser/v4/parser" | ||
) | ||
|
||
type rpcServiceAccumulator struct { | ||
packageName string | ||
currentService string | ||
|
||
rpcs []string | ||
} | ||
|
||
var _ parser.Visitor = &rpcServiceAccumulator{} | ||
|
||
func (v *rpcServiceAccumulator) VisitComment(*parser.Comment) { | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitEmptyStatement(*parser.EmptyStatement) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitEnum(*parser.Enum) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitEnumField(*parser.EnumField) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitExtend(*parser.Extend) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitExtensions(*parser.Extensions) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitField(*parser.Field) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitGroupField(*parser.GroupField) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitImport(*parser.Import) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitMapField(*parser.MapField) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitMessage(*parser.Message) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitOneof(*parser.Oneof) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitOneofField(*parser.OneofField) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitOption(*parser.Option) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitPackage(p *parser.Package) (next bool) { | ||
v.packageName = p.Name | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitReserved(*parser.Reserved) (next bool) { | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitRPC(r *parser.RPC) (next bool) { | ||
v.rpcs = append(v.rpcs, fmt.Sprintf("%s.%s.%s", v.packageName, v.currentService, r.RPCName)) | ||
return | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitService(s *parser.Service) (next bool) { | ||
v.currentService = s.ServiceName | ||
return true | ||
} | ||
|
||
func (v *rpcServiceAccumulator) VisitSyntax(*parser.Syntax) (next bool) { | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.