diff --git a/CHANGELOG.md b/CHANGELOG.md index b0eb0f9c..4ae4da13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - Methods that are implemented but not included in the pooler interface (#395). - Implemented stringer methods for pool.Role (#405). - Support the IPROTO_INSERT_ARROW request (#399). +- A simple implementation of using the box interface was written (#410). ### Changed diff --git a/box/box.go b/box/box.go new file mode 100644 index 00000000..443b95a6 --- /dev/null +++ b/box/box.go @@ -0,0 +1,24 @@ +package box + +import ( + "github.com/tarantool/go-tarantool/v2" +) + +// Box defines an interface for interacting with a Tarantool instance. +// It includes the Info method, which retrieves instance information. +type Box interface { + Info() (Info, error) // Retrieves detailed information about the Tarantool instance. +} + +// box is a concrete implementation of the Box interface. +// It holds a connection to the Tarantool instance via the Doer interface. +type box struct { + conn tarantool.Doer // Connection interface for interacting with Tarantool. +} + +// By returns a new instance of the box structure, which implements the Box interface. +func By(conn tarantool.Doer) Box { + return &box{ + conn: conn, // Assigns the provided Tarantool connection. + } +} diff --git a/box/box_test.go b/box/box_test.go new file mode 100644 index 00000000..8b5f62c1 --- /dev/null +++ b/box/box_test.go @@ -0,0 +1,27 @@ +package box_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/tarantool/go-tarantool/v2/box" +) + +func TestBy(t *testing.T) { + // We expect a panic because we are passing a nil connection (nil Doer) to the By function. + // The library does not control this zone, and the nil connection would cause a runtime error + // when we attempt to call methods (like Info) on it. This test ensures that such an invalid state + // is correctly handled by causing a panic, as it's outside of the library's responsibility. + require.Panics(t, func() { + // Create a box instance with a nil connection. This should lead to a panic later. + b := box.By(nil) + + // Ensure the box instance is not nil (which it shouldn't be), but this is not meaningful + // since we will panic when we call the Info method with the nil connection. + require.NotNil(t, b) + + // Calling Info on a box with a nil connection will result in a panic, since the underlying + // connection (Doer) cannot perform the requested action (it's nil). + _, _ = b.Info() + }) +} diff --git a/box/info.go b/box/info.go new file mode 100644 index 00000000..5693f1f6 --- /dev/null +++ b/box/info.go @@ -0,0 +1,40 @@ +package box + +import "github.com/tarantool/go-tarantool/v2" + +// ClusterInfo represents information about the cluster. +// It contains the unique identifier (UUID) of the cluster. +type ClusterInfo struct { + UUID string `msgpack:"uuid"` +} + +// Info represents detailed information about the Tarantool instance. +// It includes version, node ID, read-only status, process ID, cluster information, and more. +type Info struct { + Version string `msgpack:"version"` // The version of the Tarantool instance. + ID *int `msgpack:"id"` // The node ID (nullable). + RO bool `msgpack:"ro"` // Read-only status of the instance. + UUID string `msgpack:"uuid"` // Unique identifier of the instance. + PID int `msgpack:"pid"` // Process ID of the instance. + Status string `msgpack:"status"` // Current status of the instance (e.g., running, unconfigured). + Lsn uint64 `msgpack:"lsn"` // Log sequence number of the instance. + Cluster ClusterInfo `msgpack:"cluster"` // Cluster information, including cluster UUID. +} + +// Info retrieves the current information of the Tarantool instance. +// It calls the "box.info" function and parses the result into the Info structure. +func (b *box) Info() (Info, error) { + var info Info + + // Call "box.info" to get instance information from Tarantool. + fut := b.conn.Do(tarantool.NewCallRequest("box.info")) + + // Parse the result into the Info structure. + err := fut.GetTyped(&[]interface{}{&info}) + if err != nil { + return Info{}, err + } + + // Return the parsed info and any potential error. + return info, err +} diff --git a/box/tarantool_test.go b/box/tarantool_test.go new file mode 100644 index 00000000..1f7f5db7 --- /dev/null +++ b/box/tarantool_test.go @@ -0,0 +1,73 @@ +package box_test + +import ( + "context" + "log" + "os" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "github.com/tarantool/go-tarantool/v2" + "github.com/tarantool/go-tarantool/v2/box" + "github.com/tarantool/go-tarantool/v2/test_helpers" +) + +var server = "127.0.0.1:3014" +var dialer = tarantool.NetDialer{ + Address: server, + User: "test", + Password: "test", +} + +func validateUUID(t *testing.T, u string) { + var err error + + _, err = uuid.Parse(u) + + require.NoError(t, err) +} + +func TestBox_Info(t *testing.T) { + ctx := context.TODO() + + conn, err := tarantool.Connect(ctx, dialer, tarantool.Opts{}) + require.NoError(t, err) + + info, err := box.By(conn).Info() + require.NoError(t, err) + + // check all fields run correctly + validateUUID(t, info.UUID) + validateUUID(t, info.Cluster.UUID) + + require.NotEmpty(t, info.Version) + // check that pid parsed correctly + require.NotEqual(t, info.PID, 0) + +} + +func runTestMain(m *testing.M) int { + instance, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + Dialer: dialer, + InitScript: "config.lua", + Listen: server, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 10, + RetryTimeout: 500 * time.Millisecond, + }) + defer test_helpers.StopTarantoolWithCleanup(instance) + + if err != nil { + log.Printf("Failed to prepare test Tarantool: %s", err) + return 1 + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +}