Skip to content

Commit

Permalink
feat: Add Plugin Proto V3 (#21)
Browse files Browse the repository at this point in the history
Instead of #15.

Plugin V3 Protocol client/server regenerated from cloudquery/plugin-pb#1
  • Loading branch information
yevgenypats authored Jun 23, 2023
1 parent ba7d275 commit 50ec9d9
Show file tree
Hide file tree
Showing 11 changed files with 2,523 additions and 15 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ gen-proto:
protoc --proto_path=. --go_out . --go_opt=module="github.com/cloudquery/plugin-pb-go" --go-grpc_out=. --go-grpc_opt=module="github.com/cloudquery/plugin-pb-go" plugin-pb/source/v1/source.proto
protoc --proto_path=. --go_out . --go_opt=module="github.com/cloudquery/plugin-pb-go" --go-grpc_out=. --go-grpc_opt=module="github.com/cloudquery/plugin-pb-go" plugin-pb/source/v2/source.proto
protoc --proto_path=. --go_out . --go_opt=module="github.com/cloudquery/plugin-pb-go" --go-grpc_out=. --go-grpc_opt=module="github.com/cloudquery/plugin-pb-go" plugin-pb/destination/v1/destination.proto
protoc --proto_path=. --go_out . --go_opt=module="github.com/cloudquery/plugin-pb-go" --go-grpc_out=. --go-grpc_opt=module="github.com/cloudquery/plugin-pb-go" plugin-pb/discovery/v1/discovery.proto
protoc --proto_path=. --go_out . --go_opt=module="github.com/cloudquery/plugin-pb-go" --go-grpc_out=. --go-grpc_opt=module="github.com/cloudquery/plugin-pb-go" plugin-pb/discovery/v1/discovery.proto
protoc --proto_path=. --go_out . --go_opt=module="github.com/cloudquery/plugin-pb-go" --go-grpc_out=. --go-grpc_opt=module="github.com/cloudquery/plugin-pb-go" plugin-pb/plugin/v3/plugin.proto
16 changes: 12 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ module github.com/cloudquery/plugin-pb-go
go 1.19

require (
github.com/apache/arrow/go/v13 v13.0.0-20230622042343-ec413b7763fe
github.com/avast/retry-go/v4 v4.3.4
github.com/ghodss/yaml v1.0.0
github.com/google/go-cmp v0.5.9
github.com/rs/zerolog v1.29.1
github.com/schollz/progressbar/v3 v3.13.1
github.com/stretchr/testify v1.8.4
github.com/thoas/go-funk v0.9.3
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
google.golang.org/grpc v1.55.0
Expand All @@ -17,18 +17,26 @@ require (
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/google/flatbuffers v23.1.21+incompatible // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
29 changes: 28 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/apache/arrow/go/v13 v13.0.0-20230622042343-ec413b7763fe h1:pvUEAu13JR1Apj8dYI73iMNEvBNfKWiCFaR0UAUUDtA=
github.com/apache/arrow/go/v13 v13.0.0-20230622042343-ec413b7763fe/go.mod h1:W69eByFNO0ZR30q1/7Sr9d83zcVZmF2MiP3fFYAWJOc=
github.com/avast/retry-go/v4 v4.3.4 h1:pHLkL7jvCvP317I8Ge+Km2Yhntv3SdkJm7uekkqbKhM=
github.com/avast/retry-go/v4 v4.3.4/go.mod h1:rv+Nla6Vk3/ilU0H51VHddWHiwimzX66yZ0JT6T+UvE=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
Expand All @@ -6,23 +8,36 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/flatbuffers v23.1.21+incompatible h1:bUqzx/MXCDxuS0hRJL2EfjyZL3uQrPbMocUa8zGqsTA=
github.com/google/flatbuffers v23.1.21+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand All @@ -45,20 +60,32 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
Expand Down
4 changes: 3 additions & 1 deletion managedplugin/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package managedplugin

import "github.com/rs/zerolog"

func JSONToLog(l zerolog.Logger, msg map[string]any) {
func (c *Client) jsonToLog(l zerolog.Logger, msg map[string]any) {
level := msg["level"]
delete(msg, "level")
switch level {
Expand All @@ -14,8 +14,10 @@ func JSONToLog(l zerolog.Logger, msg map[string]any) {
l.Info().Fields(msg).Msg("")
case "warn":
l.Warn().Fields(msg).Msg("")
c.metrics.incrementWarnings()
case "error":
l.Error().Fields(msg).Msg("")
c.metrics.incrementErrors()
default:
l.Error().Fields(msg).Msg("unknown level")
}
Expand Down
16 changes: 16 additions & 0 deletions managedplugin/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package managedplugin

import "sync/atomic"

type Metrics struct {
Errors uint64
Warnings uint64
}

func (m *Metrics) incrementErrors() {
atomic.AddUint64(&m.Errors, 1)
}

func (m *Metrics) incrementWarnings() {
atomic.AddUint64(&m.Warnings, 1)
}
16 changes: 11 additions & 5 deletions managedplugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type Client struct {
Conn *grpc.ClientConn
config Config
noSentry bool
metrics *Metrics
}

type Option func(*Client)
Expand Down Expand Up @@ -124,6 +125,7 @@ func NewClient(ctx context.Context, typ PluginType, config Config, opts ...Optio
directory: defaultDownloadDir,
wg: &sync.WaitGroup{},
config: config,
metrics: &Metrics{},
}
for _, opt := range opts {
opt(&c)
Expand Down Expand Up @@ -164,6 +166,10 @@ func NewClient(ctx context.Context, typ PluginType, config Config, opts ...Optio
return &c, nil
}

func (c *Client) Metrics() Metrics {
return *c.metrics
}

func (c *Client) startLocal(ctx context.Context, path string) error {
c.grpcSocketName = GenerateRandomUnixSocketName()
// spawn the plugin first and then connect
Expand All @@ -180,7 +186,7 @@ func (c *Client) startLocal(ctx context.Context, path string) error {
cmd.Stderr = os.Stderr
cmd.SysProcAttr = getSysProcAttr()
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start destination plugin %s: %w", path, err)
return fmt.Errorf("failed to start plugin %s: %w", path, err)
}

c.cmd = cmd
Expand All @@ -195,18 +201,18 @@ func (c *Client) startLocal(ctx context.Context, path string) error {
break
}
if errors.Is(err, ErrLogLineToLong) {
c.logger.Info().Str("line", string(line)).Msg("truncated destination plugin log line")
c.logger.Info().Str("line", string(line)).Msg("truncated plugin log line")
continue
}
if err != nil {
c.logger.Err(err).Msg("failed to read log line from destination plugin")
c.logger.Err(err).Msg("failed to read log line from plugin")
break
}
var structuredLogLine map[string]any
if err := json.Unmarshal(line, &structuredLogLine); err != nil {
c.logger.Err(err).Str("line", string(line)).Msg("failed to unmarshal log line from destination plugin")
c.logger.Err(err).Str("line", string(line)).Msg("failed to unmarshal log line from plugin")
} else {
JSONToLog(c.logger, structuredLogLine)
c.jsonToLog(c.logger, structuredLogLine)
}
}
}()
Expand Down
6 changes: 3 additions & 3 deletions pb/discovery/v1/discovery.pb.go

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

50 changes: 50 additions & 0 deletions pb/plugin/v3/arrow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package plugin

import (
"bytes"

"github.com/apache/arrow/go/v13/arrow"
"github.com/apache/arrow/go/v13/arrow/ipc"
)

func SchemaToBytes(sc *arrow.Schema) ([]byte, error) {
var buf bytes.Buffer
wr := ipc.NewWriter(&buf, ipc.WithSchema(sc))
if err := wr.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

func NewSchemaFromBytes(b []byte) (*arrow.Schema, error) {
rdr, err := ipc.NewReader(bytes.NewReader(b))
if err != nil {
return nil, err
}
return rdr.Schema(), nil
}

func RecordToBytes(record arrow.Record) ([]byte, error) {
var buf bytes.Buffer
wr := ipc.NewWriter(&buf, ipc.WithSchema(record.Schema()))
if err := wr.Write(record); err != nil {
return nil, err
}
if err := wr.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

func NewRecordFromBytes(b []byte) (arrow.Record, error) {
rdr, err := ipc.NewReader(bytes.NewReader(b))
if err != nil {
return nil, err
}
for rdr.Next() {
rec := rdr.Record()
rec.Retain()
return rec, nil
}
return nil, nil
}
53 changes: 53 additions & 0 deletions pb/plugin/v3/arrow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package plugin

import (
"testing"

"github.com/apache/arrow/go/v13/arrow"
"github.com/apache/arrow/go/v13/arrow/array"
"github.com/apache/arrow/go/v13/arrow/memory"
)

func TestSchemaRoundTrip(t *testing.T) {
md := arrow.NewMetadata([]string{"foo", "bar"}, []string{"baz", "quux"})
sc := arrow.NewSchema([]arrow.Field{
{Name: "a", Type: arrow.PrimitiveTypes.Int64},
{Name: "b", Type: arrow.PrimitiveTypes.Float64},
}, &md)
b, err := SchemaToBytes(sc)
if err != nil {
t.Fatal(err)
}
sc2, err := NewSchemaFromBytes(b)
if err != nil {
t.Fatal(err)
}
if !sc.Equal(sc2) {
t.Errorf("expected %v, got %v", sc, sc2)
}
}

func TestRecordRoundTrip(t *testing.T) {
md := arrow.NewMetadata([]string{"foo", "bar"}, []string{"baz", "quux"})
sc := arrow.NewSchema([]arrow.Field{
{Name: "a", Type: arrow.PrimitiveTypes.Int64},
{Name: "b", Type: arrow.PrimitiveTypes.Float64},
}, &md)
bldr := array.NewRecordBuilder(memory.DefaultAllocator, sc)
bldr.Field(0).(*array.Int64Builder).AppendValues([]int64{1, 2, 3}, nil)
bldr.Field(1).(*array.Float64Builder).AppendValues([]float64{1.1, 2.2, 3.3}, nil)
rec := bldr.NewRecord()
defer rec.Release()

b, err := RecordToBytes(rec)
if err != nil {
t.Fatal(err)
}
rec2, err := NewRecordFromBytes(b)
if err != nil {
t.Fatal(err)
}
if !array.RecordEqual(rec, rec2) {
t.Errorf("expected %v, got %v", rec, rec2)
}
}
Loading

0 comments on commit 50ec9d9

Please sign in to comment.