From 79dfe8e63192c7a0f75e54c2f140b7ca7f863526 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Thu, 9 Apr 2020 07:52:39 -0400 Subject: [PATCH] rados: move *Command functions into a separate file Organize the rados package a bit more by moving functions that wrap rados_xyz_command functions into a dedicated file. Move related test to a separate file as well. Signed-off-by: John Mulligan --- rados/command.go | 128 ++++++++++++++++++++++++++++++++++++++++++ rados/command_test.go | 89 +++++++++++++++++++++++++++++ rados/conn.go | 118 -------------------------------------- rados/rados_test.go | 81 -------------------------- 4 files changed, 217 insertions(+), 199 deletions(-) create mode 100644 rados/command.go create mode 100644 rados/command_test.go diff --git a/rados/command.go b/rados/command.go new file mode 100644 index 0000000..805bd38 --- /dev/null +++ b/rados/command.go @@ -0,0 +1,128 @@ +package rados + +// #cgo LDFLAGS: -lrados +// #include +// #include +import "C" + +import ( + "unsafe" +) + +// MonCommand sends a command to one of the monitors +func (c *Conn) MonCommand(args []byte) (buffer []byte, info string, err error) { + return c.monCommand(args, nil) +} + +// MonCommandWithInputBuffer sends a command to one of the monitors, with an input buffer +func (c *Conn) MonCommandWithInputBuffer(args, inputBuffer []byte) (buffer []byte, info string, err error) { + return c.monCommand(args, inputBuffer) +} + +func (c *Conn) monCommand(args, inputBuffer []byte) (buffer []byte, info string, err error) { + argv := C.CString(string(args)) + defer C.free(unsafe.Pointer(argv)) + + var ( + outs, outbuf *C.char + outslen, outbuflen C.size_t + ) + inbuf := C.CString(string(inputBuffer)) + inbufLen := len(inputBuffer) + defer C.free(unsafe.Pointer(inbuf)) + + ret := C.rados_mon_command(c.cluster, + &argv, 1, + inbuf, // bulk input (e.g. crush map) + C.size_t(inbufLen), // length inbuf + &outbuf, // buffer + &outbuflen, // buffer length + &outs, // status string + &outslen) + + if outslen > 0 { + info = C.GoStringN(outs, C.int(outslen)) + C.free(unsafe.Pointer(outs)) + } + if outbuflen > 0 { + buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen)) + C.free(unsafe.Pointer(outbuf)) + } + if ret != 0 { + err = getError(ret) + return nil, info, err + } + + return +} + +// PGCommand sends a command to one of the PGs +// +// Implements: +// int rados_pg_command(rados_t cluster, const char *pgstr, +// const char **cmd, size_t cmdlen, +// const char *inbuf, size_t inbuflen, +// char **outbuf, size_t *outbuflen, +// char **outs, size_t *outslen); +func (c *Conn) PGCommand(pgid []byte, args [][]byte) (buffer []byte, info string, err error) { + return c.pgCommand(pgid, args, nil) +} + +// PGCommandWithInputBuffer sends a command to one of the PGs, with an input buffer +// +// Implements: +// int rados_pg_command(rados_t cluster, const char *pgstr, +// const char **cmd, size_t cmdlen, +// const char *inbuf, size_t inbuflen, +// char **outbuf, size_t *outbuflen, +// char **outs, size_t *outslen); +func (c *Conn) PGCommandWithInputBuffer(pgid []byte, args [][]byte, inputBuffer []byte) (buffer []byte, info string, err error) { + return c.pgCommand(pgid, args, inputBuffer) +} + +func (c *Conn) pgCommand(pgid []byte, args [][]byte, inputBuffer []byte) (buffer []byte, info string, err error) { + name := C.CString(string(pgid)) + defer C.free(unsafe.Pointer(name)) + + argc := len(args) + argv := make([]*C.char, argc) + + for i, arg := range args { + argv[i] = C.CString(string(arg)) + defer C.free(unsafe.Pointer(argv[i])) + } + + var ( + outs, outbuf *C.char + outslen, outbuflen C.size_t + ) + inbuf := C.CString(string(inputBuffer)) + inbufLen := len(inputBuffer) + defer C.free(unsafe.Pointer(inbuf)) + + ret := C.rados_pg_command(c.cluster, + name, + &argv[0], + C.size_t(argc), + inbuf, // bulk input + C.size_t(inbufLen), // length inbuf + &outbuf, // buffer + &outbuflen, // buffer length + &outs, // status string + &outslen) + + if outslen > 0 { + info = C.GoStringN(outs, C.int(outslen)) + C.free(unsafe.Pointer(outs)) + } + if outbuflen > 0 { + buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen)) + C.free(unsafe.Pointer(outbuf)) + } + if ret != 0 { + err = getError(ret) + return nil, info, err + } + + return +} diff --git a/rados/command_test.go b/rados/command_test.go new file mode 100644 index 0000000..6b2100b --- /dev/null +++ b/rados/command_test.go @@ -0,0 +1,89 @@ +package rados + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/stretchr/testify/assert" +) + +func (suite *RadosTestSuite) TestMonCommand() { + suite.SetupConnection() + + command, err := json.Marshal( + map[string]string{"prefix": "df", "format": "json"}) + assert.NoError(suite.T(), err) + + buf, info, err := suite.conn.MonCommand(command) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), info, "") + + var message map[string]interface{} + err = json.Unmarshal(buf, &message) + assert.NoError(suite.T(), err) +} + +// NB: ceph octopus appears to be stricter about the formatting of the keyring +// and now rejects whitespace that older versions did not have a problem with. +const clientKeyFormat = ` +[%s] +key = AQD4PGNXBZJNHhAA582iUgxe9DsN+MqFN4Z6Jw== +` + +func (suite *RadosTestSuite) TestMonCommandWithInputBuffer() { + suite.SetupConnection() + + entity := fmt.Sprintf("client.testMonCmdUser%d", time.Now().UnixNano()) + + // first add the new test user, specifying its key in the input buffer + command, err := json.Marshal(map[string]interface{}{ + "prefix": "auth add", + "format": "json", + "entity": entity, + }) + assert.NoError(suite.T(), err) + + client_key := fmt.Sprintf(clientKeyFormat, entity) + + inbuf := []byte(client_key) + + buf, info, err := suite.conn.MonCommandWithInputBuffer(command, inbuf) + assert.NoError(suite.T(), err) + expected_info := fmt.Sprintf("added key for %s", entity) + assert.Equal(suite.T(), expected_info, info) + assert.Equal(suite.T(), "", string(buf[:])) + + // get the key and verify that it's what we previously set + command, err = json.Marshal(map[string]interface{}{ + "prefix": "auth get-key", + "format": "json", + "entity": entity, + }) + assert.NoError(suite.T(), err) + + buf, info, err = suite.conn.MonCommand(command) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), "", info) + assert.Equal(suite.T(), + `{"key":"AQD4PGNXBZJNHhAA582iUgxe9DsN+MqFN4Z6Jw=="}`, + string(buf[:])) +} + +func (suite *RadosTestSuite) TestPGCommand() { + suite.SetupConnection() + + pgid := "1.2" + + command, err := json.Marshal( + map[string]string{"prefix": "query", "pgid": pgid, "format": "json"}) + assert.NoError(suite.T(), err) + + buf, info, err := suite.conn.PGCommand([]byte(pgid), [][]byte{[]byte(command)}) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), info, "") + + var message map[string]interface{} + err = json.Unmarshal(buf, &message) + assert.NoError(suite.T(), err) +} diff --git a/rados/conn.go b/rados/conn.go index 16ca738..d1040f5 100644 --- a/rados/conn.go +++ b/rados/conn.go @@ -291,121 +291,3 @@ func (c *Conn) GetPoolByID(id int64) (string, error) { } return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil } - -// MonCommand sends a command to one of the monitors -func (c *Conn) MonCommand(args []byte) (buffer []byte, info string, err error) { - return c.monCommand(args, nil) -} - -// MonCommandWithInputBuffer sends a command to one of the monitors, with an input buffer -func (c *Conn) MonCommandWithInputBuffer(args, inputBuffer []byte) (buffer []byte, info string, err error) { - return c.monCommand(args, inputBuffer) -} - -func (c *Conn) monCommand(args, inputBuffer []byte) (buffer []byte, info string, err error) { - argv := C.CString(string(args)) - defer C.free(unsafe.Pointer(argv)) - - var ( - outs, outbuf *C.char - outslen, outbuflen C.size_t - ) - inbuf := C.CString(string(inputBuffer)) - inbufLen := len(inputBuffer) - defer C.free(unsafe.Pointer(inbuf)) - - ret := C.rados_mon_command(c.cluster, - &argv, 1, - inbuf, // bulk input (e.g. crush map) - C.size_t(inbufLen), // length inbuf - &outbuf, // buffer - &outbuflen, // buffer length - &outs, // status string - &outslen) - - if outslen > 0 { - info = C.GoStringN(outs, C.int(outslen)) - C.free(unsafe.Pointer(outs)) - } - if outbuflen > 0 { - buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen)) - C.free(unsafe.Pointer(outbuf)) - } - if ret != 0 { - err = getError(ret) - return nil, info, err - } - - return -} - -// PGCommand sends a command to one of the PGs -// -// Implements: -// int rados_pg_command(rados_t cluster, const char *pgstr, -// const char **cmd, size_t cmdlen, -// const char *inbuf, size_t inbuflen, -// char **outbuf, size_t *outbuflen, -// char **outs, size_t *outslen); -func (c *Conn) PGCommand(pgid []byte, args [][]byte) (buffer []byte, info string, err error) { - return c.pgCommand(pgid, args, nil) -} - -// PGCommandWithInputBuffer sends a command to one of the PGs, with an input buffer -// -// Implements: -// int rados_pg_command(rados_t cluster, const char *pgstr, -// const char **cmd, size_t cmdlen, -// const char *inbuf, size_t inbuflen, -// char **outbuf, size_t *outbuflen, -// char **outs, size_t *outslen); -func (c *Conn) PGCommandWithInputBuffer(pgid []byte, args [][]byte, inputBuffer []byte) (buffer []byte, info string, err error) { - return c.pgCommand(pgid, args, inputBuffer) -} - -func (c *Conn) pgCommand(pgid []byte, args [][]byte, inputBuffer []byte) (buffer []byte, info string, err error) { - name := C.CString(string(pgid)) - defer C.free(unsafe.Pointer(name)) - - argc := len(args) - argv := make([]*C.char, argc) - - for i, arg := range args { - argv[i] = C.CString(string(arg)) - defer C.free(unsafe.Pointer(argv[i])) - } - - var ( - outs, outbuf *C.char - outslen, outbuflen C.size_t - ) - inbuf := C.CString(string(inputBuffer)) - inbufLen := len(inputBuffer) - defer C.free(unsafe.Pointer(inbuf)) - - ret := C.rados_pg_command(c.cluster, - name, - &argv[0], - C.size_t(argc), - inbuf, // bulk input - C.size_t(inbufLen), // length inbuf - &outbuf, // buffer - &outbuflen, // buffer length - &outs, // status string - &outslen) - - if outslen > 0 { - info = C.GoStringN(outs, C.int(outslen)) - C.free(unsafe.Pointer(outs)) - } - if outbuflen > 0 { - buffer = C.GoBytes(unsafe.Pointer(outbuf), C.int(outbuflen)) - C.free(unsafe.Pointer(outbuf)) - } - if ret != 0 { - err = getError(ret) - return nil, info, err - } - - return -} diff --git a/rados/rados_test.go b/rados/rados_test.go index fa95fc9..3499099 100644 --- a/rados/rados_test.go +++ b/rados/rados_test.go @@ -1,7 +1,6 @@ package rados import ( - "encoding/json" "fmt" "io" "io/ioutil" @@ -595,86 +594,6 @@ func (suite *RadosTestSuite) TestGetPoolName() { assert.Equal(suite.T(), name, suite.pool) } -func (suite *RadosTestSuite) TestMonCommand() { - suite.SetupConnection() - - command, err := json.Marshal( - map[string]string{"prefix": "df", "format": "json"}) - assert.NoError(suite.T(), err) - - buf, info, err := suite.conn.MonCommand(command) - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), info, "") - - var message map[string]interface{} - err = json.Unmarshal(buf, &message) - assert.NoError(suite.T(), err) -} - -// NB: ceph octopus appears to be stricter about the formatting of the keyring -// and now rejects whitespace that older versions did not have a problem with. -const clientKeyFormat = ` -[%s] -key = AQD4PGNXBZJNHhAA582iUgxe9DsN+MqFN4Z6Jw== -` - -func (suite *RadosTestSuite) TestMonCommandWithInputBuffer() { - suite.SetupConnection() - - entity := fmt.Sprintf("client.testMonCmdUser%d", time.Now().UnixNano()) - - // first add the new test user, specifying its key in the input buffer - command, err := json.Marshal(map[string]interface{}{ - "prefix": "auth add", - "format": "json", - "entity": entity, - }) - assert.NoError(suite.T(), err) - - client_key := fmt.Sprintf(clientKeyFormat, entity) - - inbuf := []byte(client_key) - - buf, info, err := suite.conn.MonCommandWithInputBuffer(command, inbuf) - assert.NoError(suite.T(), err) - expected_info := fmt.Sprintf("added key for %s", entity) - assert.Equal(suite.T(), expected_info, info) - assert.Equal(suite.T(), "", string(buf[:])) - - // get the key and verify that it's what we previously set - command, err = json.Marshal(map[string]interface{}{ - "prefix": "auth get-key", - "format": "json", - "entity": entity, - }) - assert.NoError(suite.T(), err) - - buf, info, err = suite.conn.MonCommand(command) - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), "", info) - assert.Equal(suite.T(), - `{"key":"AQD4PGNXBZJNHhAA582iUgxe9DsN+MqFN4Z6Jw=="}`, - string(buf[:])) -} - -func (suite *RadosTestSuite) TestPGCommand() { - suite.SetupConnection() - - pgid := "1.2" - - command, err := json.Marshal( - map[string]string{"prefix": "query", "pgid": pgid, "format": "json"}) - assert.NoError(suite.T(), err) - - buf, info, err := suite.conn.PGCommand([]byte(pgid), [][]byte{[]byte(command)}) - assert.NoError(suite.T(), err) - assert.Equal(suite.T(), info, "") - - var message map[string]interface{} - err = json.Unmarshal(buf, &message) - assert.NoError(suite.T(), err) -} - func (suite *RadosTestSuite) TestObjectListObjects() { suite.SetupConnection()