583 lines
24 KiB
Go
583 lines
24 KiB
Go
/*
|
|
Copyright 2016 The Rook Authors. All rights reserved.
|
|
|
|
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 api
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
|
|
etcd "github.com/coreos/etcd/client"
|
|
ctx "golang.org/x/net/context"
|
|
|
|
ceph "github.com/rook/rook/pkg/cephmgr/client"
|
|
testceph "github.com/rook/rook/pkg/cephmgr/client/test"
|
|
"github.com/rook/rook/pkg/cephmgr/test"
|
|
"github.com/rook/rook/pkg/clusterd"
|
|
"github.com/rook/rook/pkg/clusterd/inventory"
|
|
"github.com/rook/rook/pkg/util"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
const (
|
|
SuccessGetPoolRBDResponse = `{"pool":"rbd","pool_id":0,"size":1}{"pool":"rbd","pool_id":0,"min_size":1}{"pool":"rbd","pool_id":0,"crash_replay_interval":0}{"pool":"rbd","pool_id":0,"pg_num":2048}{"pool":"rbd","pool_id":0,"pgp_num":2048}{"pool":"rbd","pool_id":0,"crush_ruleset":0}{"pool":"rbd","pool_id":0,"hashpspool":"true"}{"pool":"rbd","pool_id":0,"nodelete":"false"}{"pool":"rbd","pool_id":0,"nopgchange":"false"}{"pool":"rbd","pool_id":0,"nosizechange":"false"}{"pool":"rbd","pool_id":0,"write_fadvise_dontneed":"false"}{"pool":"rbd","pool_id":0,"noscrub":"false"}{"pool":"rbd","pool_id":0,"nodeep-scrub":"false"}{"pool":"rbd","pool_id":0,"use_gmt_hitset":true}{"pool":"rbd","pool_id":0,"auid":0}{"pool":"rbd","pool_id":0,"min_write_recency_for_promote":0}{"pool":"rbd","pool_id":0,"fast_read":0}{"pool":"rbd","pool_id":0}{"pool":"rbd","pool_id":0}{"pool":"rbd","pool_id":0}{"pool":"rbd","pool_id":0}{"pool":"rbd","pool_id":0}{"pool":"rbd","pool_id":0}`
|
|
SuccessGetPoolECPool1Response = `{"pool":"ecPool1","pool_id":1,"size":3}{"pool":"ecPool1","pool_id":1,"min_size":3}{"pool":"ecPool1","pool_id":1,"crash_replay_interval":0}{"pool":"ecPool1","pool_id":1,"pg_num":100}{"pool":"ecPool1","pool_id":1,"pgp_num":100}{"pool":"ecPool1","pool_id":1,"crush_ruleset":1}{"pool":"ecPool1","pool_id":1,"hashpspool":"true"}{"pool":"ecPool1","pool_id":1,"nodelete":"false"}{"pool":"ecPool1","pool_id":1,"nopgchange":"false"}{"pool":"ecPool1","pool_id":1,"nosizechange":"false"}{"pool":"ecPool1","pool_id":1,"write_fadvise_dontneed":"false"}{"pool":"ecPool1","pool_id":1,"noscrub":"false"}{"pool":"ecPool1","pool_id":1,"nodeep-scrub":"false"}{"pool":"ecPool1","pool_id":1,"use_gmt_hitset":true}{"pool":"ecPool1","pool_id":1,"auid":0}{"pool":"ecPool1","pool_id":1,"erasure_code_profile":"ecPool1_ecprofile"}{"pool":"ecPool1","pool_id":1,"min_write_recency_for_promote":0}{"pool":"ecPool1","pool_id":1,"fast_read":0}{"pool":"ecPool1","pool_id":1}{"pool":"ecPool1","pool_id":1}{"pool":"ecPool1","pool_id":1}{"pool":"ecPool1","pool_id":1}{"pool":"ecPool1","pool_id":1}{"pool":"ecPool1","pool_id":1}`
|
|
)
|
|
|
|
func TestAddRemoveDeviceHandler(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
etcdClient.SetValue("/rook/nodes/config/123/disks/foo/uuid", "12345")
|
|
|
|
req, err := http.NewRequest("POST", "http://10.0.0.100/device", strings.NewReader(`{"name":"foo"}`))
|
|
assert.Nil(t, err)
|
|
|
|
w := httptest.NewRecorder()
|
|
h := NewHandler(context, nil, nil)
|
|
|
|
// missing the node id
|
|
h.AddDevice(w, req)
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
// successful request
|
|
req, err = http.NewRequest("POST", "http://10.0.0.100/device", strings.NewReader(`{"name":"foo","nodeId":"123"}`))
|
|
assert.Nil(t, err)
|
|
h = NewHandler(context, nil, nil)
|
|
w = httptest.NewRecorder()
|
|
h.AddDevice(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
devices := etcdClient.GetChildDirs("/rook/services/ceph/osd/desired/123/device")
|
|
assert.Equal(t, 1, devices.Count())
|
|
assert.True(t, devices.Contains("foo"))
|
|
|
|
// remove the device
|
|
req, err = http.NewRequest("POST", "http://10.0.0.100/device/remove", strings.NewReader(`{"name":"foo","nodeId":"123"}`))
|
|
assert.Nil(t, err)
|
|
h = NewHandler(context, nil, nil)
|
|
w = httptest.NewRecorder()
|
|
h.RemoveDevice(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// check the desired state
|
|
devices = etcdClient.GetChildDirs("/rook/services/ceph/osd/desired/123/device")
|
|
assert.Equal(t, 0, devices.Count())
|
|
}
|
|
|
|
func TestRemoveDeviceHandler(t *testing.T) {
|
|
}
|
|
|
|
func TestGetNodesHandler(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("GET", "http://10.0.0.100/node", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
etcdClient.SetValue("/rook/services/ceph/name", "cluster5")
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
h := NewHandler(context, &test.MockConnectionFactory{}, cephFactory)
|
|
|
|
// no nodes discovered, should return empty set
|
|
h.GetNodes(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, `[]`, w.Body.String())
|
|
|
|
// set up a discovered node in etcd
|
|
inventory.SetIPAddress(etcdClient, "node1", "1.2.3.4", "10.0.0.11")
|
|
inventory.SetLocation(etcdClient, "node1", "root=default,dc=datacenter1")
|
|
nodeConfigKey := path.Join(inventory.NodesConfigKey, "node1")
|
|
etcdClient.CreateDir(nodeConfigKey)
|
|
inventory.TestSetDiskInfo(etcdClient, nodeConfigKey, "sda", "123d4869-29ee-4bfd-bf21-dfd597bd222e",
|
|
100, true, false, "btrfs", "/mnt/abc", inventory.Disk, "", false)
|
|
inventory.TestSetDiskInfo(etcdClient, nodeConfigKey, "sdb", "321d4869-29ee-4bfd-bf21-dfd597bdffff",
|
|
50, false, false, "ext4", "/mnt/def", inventory.Disk, "", false)
|
|
appliedOSDKey := "/rook/services/ceph/osd/applied/node1"
|
|
etcdClient.SetValue(path.Join(appliedOSDKey, "12", "disk-uuid"), "123d4869-29ee-4bfd-bf21-dfd597bd222e")
|
|
etcdClient.SetValue(path.Join(appliedOSDKey, "13", "disk-uuid"), "321d4869-29ee-4bfd-bf21-dfd597bdffff")
|
|
|
|
// since a node exists (with storage), it should be returned now
|
|
w = httptest.NewRecorder()
|
|
h.GetNodes(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "[{\"nodeId\":\"node1\",\"clusterName\":\"cluster5\",\"publicIp\":\"1.2.3.4\",\"privateIp\":\"10.0.0.11\",\"storage\":150,\"lastUpdated\":31536000000000000,\"state\":1,\"location\":\"root=default,dc=datacenter1\"}]",
|
|
w.Body.String())
|
|
}
|
|
|
|
func TestGetNodesHandlerFailure(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
req, err := http.NewRequest("GET", "http://10.0.0.100/node", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
// failure during node lookup, should return an error status code
|
|
etcdClient.MockGet = func(context ctx.Context, key string, opts *etcd.GetOptions) (*etcd.Response, error) {
|
|
return nil, fmt.Errorf("mock etcd GET error")
|
|
}
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
h := NewHandler(context, &test.MockConnectionFactory{}, cephFactory)
|
|
h.GetNodes(w, req)
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.Equal(t, ``, w.Body.String())
|
|
}
|
|
|
|
func TestGetMonsHandler(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
|
|
req, err := http.NewRequest("GET", "http://10.0.0.100/mon", nil)
|
|
assert.Nil(t, err)
|
|
|
|
// first return no mons
|
|
w := httptest.NewRecorder()
|
|
|
|
// no mons will be returned, should be empty output
|
|
h := NewHandler(context, &test.MockConnectionFactory{}, cephFactory)
|
|
h.GetMonitors(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, `[]`, w.Body.String())
|
|
|
|
// now return some monitors from etcd
|
|
key := "/rook/services/ceph/monitor/desired/a"
|
|
etcdClient.SetValue(path.Join(key, "id"), "mon0")
|
|
etcdClient.SetValue(path.Join(key, "ipaddress"), "1.2.3.4")
|
|
etcdClient.SetValue(path.Join(key, "port"), "8765")
|
|
|
|
// monitors should be returned now, verify the output
|
|
w = httptest.NewRecorder()
|
|
h = NewHandler(context, &test.MockConnectionFactory{}, cephFactory)
|
|
h.GetMonitors(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "{\"status\":{\"quorum\":[0],\"monmap\":{\"mons\":[{\"name\":\"mon0\",\"rank\":0,\"addr\":\"10.37.129.87:6790\"}]}},\"desired\":[{\"name\":\"mon0\",\"endpoint\":\"1.2.3.4:8765\"}]}", w.Body.String())
|
|
}
|
|
|
|
func TestGetPoolsHandler(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("GET", "http://10.0.0.100/pool", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// first return no storage pools
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockMonCommand: func(args []byte) (buffer []byte, info string, err error) {
|
|
return []byte(`[]`), "", nil
|
|
},
|
|
}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
|
|
// no storage pools will be returned, should be empty output
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
h.GetPools(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, `[]`, w.Body.String())
|
|
|
|
// now return some storage pools from the ceph connection
|
|
w = httptest.NewRecorder()
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockMonCommand: func(args []byte) (buffer []byte, info string, err error) {
|
|
switch {
|
|
case strings.Index(string(args), "osd lspools") != -1:
|
|
return []byte(`[{"poolnum":0,"poolname":"rbd"},{"poolnum":1,"poolname":"ecPool1"}]`), "info", nil
|
|
case strings.Index(string(args), "osd pool get") != -1:
|
|
if strings.Index(string(args), "rbd") != -1 {
|
|
return []byte(SuccessGetPoolRBDResponse), "info", nil
|
|
} else if strings.Index(string(args), "ecPool1") != -1 {
|
|
return []byte(SuccessGetPoolECPool1Response), "info", nil
|
|
}
|
|
case strings.Index(string(args), "osd erasure-code-profile ls") != -1:
|
|
return []byte(`["default","ecPool1_ecprofile"]`), "info", nil
|
|
case strings.Index(string(args), "osd erasure-code-profile get") != -1:
|
|
if strings.Index(string(args), "default") != -1 {
|
|
return []byte(`{"k":"2","m":"1","plugin":"jerasure","technique":"reed_sol_van"}`), "info", nil
|
|
} else if strings.Index(string(args), "ecPool1") != -1 {
|
|
return []byte(`{"jerasure-per-chunk-alignment":"false","k":"2","m":"1","plugin":"jerasure","ruleset-failure-domain":"osd","ruleset-root":"default","technique":"reed_sol_van","w":"8"}`), "info", nil
|
|
}
|
|
}
|
|
return nil, "", fmt.Errorf("unexpected mon_command '%s'", string(args))
|
|
},
|
|
}
|
|
|
|
// storage pools should be returned now, verify the output
|
|
h = NewHandler(context, connFactory, cephFactory)
|
|
h.GetPools(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "[{\"poolName\":\"rbd\",\"poolNum\":0,\"type\":0,\"replicationConfig\":{\"size\":1},\"erasureCodedConfig\":{\"dataChunkCount\":0,\"codingChunkCount\":0,\"algorithm\":\"\"}},{\"poolName\":\"ecPool1\",\"poolNum\":1,\"type\":1,\"replicationConfig\":{\"size\":0},\"erasureCodedConfig\":{\"dataChunkCount\":2,\"codingChunkCount\":1,\"algorithm\":\"jerasure::reed_sol_van\"}}]", w.Body.String())
|
|
}
|
|
|
|
func TestGetPoolsHandlerFailure(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("GET", "http://10.0.0.100/pool", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// encounter an error during GetPools
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return nil, fmt.Errorf("mock error for connect as admin")
|
|
}
|
|
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
h.GetPools(w, req)
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.Equal(t, "", w.Body.String())
|
|
}
|
|
|
|
func TestCreatePoolHandler(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("POST", "http://10.0.0.100/pool",
|
|
strings.NewReader(`{"poolName":"ecPool1","poolNum":0,"type":1,"replicationConfig":{"size":0},"erasureCodedConfig":{"dataChunkCount":2,"codingChunkCount":1,"algorithm":""}}`))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockMonCommand: func(args []byte) (buffer []byte, info string, err error) {
|
|
switch {
|
|
case strings.Index(string(args), "osd erasure-code-profile get") != -1:
|
|
if strings.Index(string(args), "default") != -1 {
|
|
return []byte(`{"k":"2","m":"1","plugin":"jerasure","technique":"reed_sol_van"}`), "info", nil
|
|
}
|
|
case strings.Index(string(args), "osd erasure-code-profile set") != -1:
|
|
return []byte(""), "", nil
|
|
case strings.Index(string(args), "osd pool create") != -1:
|
|
return []byte(""), "pool 'ecPool1' created", nil
|
|
|
|
}
|
|
return nil, "", fmt.Errorf("unexpected mon_command '%s'", string(args))
|
|
},
|
|
}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
|
|
h.CreatePool(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "pool 'ecPool1' created", w.Body.String())
|
|
}
|
|
|
|
func TestCreatePoolHandlerFailure(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("POST", "http://10.0.0.100/pool", strings.NewReader(`{"poolname":"pool1"}`))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockMonCommand: func(args []byte) (buffer []byte, info string, err error) {
|
|
errMsg := "mock failure to create pool1"
|
|
return []byte(""), errMsg, fmt.Errorf(errMsg)
|
|
},
|
|
}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
|
|
h.CreatePool(w, req)
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.Equal(t, "", w.Body.String())
|
|
}
|
|
|
|
func TestGetImagesHandler(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("GET", "http://10.0.0.100/image", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// first return no storage pools, which means no images will be returned either
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockMonCommand: func(args []byte) (buffer []byte, info string, err error) {
|
|
return []byte(`[]`), "", nil
|
|
},
|
|
}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
|
|
// no images will be returned, should be empty output
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
h.GetImages(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, `[]`, w.Body.String())
|
|
|
|
// now return some storage pools and images from the ceph connection
|
|
w = httptest.NewRecorder()
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockMonCommand: func(args []byte) (buffer []byte, info string, err error) {
|
|
switch {
|
|
case strings.Index(string(args), "osd lspools") != -1:
|
|
return []byte(`[{"poolnum":0,"poolname":"pool0"},{"poolnum":1,"poolname":"pool1"}]`), "info", nil
|
|
}
|
|
return nil, "", fmt.Errorf("unexpected mon_command '%s'", string(args))
|
|
},
|
|
MockOpenIOContext: func(pool string) (ceph.IOContext, error) {
|
|
return &testceph.MockIOContext{
|
|
MockGetImageNames: func() (names []string, err error) {
|
|
return []string{fmt.Sprintf("image1 - %s", pool)}, nil
|
|
},
|
|
MockGetImage: func(name string) ceph.Image {
|
|
return &testceph.MockImage{
|
|
MockName: name,
|
|
MockStat: func() (info *ceph.ImageInfo, err error) {
|
|
return &ceph.ImageInfo{
|
|
Size: 100,
|
|
}, nil
|
|
},
|
|
}
|
|
},
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
// verify that the expected images are returned
|
|
h = NewHandler(context, connFactory, cephFactory)
|
|
h.GetImages(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "[{\"imageName\":\"image1 - pool0\",\"poolName\":\"pool0\",\"size\":100,\"device\":\"\",\"mountPoint\":\"\"},{\"imageName\":\"image1 - pool1\",\"poolName\":\"pool1\",\"size\":100,\"device\":\"\",\"mountPoint\":\"\"}]", w.Body.String())
|
|
}
|
|
|
|
func TestGetImagesHandlerFailure(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("GET", "http://10.0.0.100/image", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
cephFactory := &testceph.MockConnectionFactory{
|
|
Fsid: "myfsid",
|
|
SecretKey: "mykey",
|
|
Conn: &testceph.MockConnection{
|
|
MockMonCommand: func(args []byte) (buffer []byte, info string, err error) {
|
|
return nil, "mock error", fmt.Errorf("mock error for list pools")
|
|
},
|
|
},
|
|
}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
|
|
// GetImages should fail due to the mocked error for listing pools
|
|
w := httptest.NewRecorder()
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
h.GetImages(w, req)
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.Equal(t, ``, w.Body.String())
|
|
}
|
|
|
|
func TestCreateImageHandler(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("POST", "http://10.0.0.100/image", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
|
|
// image is missing from request body, should be bad request
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
h.CreateImage(w, req)
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
assert.Equal(t, ``, w.Body.String())
|
|
|
|
// request body exists but it's bad json, should be bad request
|
|
req, err = http.NewRequest("POST", "http://10.0.0.100/image", strings.NewReader(`bad json`))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
w = httptest.NewRecorder()
|
|
h = NewHandler(context, connFactory, cephFactory)
|
|
h.CreateImage(w, req)
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
assert.Equal(t, ``, w.Body.String())
|
|
|
|
// missing fields for the image passed via request body, should be bad request
|
|
req, err = http.NewRequest("POST", "http://10.0.0.100/image", strings.NewReader(`{"imageName":"myImage1"}`))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
w = httptest.NewRecorder()
|
|
h = NewHandler(context, connFactory, cephFactory)
|
|
h.CreateImage(w, req)
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
assert.Equal(t, ``, w.Body.String())
|
|
|
|
// well formed successful request to create an image
|
|
req, err = http.NewRequest("POST", "http://10.0.0.100/image", strings.NewReader(`{"imageName":"myImage1","poolName":"myPool1","size":1024}`))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockOpenIOContext: func(pool string) (ceph.IOContext, error) {
|
|
return &testceph.MockIOContext{
|
|
MockCreateImage: func(name string, size uint64, order int, args ...uint64) (image ceph.Image, err error) {
|
|
return &testceph.MockImage{MockName: name}, nil
|
|
},
|
|
}, nil
|
|
},
|
|
}
|
|
w = httptest.NewRecorder()
|
|
h = NewHandler(context, connFactory, cephFactory)
|
|
h.CreateImage(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, `succeeded created image myImage1`, w.Body.String())
|
|
}
|
|
|
|
func TestCreateImageHandlerFailure(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("POST", "http://10.0.0.100/image", strings.NewReader(`{"imageName":"myImage1","poolName":"myPool1","size":1024}`))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockOpenIOContext: func(pool string) (ceph.IOContext, error) {
|
|
return &testceph.MockIOContext{
|
|
// mock a failure in the create image call
|
|
MockCreateImage: func(name string, size uint64, order int, args ...uint64) (image ceph.Image, err error) {
|
|
return &testceph.MockImage{}, fmt.Errorf("mock failure to create image")
|
|
},
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
// create image request should fail while creating the image
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
h.CreateImage(w, req)
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.Equal(t, ``, w.Body.String())
|
|
}
|
|
|
|
func TestGetImageMapInfoHandler(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("POST", "http://10.0.0.100/image/mapinfo", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
cephFactory.Conn = &testceph.MockConnection{
|
|
MockMonCommand: func(args []byte) (buffer []byte, info string, err error) {
|
|
switch {
|
|
case strings.Index(string(args), "mon_status") != -1:
|
|
return []byte(testceph.SuccessfulMonStatusResponse), "info", nil
|
|
case strings.Index(string(args), "auth get-key") != -1:
|
|
return []byte(`{"key":"AQBsCv1X5oD9GhAARHVU9N+kFRWDjyLA1dqzIg=="}`), "info", nil
|
|
}
|
|
return nil, "", nil
|
|
},
|
|
}
|
|
|
|
// get image map info and verify the response
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
h.GetImageMapInfo(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "{\"monAddresses\":[\"10.37.129.87:6790\"],\"userName\":\"admin\",\"secretKey\":\"AQBsCv1X5oD9GhAARHVU9N+kFRWDjyLA1dqzIg==\"}", w.Body.String())
|
|
}
|
|
|
|
func TestGetImageMapInfoHandlerFailure(t *testing.T) {
|
|
etcdClient := util.NewMockEtcdClient()
|
|
context := &clusterd.Context{EtcdClient: etcdClient}
|
|
|
|
req, err := http.NewRequest("POST", "http://10.0.0.100/image/mapinfo", nil)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
w := httptest.NewRecorder()
|
|
cephFactory := &testceph.MockConnectionFactory{Fsid: "myfsid", SecretKey: "mykey"}
|
|
connFactory := &test.MockConnectionFactory{}
|
|
connFactory.MockConnectAsAdmin = func(context *clusterd.Context, cephFactory ceph.ConnectionFactory) (ceph.Connection, error) {
|
|
return cephFactory.NewConnWithClusterAndUser("mycluster", "admin")
|
|
}
|
|
cephFactory.Conn = &testceph.MockConnection{}
|
|
|
|
// get image map info should fail becuase there's no mock response set up for auth get-key
|
|
h := NewHandler(context, connFactory, cephFactory)
|
|
h.GetImageMapInfo(w, req)
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.Equal(t, "", w.Body.String())
|
|
}
|