From a1f1699f379d5faa93d4f54dc007c79ce654afba Mon Sep 17 00:00:00 2001 From: Paul Theunis Date: Mon, 18 Mar 2019 11:09:52 -0700 Subject: [PATCH] Add authentication to REST Calls for Migration. (#936) --- api/server/migrate.go | 94 ++++++++++++++++++----- api/server/migrate_test.go | 145 ++++++++++++++++++------------------ volume/drivers/fake/fake.go | 16 ++++ 3 files changed, 164 insertions(+), 91 deletions(-) diff --git a/api/server/migrate.go b/api/server/migrate.go index 38060d5be..f3c75d452 100644 --- a/api/server/migrate.go +++ b/api/server/migrate.go @@ -5,7 +5,6 @@ import ( "net/http" "github.com/libopenstorage/openstorage/api" - ost_errors "github.com/libopenstorage/openstorage/api/errors" ) func (vd *volAPI) cloudMigrateStart(w http.ResponseWriter, r *http.Request) { @@ -17,21 +16,52 @@ func (vd *volAPI) cloudMigrateStart(w http.ResponseWriter, r *http.Request) { return } - d, err := vd.getVolDriver(r) + // Get context with auth token + ctx, err := vd.annotateContext(r) if err != nil { - notFound(w, r) + vd.sendError(vd.name, method, w, err.Error(), http.StatusBadRequest) return } - response, err := d.CloudMigrateStart(startReq) + // Get gRPC connection + conn, err := vd.getConn() if err != nil { - if _, ok := err.(*ost_errors.ErrExists); ok { - w.WriteHeader(http.StatusConflict) - return + vd.sendError(vd.name, method, w, err.Error(), http.StatusInternalServerError) + return + } + + migrations := api.NewOpenStorageMigrateClient(conn) + migrateRequest := &api.SdkCloudMigrateStartRequest{ + ClusterId: startReq.ClusterId, + } + + switch startReq.Operation { + case api.CloudMigrate_MigrateCluster: + migrateRequest.Opt = &api.SdkCloudMigrateStartRequest_AllVolumes{ + AllVolumes: &api.SdkCloudMigrateStartRequest_MigrateAllVolumes{}, + } + case api.CloudMigrate_MigrateVolume: + migrateRequest.Opt = &api.SdkCloudMigrateStartRequest_Volume{ + Volume: &api.SdkCloudMigrateStartRequest_MigrateVolume{ + VolumeId: startReq.TargetId, + }, } + case api.CloudMigrate_MigrateVolumeGroup: + migrateRequest.Opt = &api.SdkCloudMigrateStartRequest_VolumeGroup{ + VolumeGroup: &api.SdkCloudMigrateStartRequest_MigrateVolumeGroup{ + GroupId: startReq.TargetId, + }, + } + } + + resp, err := migrations.Start(ctx, migrateRequest) + if err != nil { vd.sendError(method, startReq.TargetId, w, err.Error(), http.StatusInternalServerError) return } + response := &api.CloudMigrateStartResponse{ + TaskId: resp.GetResult().GetTaskId(), + } json.NewEncoder(w).Encode(response) } @@ -44,13 +74,26 @@ func (vd *volAPI) cloudMigrateCancel(w http.ResponseWriter, r *http.Request) { return } - d, err := vd.getVolDriver(r) + // Get context with auth token + ctx, err := vd.annotateContext(r) if err != nil { - notFound(w, r) + vd.sendError(vd.name, method, w, err.Error(), http.StatusBadRequest) return } - err = d.CloudMigrateCancel(cancelReq) + // Get gRPC connection + conn, err := vd.getConn() + if err != nil { + vd.sendError(vd.name, method, w, err.Error(), http.StatusInternalServerError) + return + } + + migrations := api.NewOpenStorageMigrateClient(conn) + migrateRequest := &api.SdkCloudMigrateCancelRequest{ + Request: cancelReq, + } + + _, err = migrations.Cancel(ctx, migrateRequest) if err != nil { vd.sendError(method, cancelReq.TaskId, w, err.Error(), http.StatusInternalServerError) return @@ -62,12 +105,6 @@ func (vd *volAPI) cloudMigrateStatus(w http.ResponseWriter, r *http.Request) { statusReq := &api.CloudMigrateStatusRequest{} method := "cloudMigrateState" - d, err := vd.getVolDriver(r) - if err != nil { - notFound(w, r) - return - } - // Use empty request if nothing was sent if r.ContentLength != 0 { if err := json.NewDecoder(r.Body).Decode(statusReq); err != nil { @@ -76,11 +113,34 @@ func (vd *volAPI) cloudMigrateStatus(w http.ResponseWriter, r *http.Request) { } } - statusResp, err := d.CloudMigrateStatus(statusReq) + // Get context with auth token + ctx, err := vd.annotateContext(r) + if err != nil { + vd.sendError(vd.name, method, w, err.Error(), http.StatusBadRequest) + return + } + + // Get gRPC connection + conn, err := vd.getConn() + if err != nil { + vd.sendError(vd.name, method, w, err.Error(), http.StatusInternalServerError) + return + } + + migrations := api.NewOpenStorageMigrateClient(conn) + migrateRequest := &api.SdkCloudMigrateStatusRequest{ + Request: statusReq, + } + + resp, err := migrations.Status(ctx, migrateRequest) if err != nil { vd.sendError(method, "", w, err.Error(), http.StatusInternalServerError) return } + statusResp := &api.CloudMigrateStatusResponse{ + Info: resp.GetResult().GetInfo(), + } + json.NewEncoder(w).Encode(statusResp) } diff --git a/api/server/migrate_test.go b/api/server/migrate_test.go index a973cedf0..cd49220fa 100644 --- a/api/server/migrate_test.go +++ b/api/server/migrate_test.go @@ -1,111 +1,108 @@ package server -/* - import ( - "fmt" - "testing" - + "context" "github.com/libopenstorage/openstorage/api" - client "github.com/libopenstorage/openstorage/api/client/volume" - "github.com/stretchr/testify/require" + volumeclient "github.com/libopenstorage/openstorage/api/client/volume" + "github.com/stretchr/testify/assert" + "testing" ) func TestMigrateStart(t *testing.T) { - ts, testVolDriver := testRestServer(t) + var err error + // Setup volume rest functions server + ts, testVolDriver := testRestServerSdk(t) defer ts.Close() defer testVolDriver.Stop() - cl, err := client.NewDriverClient(ts.URL, mockDriverName, "", mockDriverName) - require.NoError(t, err) + // get token + token, err := createToken("test", "system.admin", testSharedSecret) + assert.NoError(t, err) + + cl, err := volumeclient.NewAuthDriverClient(ts.URL, "fake", version, token, "", "fake") + assert.NoError(t, err) + + // Setup request + name := "myvol" + size := uint64(1234) + req := &api.VolumeCreateRequest{ + Locator: &api.VolumeLocator{Name: name}, + Source: &api.Source{}, + Spec: &api.VolumeSpec{ + HaLevel: 3, + Size: size, + Format: api.FSType_FS_TYPE_EXT4, + Shared: true, + }, + } + + // Create a volume client + driverclient := volumeclient.VolumeDriver(cl) + id, err := driverclient.Create(req.GetLocator(), req.GetSource(), req.GetSpec()) + assert.Nil(t, err) + assert.NotEmpty(t, id) goodRequest := &api.CloudMigrateStartRequest{ - Operation: api.CloudMigrate_MigrateCluster, + Operation: api.CloudMigrate_MigrateVolume, ClusterId: "clusterID", - TargetId: "goodVolumeID", + TargetId: id, } - badRequest := &api.CloudMigrateStartRequest{ - Operation: api.CloudMigrate_MigrateCluster, - ClusterId: "clusterID", - TargetId: "badVolumeID", - } - goodResponse := &api.CloudMigrateStartResponse{ - TaskId: "random-id", - } - testVolDriver.MockDriver().EXPECT().CloudMigrateStart(badRequest).Return(nil, fmt.Errorf("Volume not found")).Times(1) - testVolDriver.MockDriver().EXPECT().CloudMigrateStart(goodRequest).Return(goodResponse, nil).Times(1) // Start Migrate - resp, err := client.VolumeDriver(cl).CloudMigrateStart(badRequest) - require.Error(t, err) - require.Nil(t, resp) - require.Contains(t, err.Error(), "Volume not found") - resp, err = client.VolumeDriver(cl).CloudMigrateStart(goodRequest) - require.NoError(t, err) - require.Equal(t, goodResponse.TaskId, resp.TaskId, "Unexpected taskId in response") + resp, err := volumeclient.VolumeDriver(cl).CloudMigrateStart(goodRequest) + assert.Nil(t, err) + assert.NotNil(t, resp.TaskId) + + // Assert volume information is correct + volumes := api.NewOpenStorageVolumeClient(testVolDriver.Conn()) + ctx, err := contextWithToken(context.Background(), "test", "system.admin", testSharedSecret) + assert.NoError(t, err) + + _, err = volumes.Delete(ctx, &api.SdkVolumeDeleteRequest{ + VolumeId: id, + }) + assert.NoError(t, err) } func TestMigrateCancel(t *testing.T) { - ts, testVolDriver := testRestServer(t) + var err error + // Setup volume rest functions server + ts, testVolDriver := testRestServerSdk(t) defer ts.Close() defer testVolDriver.Stop() - cl, err := client.NewDriverClient(ts.URL, mockDriverName, "", mockDriverName) - require.NoError(t, err) + // get token + token, err := createToken("test", "system.admin", testSharedSecret) + assert.NoError(t, err) + + cl, err := volumeclient.NewAuthDriverClient(ts.URL, "fake", version, token, "", "fake") + assert.NoError(t, err) goodRequest := &api.CloudMigrateCancelRequest{ TaskId: "goodTaskID", } - badRequest := &api.CloudMigrateCancelRequest{ - TaskId: "badTaskID", - } - testVolDriver.MockDriver().EXPECT().CloudMigrateCancel(badRequest).Return(fmt.Errorf("TaskId not found")).Times(1) - testVolDriver.MockDriver().EXPECT().CloudMigrateCancel(goodRequest).Return(nil).Times(1) // Cancel Migrate - err = client.VolumeDriver(cl).CloudMigrateCancel(badRequest) - require.Error(t, err) - require.Contains(t, err.Error(), "TaskId not found") - err = client.VolumeDriver(cl).CloudMigrateCancel(goodRequest) - require.NoError(t, err) + err = volumeclient.VolumeDriver(cl).CloudMigrateCancel(goodRequest) + assert.Nil(t, err) } -func TestMigrateiStatus(t *testing.T) { - ts, testVolDriver := testRestServer(t) +func TestMigrateStatus(t *testing.T) { + var err error + // Setup volume rest functions server + ts, testVolDriver := testRestServerSdk(t) defer ts.Close() defer testVolDriver.Stop() - cl, err := client.NewDriverClient(ts.URL, mockDriverName, "", mockDriverName) - require.NoError(t, err) - - emptyStatus := &api.CloudMigrateStatusResponse{} - statusResponse := &api.CloudMigrateStatusResponse{ - Info: map[string]*api.CloudMigrateInfoList{ - "clusterId": &api.CloudMigrateInfoList{ - List: []*api.CloudMigrateInfo{ - &api.CloudMigrateInfo{ - TaskId: "taskId", - ClusterId: "clusterId", - LocalVolumeId: "localVolumeId", - LocalVolumeName: "localVolumeName", - RemoteVolumeId: "remoteVolumeName", - CloudbackupId: "cloudbackupId", - CurrentStage: api.CloudMigrate_Done, - Status: api.CloudMigrate_Complete, - }}}}, - } + // get token + token, err := createToken("test", "system.admin", testSharedSecret) + assert.NoError(t, err) - testVolDriver.MockDriver().EXPECT().CloudMigrateStatus(&api.CloudMigrateStatusRequest{}).Return(emptyStatus, nil).Times(1) - testVolDriver.MockDriver().EXPECT().CloudMigrateStatus(&api.CloudMigrateStatusRequest{}).Return(statusResponse, nil).Times(1) + cl, err := volumeclient.NewAuthDriverClient(ts.URL, "fake", version, token, "", "fake") + assert.NoError(t, err) // Get Migrate status - status, err := client.VolumeDriver(cl).CloudMigrateStatus(&api.CloudMigrateStatusRequest{}) - require.NoError(t, err) - require.Equal(t, 0, len(status.Info)) - status, err = client.VolumeDriver(cl).CloudMigrateStatus(&api.CloudMigrateStatusRequest{}) - require.NoError(t, err) - require.Equal(t, 1, len(status.Info)) - require.Equal(t, statusResponse, status) - + resp, err := volumeclient.VolumeDriver(cl).CloudMigrateStatus(&api.CloudMigrateStatusRequest{}) + assert.Nil(t, err) + assert.Equal(t, 1, len(resp.Info)) } -*/ diff --git a/volume/drivers/fake/fake.go b/volume/drivers/fake/fake.go index 63d942bae..d6113cb4b 100644 --- a/volume/drivers/fake/fake.go +++ b/volume/drivers/fake/fake.go @@ -275,6 +275,22 @@ func (d *driver) Detach(volumeID string, options map[string]string) error { return nil } +func (d *driver) CloudMigrateStart(request *api.CloudMigrateStartRequest) (*api.CloudMigrateStartResponse, error) { + return &api.CloudMigrateStartResponse{TaskId: request.TaskId}, nil +} + +func (d *driver) CloudMigrateCancel(request *api.CloudMigrateCancelRequest) error { + return nil +} + +func (d *driver) CloudMigrateStatus(request *api.CloudMigrateStatusRequest) (*api.CloudMigrateStatusResponse, error) { + cml := make(map[string]*api.CloudMigrateInfoList, 0) + cml["result"] = &api.CloudMigrateInfoList{} + return &api.CloudMigrateStatusResponse{ + Info: cml, + }, nil +} + func (d *driver) Set(volumeID string, locator *api.VolumeLocator, spec *api.VolumeSpec) error { v, err := d.GetVol(volumeID) if err != nil {