diff --git a/adapter/socketclient/socketclient.go b/adapter/socketclient/socketclient.go index 0b70da12..49277c5d 100644 --- a/adapter/socketclient/socketclient.go +++ b/adapter/socketclient/socketclient.go @@ -268,7 +268,7 @@ func (c *Client) Connect() error { return err } - if err := c.open(); err != nil { + if err := c.open(c.clientName); err != nil { _ = c.disconnect() return err } @@ -353,12 +353,12 @@ const ( deleteMsgContext = byte(124) ) -func (c *Client) open() error { +func (c *Client) open(clientName string) error { var msgCodec = codec.DefaultCodec // Request socket client create req := &memclnt.SockclntCreate{ - Name: c.clientName, + Name: clientName, } msg, err := msgCodec.EncodeMsg(req, sockCreateMsgId) if err != nil { @@ -540,6 +540,7 @@ func (c *Client) readerLoop() { msg, err := c.readMsg(buf[:]) if err != nil { if isClosedError(err) { + log.Debugf("reader closed: %v", err) return } log.Debugf("readMsg error: %v", err) diff --git a/binapigen/vppapi/input.go b/binapigen/vppapi/input.go index af3524ba..6805b41d 100644 --- a/binapigen/vppapi/input.go +++ b/binapigen/vppapi/input.go @@ -133,7 +133,7 @@ func cloneRepoLocally(repo string, commit string, branch string, depth int) (str } else if err != nil { return "", fmt.Errorf("failed to check if cache exists: %w", err) } - logrus.Debugf("local repo dir: %q, fetching %q", cachePath, commit) + logrus.Debugf("using local repo dir: %q, fetching %q", cachePath, commit) cmd := exec.Command("git", "fetch", "--tags", "origin") cmd.Dir = cachePath diff --git a/cmd/govpp/cmd_vppapi_diff.go b/cmd/govpp/cmd_vppapi_diff.go index e7ec297f..8cf3a42e 100644 --- a/cmd/govpp/cmd_vppapi_diff.go +++ b/cmd/govpp/cmd_vppapi_diff.go @@ -22,8 +22,6 @@ import ( "github.com/olekukonko/tablewriter" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - - "go.fd.io/govpp/binapigen/vppapi" ) // TODO: @@ -103,7 +101,7 @@ func runVppApiDiffCmd(out io.Writer, opts VppApiDiffCmdOptions) error { return err } - vppAgainst, err := vppapi.ResolveVppInput(opts.Against) + vppAgainst, err := resolveVppInput(opts.Against) if err != nil { return fmt.Errorf("resolving --against failed: %w", err) } diff --git a/cmd/govpp/compare.go b/cmd/govpp/compare.go index 76b6ca92..8df28ff3 100644 --- a/cmd/govpp/compare.go +++ b/cmd/govpp/compare.go @@ -115,7 +115,7 @@ func CompareSchemas(schema1, schema2 *vppapi.Schema) []Difference { differences = append(differences, Difference{ Type: VersionDifference, Description: color.Sprintf("Schema version is different: %s vs %s", - clrWhite.Sprint(schema1.Version), clrWhite.Sprint(schema2.Version)), + clrDiffVersion.Sprint(schema1.Version), clrDiffVersion.Sprint(schema2.Version)), Value1: schema1.Version, Value2: schema2.Version, }) @@ -126,7 +126,7 @@ func CompareSchemas(schema1, schema2 *vppapi.Schema) []Difference { Type: TotalFilesDifference, Description: color.Sprintf("Total file count %s from %v to %v", clrWhite.Sprint(numberChangeString(len(schema1.Files), len(schema2.Files))), - clrWhite.Sprint(len(schema1.Files)), clrWhite.Sprint(len(schema2.Files))), + clrDiffNumber.Sprint(len(schema1.Files)), clrDiffNumber.Sprint(len(schema2.Files))), Value1: len(schema1.Files), Value2: len(schema2.Files), }) @@ -169,7 +169,7 @@ func compareSchemaFiles(files1 []vppapi.File, files2 []vppapi.File) []Difference } else { differences = append(differences, Difference{ Type: FileRemovedDifference, - Description: color.Sprintf("File removed: %s", clrWhite.Sprint(fileName)), + Description: color.Sprintf("File removed: %s", clrDiffFile.Sprint(fileName)), Value1: file1, Value2: nil, }) @@ -181,7 +181,7 @@ func compareSchemaFiles(files1 []vppapi.File, files2 []vppapi.File) []Difference if _, ok := fileMap1[fileName]; !ok { differences = append(differences, Difference{ Type: FileAddedDifference, - Description: color.Sprintf("File added: %s", clrWhite.Sprint(fileName)), + Description: color.Sprintf("File added: %s", clrDiffFile.Sprint(fileName)), Value1: nil, Value2: file2, }) @@ -228,7 +228,7 @@ func compareFiles(file1, file2 vppapi.File) []Difference { differences = append(differences, Difference{ Type: FileVersionDifference, Description: color.Sprintf("File version changed from %s to %s", - clrWhite.Sprint(fileVer1), clrWhite.Sprint(fileVer2)), + clrDiffVersion.Sprint(fileVer1), clrDiffVersion.Sprint(fileVer2)), Value1: fileVer1, Value2: fileVer2, }) @@ -237,7 +237,7 @@ func compareFiles(file1, file2 vppapi.File) []Difference { differences = append(differences, Difference{ Type: FileCrcDifference, Description: color.Sprintf("File CRC changed from %s to %s", - clrWhite.Sprint(file1.CRC), clrWhite.Sprint(file2.CRC)), + clrDiffVersion.Sprint(file1.CRC), clrDiffVersion.Sprint(file2.CRC)), Value1: file1.CRC, Value2: file2.CRC, }) @@ -289,7 +289,7 @@ func compareFiles(file1, file2 vppapi.File) []Difference { } else { differences = append(differences, Difference{ Type: MessageRemovedDifference, - Description: color.Sprintf("Message removed: %s", clrCyan.Sprint(msgName)), + Description: color.Sprintf("Message removed: %s", clrDiffMessage.Sprint(msgName)), Value1: msg1, Value2: nil, }) @@ -300,7 +300,7 @@ func compareFiles(file1, file2 vppapi.File) []Difference { if _, ok := msgMap1[msgName]; !ok { differences = append(differences, Difference{ Type: MessageAddedDifference, - Description: color.Sprintf("Message added: %s", clrCyan.Sprint(msgName)), + Description: color.Sprintf("Message added: %s", clrDiffMessage.Sprint(msgName)), Value1: nil, Value2: msg, }) @@ -324,7 +324,7 @@ func compareMessages(msg1 vppapi.Message, msg2 vppapi.Message) []Difference { differences = append(differences, Difference{ Type: MessageCrcDifference, Description: color.Sprintf("Message %s changed CRC from %s to %s", - clrCyan.Sprint(msg1.Name), clrWhite.Sprint(msg1.CRC), clrWhite.Sprint(msg2.CRC)), + clrDiffMessage.Sprint(msg1.Name), clrDiffVersion.Sprint(msg1.CRC), clrDiffVersion.Sprint(msg2.CRC)), Value1: msg1.CRC, Value2: msg2.CRC, }) @@ -332,7 +332,7 @@ func compareMessages(msg1 vppapi.Message, msg2 vppapi.Message) []Difference { // Compare message comments if msg1.Comment != msg2.Comment { - desc := color.Sprintf("Message %s comment ", clrCyan.Sprint(msg1.Name)) + desc := color.Sprintf("Message %s comment ", clrDiffMessage.Sprint(msg1.Name)) if msg1.Comment == "" { desc += "added" } else if msg2.Comment == "" { @@ -355,7 +355,7 @@ func compareMessages(msg1 vppapi.Message, msg2 vppapi.Message) []Difference { differences = append(differences, Difference{ Type: MsgOptionChangedDifference, Description: color.Sprintf("Message %s changed option %s from %q to %q", - clrCyan.Sprint(msg1.Name), clrWhite.Sprint(option), clrWhite.Sprint(val1), clrWhite.Sprint(val2)), + clrDiffMessage.Sprint(msg1.Name), clrDiffOption.Sprint(option), clrDiffOption.Sprint(val1), clrDiffOption.Sprint(val2)), Value1: keyValString(option, val1), Value2: keyValString(option, val2), }) @@ -364,7 +364,7 @@ func compareMessages(msg1 vppapi.Message, msg2 vppapi.Message) []Difference { differences = append(differences, Difference{ Type: MsgOptionRemovedDifference, Description: color.Sprintf("Message %s removed option: %s", - clrCyan.Sprint(msg1.Name), clrWhite.Sprint(option)), + clrDiffMessage.Sprint(msg1.Name), clrDiffOption.Sprint(keyValString(option, val1))), Value1: keyValString(option, val1), Value2: "", }) @@ -376,7 +376,7 @@ func compareMessages(msg1 vppapi.Message, msg2 vppapi.Message) []Difference { differences = append(differences, Difference{ Type: MsgOptionAddedDifference, Description: color.Sprintf("Message %s added option: %s", - clrCyan.Sprint(msg2.Name), clrWhite.Sprint(keyValString(option, val2))), + clrDiffMessage.Sprint(msg2.Name), clrDiffOption.Sprint(keyValString(option, val2))), Value1: "", Value2: keyValString(option, val2), }) @@ -390,7 +390,7 @@ func numberOfContentChangedDifference(typ string, c1, c2 int) Difference { return Difference{ Type: FileContentsChangedDifference, Description: color.Sprintf("Number of %s has %s from %v to %v", - clrWhite.Sprint(typ), clrWhite.Sprint(numberChangeString(c1, c2)), clrWhite.Sprint(c1), clrWhite.Sprint(c2)), + clrWhite.Sprint(typ), clrWhite.Sprint(numberChangeString(c1, c2)), clrDiffNumber.Sprint(c1), clrDiffNumber.Sprint(c2)), Value1: c1, Value2: c2, } @@ -411,5 +411,5 @@ func keyValString(k, v string) string { if v == "" { return k } - return color.Sprintf("%s=%s", k, v) + return color.Sprintf("%s=%q", k, v) } diff --git a/cmd/govpp/input.go b/cmd/govpp/input.go index ecf716c0..a2b79403 100644 --- a/cmd/govpp/input.go +++ b/cmd/govpp/input.go @@ -15,6 +15,7 @@ package main import ( + "fmt" "os" "path" "path/filepath" @@ -42,8 +43,13 @@ func resolveVppInput(input string) (*vppapi.VppInput, error) { tookSec := time.Since(t).Seconds() - logrus.Tracef("resolved VPP input %q in %.3fs\n%s\n - API dir: %s\n - VPP Version: %s\n - Files: %v", - input, tookSec, strings.Repeat("-", 100), vppInput.ApiDirectory, vppInput.Schema.Version, len(vppInput.Schema.Files)) + logrus.WithFields(map[string]interface{}{ + "took": fmt.Sprintf("%.3fs", tookSec), + "version": vppInput.Schema.Version, + "files": len(vppInput.Schema.Files), + "apiDir": len(vppInput.Schema.Files), + }).Tracef("resolved VPP input %q\n%s\n - API dir: %s\n - VPP Version: %s\n - Files: %v", + input, strings.Repeat("-", 100), vppInput.ApiDirectory, vppInput.Schema.Version, len(vppInput.Schema.Files)) return vppInput, nil } diff --git a/cmd/govpp/util.go b/cmd/govpp/util.go index be8da34e..8b801fcc 100644 --- a/cmd/govpp/util.go +++ b/cmd/govpp/util.go @@ -25,9 +25,12 @@ import ( ) var ( - clrWhite = color.Style{color.White} - clrCyan = color.Style{color.Cyan} - clrDiffFile = color.Style{color.Yellow} + clrWhite = color.Style{color.White} + clrDiffMessage = color.Style{color.Cyan} + clrDiffOption = color.Style{color.Blue} + clrDiffFile = color.Style{color.Yellow} + clrDiffVersion = color.Style{color.LightMagenta} + clrDiffNumber = color.Style{color.LightBlue} ) const ( diff --git a/codec/buffer.go b/codec/buffer.go new file mode 100644 index 00000000..21354a1d --- /dev/null +++ b/codec/buffer.go @@ -0,0 +1,191 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// 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 codec + +import ( + "bytes" + "encoding/binary" + "math" +) + +// Buffer provides buffer for encoding and decoding data on wire. +type Buffer struct { + buf []byte + pos int +} + +// NewBuffer creates new buffer using b as data. +func NewBuffer(b []byte) *Buffer { + return &Buffer{ + buf: b, + } +} + +// Bytes returns buffer data up to current position. +func (b *Buffer) Bytes() []byte { + return b.buf[:b.pos] +} + +func (b *Buffer) EncodeBytes(v []byte, length int) { + if length == 0 { + length = len(v) + } + copy(b.buf[b.pos:b.pos+length], v) + b.pos += length +} + +func (b *Buffer) DecodeBytes(length int) []byte { + v := b.buf[b.pos : b.pos+length] + b.pos += length + return v +} + +func (b *Buffer) EncodeBool(v bool) { + if v { + b.buf[b.pos] = 1 + } else { + b.buf[b.pos] = 0 + } + b.pos += 1 +} + +func (b *Buffer) DecodeBool() bool { + v := b.buf[b.pos] != 0 + b.pos += 1 + return v +} + +func (b *Buffer) EncodeUint8(v uint8) { + b.buf[b.pos] = byte(v) + b.pos += 1 +} + +func (b *Buffer) DecodeUint8() uint8 { + v := uint8(b.buf[b.pos]) + b.pos += 1 + return v +} + +func (b *Buffer) EncodeUint16(v uint16) { + binary.BigEndian.PutUint16(b.buf[b.pos:b.pos+2], v) + b.pos += 2 +} + +func (b *Buffer) DecodeUint16() uint16 { + v := binary.BigEndian.Uint16(b.buf[b.pos : b.pos+2]) + b.pos += 2 + return v +} + +func (b *Buffer) EncodeUint32(v uint32) { + binary.BigEndian.PutUint32(b.buf[b.pos:b.pos+4], v) + b.pos += 4 +} + +func (b *Buffer) DecodeUint32() uint32 { + v := binary.BigEndian.Uint32(b.buf[b.pos : b.pos+4]) + b.pos += 4 + return v +} + +func (b *Buffer) EncodeUint64(v uint64) { + binary.BigEndian.PutUint64(b.buf[b.pos:b.pos+8], v) + b.pos += 8 +} + +func (b *Buffer) DecodeUint64() uint64 { + v := binary.BigEndian.Uint64(b.buf[b.pos : b.pos+8]) + b.pos += 8 + return v +} + +func (b *Buffer) EncodeInt8(v int8) { + b.buf[b.pos] = byte(v) + b.pos += 1 +} + +func (b *Buffer) DecodeInt8() int8 { + v := int8(b.buf[b.pos]) + b.pos += 1 + return v +} + +func (b *Buffer) EncodeInt16(v int16) { + binary.BigEndian.PutUint16(b.buf[b.pos:b.pos+2], uint16(v)) + b.pos += 2 +} + +func (b *Buffer) DecodeInt16() int16 { + v := int16(binary.BigEndian.Uint16(b.buf[b.pos : b.pos+2])) + b.pos += 2 + return v +} + +func (b *Buffer) EncodeInt32(v int32) { + binary.BigEndian.PutUint32(b.buf[b.pos:b.pos+4], uint32(v)) + b.pos += 4 +} + +func (b *Buffer) DecodeInt32() int32 { + v := int32(binary.BigEndian.Uint32(b.buf[b.pos : b.pos+4])) + b.pos += 4 + return v +} + +func (b *Buffer) EncodeInt64(v int64) { + binary.BigEndian.PutUint64(b.buf[b.pos:b.pos+8], uint64(v)) + b.pos += 8 +} + +func (b *Buffer) DecodeInt64() int64 { + v := int64(binary.BigEndian.Uint64(b.buf[b.pos : b.pos+8])) + b.pos += 8 + return v +} + +func (b *Buffer) EncodeFloat64(v float64) { + binary.LittleEndian.PutUint64(b.buf[b.pos:b.pos+8], math.Float64bits(v)) + b.pos += 8 +} + +func (b *Buffer) DecodeFloat64() float64 { + v := math.Float64frombits(binary.LittleEndian.Uint64(b.buf[b.pos : b.pos+8])) + b.pos += 8 + return v +} + +func (b *Buffer) EncodeString(v string, length int) { + if length == 0 { + b.EncodeUint32(uint32(len(v))) + length = len(v) + } + copy(b.buf[b.pos:b.pos+length], v) + b.pos += length +} + +func (b *Buffer) DecodeString(length int) string { + var v []byte + if length == 0 { + length = int(b.DecodeUint32()) + v = b.buf[b.pos : b.pos+length] + } else { + v = b.buf[b.pos : b.pos+length] + if nul := bytes.Index(v, []byte{0x00}); nul >= 0 { + v = v[:nul] + } + } + b.pos += length + return string(v) +} diff --git a/codec/codec.go b/codec/codec.go index 21354a1d..308f79d3 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -1,191 +1,130 @@ -// Copyright (c) 2020 Cisco and/or its affiliates. +// Copyright (c) 2017 Cisco and/or its affiliates. // -// 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: +// 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 +// 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. +// 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 codec import ( - "bytes" "encoding/binary" - "math" + "errors" + "fmt" + + "go.fd.io/govpp/api" ) -// Buffer provides buffer for encoding and decoding data on wire. -type Buffer struct { - buf []byte - pos int -} +var DefaultCodec = new(MsgCodec) -// NewBuffer creates new buffer using b as data. -func NewBuffer(b []byte) *Buffer { - return &Buffer{ - buf: b, - } +func EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) { + return DefaultCodec.EncodeMsg(msg, msgID) } - -// Bytes returns buffer data up to current position. -func (b *Buffer) Bytes() []byte { - return b.buf[:b.pos] +func DecodeMsg(data []byte, msg api.Message) (err error) { + return DefaultCodec.DecodeMsg(data, msg) } - -func (b *Buffer) EncodeBytes(v []byte, length int) { - if length == 0 { - length = len(v) - } - copy(b.buf[b.pos:b.pos+length], v) - b.pos += length +func DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) { + return DefaultCodec.DecodeMsgContext(data, msgType) } -func (b *Buffer) DecodeBytes(length int) []byte { - v := b.buf[b.pos : b.pos+length] - b.pos += length - return v -} +// MsgCodec provides encoding and decoding functionality of `api.Message` structs into/from +// binary format as accepted by VPP. +type MsgCodec struct{} -func (b *Buffer) EncodeBool(v bool) { - if v { - b.buf[b.pos] = 1 - } else { - b.buf[b.pos] = 0 +func (*MsgCodec) EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) { + if msg == nil { + return nil, errors.New("nil message passed in") } - b.pos += 1 -} -func (b *Buffer) DecodeBool() bool { - v := b.buf[b.pos] != 0 - b.pos += 1 - return v -} - -func (b *Buffer) EncodeUint8(v uint8) { - b.buf[b.pos] = byte(v) - b.pos += 1 -} - -func (b *Buffer) DecodeUint8() uint8 { - v := uint8(b.buf[b.pos]) - b.pos += 1 - return v -} - -func (b *Buffer) EncodeUint16(v uint16) { - binary.BigEndian.PutUint16(b.buf[b.pos:b.pos+2], v) - b.pos += 2 -} - -func (b *Buffer) DecodeUint16() uint16 { - v := binary.BigEndian.Uint16(b.buf[b.pos : b.pos+2]) - b.pos += 2 - return v -} - -func (b *Buffer) EncodeUint32(v uint32) { - binary.BigEndian.PutUint32(b.buf[b.pos:b.pos+4], v) - b.pos += 4 -} - -func (b *Buffer) DecodeUint32() uint32 { - v := binary.BigEndian.Uint32(b.buf[b.pos : b.pos+4]) - b.pos += 4 - return v -} - -func (b *Buffer) EncodeUint64(v uint64) { - binary.BigEndian.PutUint64(b.buf[b.pos:b.pos+8], v) - b.pos += 8 -} + // try to recover panic which might possibly occur + defer func() { + if r := recover(); r != nil { + var ok bool + if err, ok = r.(error); !ok { + err = fmt.Errorf("%v", r) + } + err = fmt.Errorf("panic occurred during encoding message %s: %v", msg.GetMessageName(), err) + } + }() -func (b *Buffer) DecodeUint64() uint64 { - v := binary.BigEndian.Uint64(b.buf[b.pos : b.pos+8]) - b.pos += 8 - return v -} + marshaller, ok := msg.(Marshaler) + if !ok { + marshaller = Wrapper{msg} + } -func (b *Buffer) EncodeInt8(v int8) { - b.buf[b.pos] = byte(v) - b.pos += 1 -} + size := marshaller.Size() + offset := getOffset(msg) -func (b *Buffer) DecodeInt8() int8 { - v := int8(b.buf[b.pos]) - b.pos += 1 - return v -} + // encode msg ID + b := make([]byte, size+offset) + b[0] = byte(msgID >> 8) + b[1] = byte(msgID) -func (b *Buffer) EncodeInt16(v int16) { - binary.BigEndian.PutUint16(b.buf[b.pos:b.pos+2], uint16(v)) - b.pos += 2 -} + data, err = marshaller.Marshal(b[offset:]) + if err != nil { + return nil, err + } -func (b *Buffer) DecodeInt16() int16 { - v := int16(binary.BigEndian.Uint16(b.buf[b.pos : b.pos+2])) - b.pos += 2 - return v + return b[0:len(b):len(b)], nil } -func (b *Buffer) EncodeInt32(v int32) { - binary.BigEndian.PutUint32(b.buf[b.pos:b.pos+4], uint32(v)) - b.pos += 4 -} +func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) (err error) { + if msg == nil { + return errors.New("nil message passed in") + } -func (b *Buffer) DecodeInt32() int32 { - v := int32(binary.BigEndian.Uint32(b.buf[b.pos : b.pos+4])) - b.pos += 4 - return v -} + // try to recover panic which might possibly occur + defer func() { + if r := recover(); r != nil { + var ok bool + if err, ok = r.(error); !ok { + err = fmt.Errorf("%v", r) + } + err = fmt.Errorf("panic occurred during decoding message %s: %v", msg.GetMessageName(), err) + } + }() -func (b *Buffer) EncodeInt64(v int64) { - binary.BigEndian.PutUint64(b.buf[b.pos:b.pos+8], uint64(v)) - b.pos += 8 -} + marshaller, ok := msg.(Unmarshaler) + if !ok { + marshaller = Wrapper{msg} + } -func (b *Buffer) DecodeInt64() int64 { - v := int64(binary.BigEndian.Uint64(b.buf[b.pos : b.pos+8])) - b.pos += 8 - return v -} + offset := getOffset(msg) -func (b *Buffer) EncodeFloat64(v float64) { - binary.LittleEndian.PutUint64(b.buf[b.pos:b.pos+8], math.Float64bits(v)) - b.pos += 8 -} + err = marshaller.Unmarshal(data[offset:]) + if err != nil { + return err + } -func (b *Buffer) DecodeFloat64() float64 { - v := math.Float64frombits(binary.LittleEndian.Uint64(b.buf[b.pos : b.pos+8])) - b.pos += 8 - return v + return nil } -func (b *Buffer) EncodeString(v string, length int) { - if length == 0 { - b.EncodeUint32(uint32(len(v))) - length = len(v) +func (*MsgCodec) DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) { + switch msgType { + case api.RequestMessage: + return binary.BigEndian.Uint32(data[6:10]), nil + case api.ReplyMessage: + return binary.BigEndian.Uint32(data[2:6]), nil } - copy(b.buf[b.pos:b.pos+length], v) - b.pos += length + + return 0, nil } -func (b *Buffer) DecodeString(length int) string { - var v []byte - if length == 0 { - length = int(b.DecodeUint32()) - v = b.buf[b.pos : b.pos+length] - } else { - v = b.buf[b.pos : b.pos+length] - if nul := bytes.Index(v, []byte{0x00}); nul >= 0 { - v = v[:nul] - } +func getOffset(msg api.Message) (offset int) { + switch msg.GetMessageType() { + case api.RequestMessage: + return 10 + case api.ReplyMessage: + return 6 + case api.EventMessage: + return 6 } - b.pos += length - return string(v) + return 2 } diff --git a/codec/msg_codec_test.go b/codec/codec_test.go similarity index 100% rename from codec/msg_codec_test.go rename to codec/codec_test.go diff --git a/codec/msg_codec.go b/codec/msg_codec.go deleted file mode 100644 index 308f79d3..00000000 --- a/codec/msg_codec.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2017 Cisco and/or its affiliates. -// -// 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 codec - -import ( - "encoding/binary" - "errors" - "fmt" - - "go.fd.io/govpp/api" -) - -var DefaultCodec = new(MsgCodec) - -func EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) { - return DefaultCodec.EncodeMsg(msg, msgID) -} -func DecodeMsg(data []byte, msg api.Message) (err error) { - return DefaultCodec.DecodeMsg(data, msg) -} -func DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) { - return DefaultCodec.DecodeMsgContext(data, msgType) -} - -// MsgCodec provides encoding and decoding functionality of `api.Message` structs into/from -// binary format as accepted by VPP. -type MsgCodec struct{} - -func (*MsgCodec) EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) { - if msg == nil { - return nil, errors.New("nil message passed in") - } - - // try to recover panic which might possibly occur - defer func() { - if r := recover(); r != nil { - var ok bool - if err, ok = r.(error); !ok { - err = fmt.Errorf("%v", r) - } - err = fmt.Errorf("panic occurred during encoding message %s: %v", msg.GetMessageName(), err) - } - }() - - marshaller, ok := msg.(Marshaler) - if !ok { - marshaller = Wrapper{msg} - } - - size := marshaller.Size() - offset := getOffset(msg) - - // encode msg ID - b := make([]byte, size+offset) - b[0] = byte(msgID >> 8) - b[1] = byte(msgID) - - data, err = marshaller.Marshal(b[offset:]) - if err != nil { - return nil, err - } - - return b[0:len(b):len(b)], nil -} - -func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) (err error) { - if msg == nil { - return errors.New("nil message passed in") - } - - // try to recover panic which might possibly occur - defer func() { - if r := recover(); r != nil { - var ok bool - if err, ok = r.(error); !ok { - err = fmt.Errorf("%v", r) - } - err = fmt.Errorf("panic occurred during decoding message %s: %v", msg.GetMessageName(), err) - } - }() - - marshaller, ok := msg.(Unmarshaler) - if !ok { - marshaller = Wrapper{msg} - } - - offset := getOffset(msg) - - err = marshaller.Unmarshal(data[offset:]) - if err != nil { - return err - } - - return nil -} - -func (*MsgCodec) DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) { - switch msgType { - case api.RequestMessage: - return binary.BigEndian.Uint32(data[6:10]), nil - case api.ReplyMessage: - return binary.BigEndian.Uint32(data[2:6]), nil - } - - return 0, nil -} - -func getOffset(msg api.Message) (offset int) { - switch msg.GetMessageType() { - case api.RequestMessage: - return 10 - case api.ReplyMessage: - return 6 - case api.EventMessage: - return 6 - } - return 2 -} diff --git a/docs/GOVPP_CLI.md b/docs/GOVPP_CLI.md index 3ceec21e..b0567a6b 100644 --- a/docs/GOVPP_CLI.md +++ b/docs/GOVPP_CLI.md @@ -156,10 +156,11 @@ The `diff` command allows you to compare two VPP API schemas and lists the diffe Here's an example usage of the `diff` command: ```sh -govpp vppapi diff "./vppapi2210" --against "./vppapi2302" + govpp vppapi diff 'http://github.com/FDio/vpp.git#tag=v23.10' --against 'http://github.com/FDio/vpp.git#tag=v24.02' ``` -This command compares the VPP API schema from `vppapi2210` directory against the VPP API schema in `vppapi2302` and lists the differences between them. The output shows related information details for each difference. +This command compares the VPP API schema of version`v23.10` against the VPP API schema of veersion `v24.02` and lists the differences between them. +The output shows related information details for each difference. You can use the `--help` flag to get more information about the available flags and options. diff --git a/test/integration/README.md b/test/integration/README.md index d19d443c..bbc35151 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -35,7 +35,7 @@ The make target above simply runs a helper script which accepts additional argum # ./test/integration/run_integration.sh # Run with verbose mode -./test/integration/run_integration.sh -test.v +./test/run_integration.sh -test.v ``` ### Run Specific Test Case @@ -43,7 +43,7 @@ The make target above simply runs a helper script which accepts additional argum To run a specific integration test case(s): ```shell -./test/integration/run_integration.sh -test.run="Interfaces" +./test/run_integration.sh -test.run="Interfaces" ``` ## Running Tests on your Host diff --git a/test/integration/main_test.go b/test/integration/main_test.go index 4c652601..dc906d11 100644 --- a/test/integration/main_test.go +++ b/test/integration/main_test.go @@ -18,9 +18,12 @@ package integration import ( "fmt" + "net/http" "os" "os/exec" "testing" + + _ "net/http/pprof" ) var ( @@ -30,6 +33,9 @@ var ( func TestMain(m *testing.M) { if IntegrationTestsActive { + go func() { + fmt.Fprintln(os.Stderr, http.ListenAndServe(":6060", nil)) + }() os.Exit(m.Run()) } fmt.Fprintf(os.Stderr, "integration tests are NOT enabled (set TEST='integration' to enable)\n") diff --git a/test/integration/trace_test.go b/test/integration/trace_test.go index fbb76c56..494ee98f 100644 --- a/test/integration/trace_test.go +++ b/test/integration/trace_test.go @@ -17,11 +17,11 @@ package integration import ( "context" "fmt" - "go.fd.io/govpp/core" "testing" "go.fd.io/govpp/api" "go.fd.io/govpp/binapi/vpe" + "go.fd.io/govpp/core" "go.fd.io/govpp/test/vpptesting" ) diff --git a/test/vpptesting/vpplauncher/vpplauncher.go b/test/vpptesting/vpplauncher/vpplauncher.go index 2ba10b52..6fff876b 100644 --- a/test/vpptesting/vpplauncher/vpplauncher.go +++ b/test/vpptesting/vpplauncher/vpplauncher.go @@ -203,9 +203,9 @@ func (p *VPP) Start() error { var exiterr *exec.ExitError if errors.As(err, &exiterr) { if strings.Contains(exiterr.Error(), "core dumped") { - err = fmt.Errorf("VPP crashed (%w) stderr: %s", exiterr, exiterr.Stderr) + err = fmt.Errorf("VPP crashed (%w) stderr: %s", exiterr, p.stderr.Bytes()) } else { - err = fmt.Errorf("VPP exited (%w) stderr: %s", exiterr, exiterr.Stderr) + err = fmt.Errorf("VPP exited (%w) stderr: %s", exiterr, p.stderr.Bytes()) } } } @@ -249,6 +249,9 @@ func (p *VPP) Stop() error { defer func() { p.cmd = nil }() + if p.exitErr != nil { + return nil + } // send signal to stop if err := p.cmd.Process.Signal(syscall.SIGTERM); err != nil { return fmt.Errorf("sending TERM signal failed: %w", err) diff --git a/test/vpptesting/vpptest.go b/test/vpptesting/vpptest.go index d2d96ae8..0a5cb865 100644 --- a/test/vpptesting/vpptest.go +++ b/test/vpptesting/vpptest.go @@ -50,15 +50,19 @@ const ( vppConnectRetryNum = 3 vppStopDelay = time.Millisecond * 50 vppDisconnectTimeout = time.Millisecond * 50 + vppReplyTimeout = time.Second * 1 ) type TestCtx struct { T testing.TB + Context context.Context + Cancel context.CancelFunc vppCmd *vpplauncher.VPP Conn *govppcore.Connection statsConn *govppcore.StatsConnection memclntRPC memclnt.RPCService vlibRPC vlib.RPCService + Err error } func SetupVPP(t testing.TB) (tc *TestCtx) { @@ -104,6 +108,7 @@ func SetupVPP(t testing.TB) (tc *TestCtx) { var conn *govppcore.Connection err = retry(vppConnectRetryNum, func() (err error) { + govppcore.DefaultReplyTimeout = vppReplyTimeout conn, err = govppcore.Connect(adapter) return }) @@ -126,8 +131,27 @@ func SetupVPP(t testing.TB) (tc *TestCtx) { t.Fatalf("expected VPP PID to be %v, got %v", vppPID, vpeInfo.VpePID) } + go func() { + q := make(chan struct{}) + t.Cleanup(func() { + close(q) + }) + select { + case <-q: + // do no wait after test + case exitErr := <-vppCmd.OnExit(): + if exitErr != nil { + t.Errorf("VPP process exited with error: %v", exitErr) + } + } + }() + + ctx, cancel := context.WithCancel(context.Background()) + return &TestCtx{ T: t, + Context: ctx, + Cancel: cancel, vppCmd: vppCmd, Conn: conn, memclntRPC: memclntRPC, @@ -184,6 +208,8 @@ func (ctx *TestCtx) TeardownVPP() { if err := ctx.vppCmd.Stop(); err != nil { ctx.T.Logf("stopping VPP failed: %v", err) + } else { + ctx.T.Logf("VPP stopped") } }