Skip to content

Commit

Permalink
feature/box: first box and box.info implementation
Browse files Browse the repository at this point in the history
I implemented the box interface for tarantool with a small number of fields, which in the future can be supplemented
  • Loading branch information
Maksim Konovalov committed Oct 19, 2024
1 parent 8cf8673 commit 5d9ad18
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
24 changes: 24 additions & 0 deletions box/box.go
Original file line number Diff line number Diff line change
@@ -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.
}
}
28 changes: 28 additions & 0 deletions box/box_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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()
})
}
48 changes: 48 additions & 0 deletions box/info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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 {
// The Version of the Tarantool instance.
Version string `msgpack:"version"`
// The node ID (nullable).
ID *int `msgpack:"id"`
// Read-only (RO) status of the instance.
RO bool `msgpack:"ro"`
// UUID - Unique identifier of the instance.
UUID string `msgpack:"uuid"`
// Process ID of the instance.
PID int `msgpack:"pid"`
// Status - Current status of the instance (e.g., running, unconfigured).
Status string `msgpack:"status"`
// LSN - Log sequence number of the instance.
LSN uint64 `msgpack:"lsn"`
// Cluster information, including cluster UUID.
Cluster ClusterInfo `msgpack:"cluster"`
}

// 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
}
65 changes: 65 additions & 0 deletions box/tarantool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
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:3013"
var dialer = tarantool.NetDialer{
Address: server,
User: "test",
Password: "test",
}

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
_, err = uuid.Parse(info.UUID)
require.NoErrorf(t, err, "validate instance uuid is valid")

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: "testdata/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)
}
13 changes: 13 additions & 0 deletions box/testdata/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- Do not set listen for now so connector won't be
-- able to send requests until everything is configured.
box.cfg{
work_dir = os.getenv("TEST_TNT_WORK_DIR"),
}

box.schema.user.create('test', { password = 'test' , if_not_exists = true })
box.schema.user.grant('test', 'execute', 'universe', nil, { if_not_exists = true })

-- Set listen only when every other thing is configured.
box.cfg{
listen = os.getenv("TEST_TNT_LISTEN"),
}

0 comments on commit 5d9ad18

Please sign in to comment.