From 4269f5f6f3a5185dfe116442736602861faf6e35 Mon Sep 17 00:00:00 2001 From: prateekpandey14 Date: Mon, 13 Aug 2018 17:07:16 +0530 Subject: [PATCH] Enhance mayactl snapshot commands for cas template based provisioning 1. This commit will enhance mayctl snapshot commands create/list/revert to work with CAS template based volume provisioning. 2. Add basic integration test for volume snapshot related operations based on CAS template based provisioning. Signed-off-by: prateekpandey14 --- ci/setup_env.sh | 61 ++++++++++++++++--- .../app/server/snapshot_endpoint.go | 33 +++++----- cmd/mayactl/app/command/snapshot/create.go | 3 +- cmd/mayactl/app/command/volume_create.go | 10 +-- cmd/mayactl/app/command/volumes_list.go | 10 +-- pkg/client/mapiserver/snapshot.go | 9 ++- pkg/client/mapiserver/volume_create.go | 23 ++++--- pkg/client/mapiserver/volume_create_test.go | 20 +++--- 8 files changed, 109 insertions(+), 60 deletions(-) diff --git a/ci/setup_env.sh b/ci/setup_env.sh index 2fbe69bc49..3a9db0f937 100755 --- a/ci/setup_env.sh +++ b/ci/setup_env.sh @@ -4,28 +4,71 @@ MAPI_SVC_ADDR=`kubectl get service -n openebs maya-apiserver-service -o json | g export MAPI_ADDR="http://${MAPI_SVC_ADDR}:5656" export KUBERNETES_SERVICE_HOST="127.0.0.1" export MAYACTL="$GOPATH/src/github.com/openebs/maya/bin/maya/mayactl" +export KUBECONFIG=$HOME/.kube/config +POD=$(kubectl get pods -o=jsonpath='{.items[0].metadata.name}' -n openebs) +SNAPNAME=$(printf "%s_%s" "testsnap" "$(date +%F%N)") echo "*************** Running mayactl volume list *******************************" ${MAYACTL} volume list -#rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi - -POD=$(kubectl get pods -o=jsonpath='{.items[0].metadata.name}' -n openebs) -kubectl logs --tail=10 $POD -n openebs +rc=$?; +if [[ $rc != 0 ]]; then + kubectl logs --tail=10 $POD -n openebs + exit $rc; +fi printf "\n\n" echo "************** Running mayactl volume info *******************************" ${MAYACTL} volume info --volname default-demo-vol1-claim -rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi +rc=$?; +if [[ $rc != 0 ]]; then + kubectl logs --tail=10 $POD -n openebs + exit $rc; +fi printf "\n\n" sleep 10 echo "************** Running mayactl volume stats ******************************" ${MAYACTL} volume stats --volname default-demo-vol1-claim -rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi +rc=$?; +if [[ $rc != 0 ]]; then + kubectl logs --tail=10 $POD -n openebs + exit $rc; +fi + +echo "************** Running mayactl snapshot create **************************" +${MAYACTL} snapshot create --volname default-demo-vol1-claim --snapname $SNAPNAME +rc=$?; +if [[ $rc != 0 ]]; then + kubectl logs --tail=10 $POD -n openebs + exit $rc; +fi + +printf "\n\n" +sleep 30 + +${MAYACTL} snapshot create --volname default-demo-vol1-claim --snapname snap2 +if [[ $rc != 0 ]]; then + kubectl logs --tail=10 $POD -n openebs + exit $rc; +fi + +sleep 30 -#echo "************** Running mayactl volume delete ******************************" -#${MAYACTL} volume delete --volname demo-vol1-claim -n openebs -#rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi +echo "************** Running mayactl snapshot list ******************************" +${MAYACTL} snapshot list --volname default-demo-vol1-claim +if [[ $rc != 0 ]]; then + kubectl logs --tail=10 $POD -n openebs + exit $rc; +fi + +printf "\n\n" +sleep 5 +echo "************** Running mayactl volume delete ******************************" +${MAYACTL} volume delete --volname default-demo-vol1-claim +if [[ $rc != 0 ]]; then + kubectl logs --tail=10 $POD -n openebs + exit $rc; +fi diff --git a/cmd/maya-apiserver/app/server/snapshot_endpoint.go b/cmd/maya-apiserver/app/server/snapshot_endpoint.go index 49fe641522..0c6336ee55 100644 --- a/cmd/maya-apiserver/app/server/snapshot_endpoint.go +++ b/cmd/maya-apiserver/app/server/snapshot_endpoint.go @@ -19,24 +19,27 @@ func (s *HTTPServer) snapshotSpecificRequest(resp http.ResponseWriter, req *http if path == req.URL.Path { return nil, CodedError(405, ErrInvalidMethod) } - + volOp := &volumeAPIOpsV1alpha1{ + req: req, + resp: resp, + } switch { case strings.Contains(path, "/create/"): - return s.snapshotCreate(resp, req) + return volOp.snapshotCreate() case strings.Contains(path, "/revert/"): - return s.snapshotRevert(resp, req) + return volOp.snapshotRevert() case strings.Contains(path, "/list"): volName := strings.TrimPrefix(path, "/list/") - return s.snapshotList(resp, req, volName) + return volOp.snapshotList(volName) default: return nil, CodedError(405, ErrInvalidMethod) } } // SnapshotCreate is http handler which handles snaphsot-create request -func (s *HTTPServer) snapshotCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) { +func (v *volumeAPIOpsV1alpha1) snapshotCreate() (interface{}, error) { - if req.Method != "PUT" && req.Method != "POST" { + if v.req.Method != "PUT" && v.req.Method != "POST" { return nil, CodedError(405, ErrInvalidMethod) } @@ -45,7 +48,7 @@ func (s *HTTPServer) snapshotCreate(resp http.ResponseWriter, req *http.Request) snap := v1.VolumeSnapshot{} // The yaml/json spec is decoded to VolumeSnapshot struct - if err := decodeBody(req, &snap); err != nil { + if err := decodeBody(v.req, &snap); err != nil { return nil, CodedError(400, err.Error()) } @@ -63,7 +66,7 @@ func (s *HTTPServer) snapshotCreate(resp http.ResponseWriter, req *http.Request) glog.Infof("Processing snapshot-create request of volume: %s", snap.Spec.VolumeName) - voldetails, err := s.volumeRead(resp, req, snap.Spec.VolumeName) + voldetails, err := v.read(snap.Spec.VolumeName) if err != nil { return nil, err } @@ -85,8 +88,8 @@ func (s *HTTPServer) snapshotCreate(resp http.ResponseWriter, req *http.Request) // SnapshotRevert is http handler for handling snapshot-revert request. // Volume and existing snapshot name will be passed as struct fields to // revert to that particular snapshot -func (s *HTTPServer) snapshotRevert(resp http.ResponseWriter, req *http.Request) (interface{}, error) { - if req.Method != "PUT" && req.Method != "POST" { +func (v *volumeAPIOpsV1alpha1) snapshotRevert() (interface{}, error) { + if v.req.Method != "PUT" && v.req.Method != "POST" { return nil, CodedError(405, ErrInvalidMethod) } glog.Infof("Processing Volume snapshot-revert request") @@ -94,7 +97,7 @@ func (s *HTTPServer) snapshotRevert(resp http.ResponseWriter, req *http.Request) snap := v1.VolumeSnapshot{} // The yaml/json spec is decoded to VolumeSnapshot struct - if err := decodeBody(req, &snap); err != nil { + if err := decodeBody(v.req, &snap); err != nil { return nil, CodedError(400, err.Error()) } @@ -112,7 +115,7 @@ func (s *HTTPServer) snapshotRevert(resp http.ResponseWriter, req *http.Request) glog.Infof("Processing snapshot-revert request of volume: %s", snap.Spec.VolumeName) - voldetails, err := s.volumeRead(resp, req, snap.Spec.VolumeName) + voldetails, err := v.read(snap.Spec.VolumeName) if err != nil { return nil, err } @@ -132,9 +135,9 @@ func (s *HTTPServer) snapshotRevert(resp http.ResponseWriter, req *http.Request) } // SnapshotList is http handler for listing all created snapshot specific to particular volume -func (s *HTTPServer) snapshotList(resp http.ResponseWriter, req *http.Request, volName string) (interface{}, error) { +func (v *volumeAPIOpsV1alpha1) snapshotList(volName string) (interface{}, error) { - if req.Method != "GET" { + if v.req.Method != "GET" { return nil, CodedError(405, ErrInvalidMethod) } glog.Infof("Processing Volume snapshot-list request") @@ -148,7 +151,7 @@ func (s *HTTPServer) snapshotList(resp http.ResponseWriter, req *http.Request, v return nil, CodedError(400, fmt.Sprintf("Volume name missing in '%v'", snap.Spec.VolumeName)) } - voldetails, err := s.volumeRead(resp, req, volName) + voldetails, err := v.read(volName) if err != nil { return nil, err } diff --git a/cmd/mayactl/app/command/snapshot/create.go b/cmd/mayactl/app/command/snapshot/create.go index 4e31c621b2..c932b6f2db 100644 --- a/cmd/mayactl/app/command/snapshot/create.go +++ b/cmd/mayactl/app/command/snapshot/create.go @@ -77,7 +77,6 @@ func (c *CmdSnaphotOptions) RunSnapshotCreate(cmd *cobra.Command) error { return fmt.Errorf("Snapshot creation failed: %v", resp) } - fmt.Printf("Volume snapshot Successfully Created:%v\n", c.volName) - + fmt.Printf("Volume snapshot created for volume %s : '%s'\n", c.volName, c.snapName) return nil } diff --git a/cmd/mayactl/app/command/volume_create.go b/cmd/mayactl/app/command/volume_create.go index 9ba60d53d6..65aa372113 100644 --- a/cmd/mayactl/app/command/volume_create.go +++ b/cmd/mayactl/app/command/volume_create.go @@ -19,9 +19,9 @@ package command import ( "fmt" + "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1" "github.com/openebs/maya/pkg/client/mapiserver" "github.com/openebs/maya/pkg/util" - mayav1 "github.com/openebs/maya/types/v1" "github.com/spf13/cobra" ) @@ -61,7 +61,7 @@ func (c *CmdVolumeOptions) RunVolumeCreate(cmd *cobra.Command) error { if err != nil { return err } - resp := mapiserver.CreateVolume(c.volName, c.size) + resp := mapiserver.CreateVolume(c.volName, c.size, c.namespace) if resp != nil { return fmt.Errorf("Volume creation failed: %v", resp) } @@ -71,13 +71,13 @@ func (c *CmdVolumeOptions) RunVolumeCreate(cmd *cobra.Command) error { // IsVolumeExist checks whether the volume already exists or not func IsVolumeExist(volname string) error { - var vols mayav1.VolumeList - err := mapiserver.ListVolumes(&vols) + var cvols v1alpha1.CASVolumeList + err := mapiserver.ListVolumes(&cvols) if err != nil { return err } - for _, items := range vols.Items { + for _, items := range cvols.Items { if volname == items.ObjectMeta.Name { return fmt.Errorf("Volume creation failed : Volume %v already exist ", volname) } diff --git a/cmd/mayactl/app/command/volumes_list.go b/cmd/mayactl/app/command/volumes_list.go index 62312a58b9..5d566b4852 100644 --- a/cmd/mayactl/app/command/volumes_list.go +++ b/cmd/mayactl/app/command/volumes_list.go @@ -19,9 +19,9 @@ package command import ( "fmt" + "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1" "github.com/openebs/maya/pkg/client/mapiserver" "github.com/openebs/maya/pkg/util" - mtypesv1 "github.com/openebs/maya/types/v1" "github.com/spf13/cobra" ) @@ -56,15 +56,15 @@ func NewCmdVolumesList() *cobra.Command { func (c *CmdVolumeOptions) RunVolumesList(cmd *cobra.Command) error { //fmt.Println("Executing volume list...") - var vsms mtypesv1.VolumeList - err := mapiserver.ListVolumes(&vsms) + var cvols v1alpha1.CASVolumeList + err := mapiserver.ListVolumes(&cvols) if err != nil { return err } - out := make([]string, len(vsms.Items)+1) + out := make([]string, len(cvols.Items)+1) out[0] = "Name|Status" - for i, items := range vsms.Items { + for i, items := range cvols.Items { if items.Status.Reason == "" { items.Status.Reason = "Running" } diff --git a/pkg/client/mapiserver/snapshot.go b/pkg/client/mapiserver/snapshot.go index 2d166de68b..017911564a 100644 --- a/pkg/client/mapiserver/snapshot.go +++ b/pkg/client/mapiserver/snapshot.go @@ -35,7 +35,7 @@ const ( snapshotRevertPath = "/latest/snapshots/revert/" snapshotListPath = "/latest/snapshots/list/" snapshotTemplate = ` -Snapshot Details: +Snapshot Details: ------------------ {{ printf "NAME\t CREATED AT\t SIZE(in MB)\t PARENT\t CHILDREN" }} {{ printf "-----\t -----------\t ------------\t -------\t ---------" }} {{ range $key, $value := . }} @@ -70,8 +70,11 @@ func CreateSnapshot(volName string, snapName string, namespace string) error { } // Marshal serializes the values - jsonValue, _ := json.Marshal(snap) - _, err := postRequest(GetURL()+snapshotCreatePath, jsonValue, namespace, true) + jsonValue, err := json.Marshal(snap) + if err != nil { + return err + } + _, err = postRequest(GetURL()+snapshotCreatePath, jsonValue, namespace, true) return err } diff --git a/pkg/client/mapiserver/volume_create.go b/pkg/client/mapiserver/volume_create.go index 8c67814702..5a63bc540e 100644 --- a/pkg/client/mapiserver/volume_create.go +++ b/pkg/client/mapiserver/volume_create.go @@ -4,30 +4,29 @@ import ( "encoding/json" "time" - "github.com/openebs/maya/types/v1" + "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( - volumeCreateTimeout = 5 * time.Second + volumeCreateTimeout = 60 * time.Second volumePath = "/latest/volumes/" ) // CreateVolume creates a volume by invoking the API call to m-apiserver -func CreateVolume(vname, size string) error { +func CreateVolume(vname, size, namespace string) error { // Filling structure with values - vs := v1.Volume{ - TypeMeta: v1.TypeMeta{ - Kind: "Volume", - APIVersion: "v1", + cVol := v1alpha1.CASVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: vname, + Namespace: namespace, }, - ObjectMeta: v1.ObjectMeta{ - Name: vname, + Spec: v1alpha1.CASVolumeSpec{ + Capacity: size, }, - Capacity: size, } - // Marshal serializes the value of vs structure - jsonValue, err := json.Marshal(vs) + jsonValue, err := json.Marshal(cVol) if err != nil { return err } diff --git a/pkg/client/mapiserver/volume_create_test.go b/pkg/client/mapiserver/volume_create_test.go index f0664054ed..e1556a2c7e 100644 --- a/pkg/client/mapiserver/volume_create_test.go +++ b/pkg/client/mapiserver/volume_create_test.go @@ -15,6 +15,7 @@ func TestCreateVolume(t *testing.T) { tests := map[string]*struct { volumeName string size string + namespace string fakeHandler utiltesting.FakeHandler err error addr string @@ -22,10 +23,11 @@ func TestCreateVolume(t *testing.T) { "StatusOK": { volumeName: "qwewretrytu", size: "1G", + namespace: "appspace", fakeHandler: utiltesting.FakeHandler{ - StatusCode: 200, - ResponseBody: "Volume 'qwewretrytu' deleted Successfully", - T: t, + StatusCode: 200, + //ResponseBody: "Volume 'qwewretrytu' deleted Successfully", + T: t, }, err: nil, addr: "MAPI_ADDR", @@ -34,8 +36,8 @@ func TestCreateVolume(t *testing.T) { volumeName: "12324rty653423", size: "1G", fakeHandler: utiltesting.FakeHandler{ - StatusCode: 404, - ResponseBody: "Volume '12324rty653423' deleted Successfully", + StatusCode: 404, + //ResponseBody: "Volume '12324rty653423' deleted Successfully", T: t, }, @@ -46,9 +48,9 @@ func TestCreateVolume(t *testing.T) { volumeName: "", size: "1G", fakeHandler: utiltesting.FakeHandler{ - StatusCode: 400, - ResponseBody: "Volume name is missing", - T: t, + StatusCode: 400, + //ResponseBody: "Volume name is missing", + T: t, }, err: fmt.Errorf("Server status error: %v", http.StatusText(400)), addr: "MAPI_ADDR", @@ -61,7 +63,7 @@ func TestCreateVolume(t *testing.T) { os.Setenv(tt.addr, server.URL) defer os.Unsetenv(tt.addr) defer server.Close() - got := CreateVolume(tt.volumeName, tt.size) + got := CreateVolume(tt.volumeName, tt.size, tt.namespace) if !reflect.DeepEqual(got, tt.err) { t.Fatalf("CreateVolume(%v, %v) => got %v, want %v ", tt.volumeName, tt.size, got, tt.err)