Skip to content

Commit

Permalink
PWX-37650: Supports adding the pod name as a locator label for clone/…
Browse files Browse the repository at this point in the history
…snap restore

Signed-off-by: Adam Krpan <[email protected]>
  • Loading branch information
Pure-AdamuKaapan committed Jun 15, 2024
1 parent c0c3096 commit 3a7c483
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 4 deletions.
8 changes: 8 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,14 @@ func (v *VolumeSpec) IsNFSProxyVolume() bool {
return v.GetProxySpec() != nil && v.GetProxySpec().NfsSpec != nil
}

// GetFADAPodName returns the FlashArray Pod name specified in the Pure Block spec, or empty if any fields are unspecified
func (v *VolumeSpec) GetFADAPodName() string {
if v.GetProxySpec() != nil && v.GetProxySpec().PureBlockSpec != nil {
return v.GetProxySpec().PureBlockSpec.PodName
}
return ""
}

// GetCloneCreatorOwnership returns the appropriate ownership for the
// new snapshot and if an update is required
func (v *VolumeSpec) GetCloneCreatorOwnership(ctx context.Context) (*Ownership, bool) {
Expand Down
19 changes: 16 additions & 3 deletions api/server/sdk/volume_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ import (
"google.golang.org/grpc/status"
)

// FADAPodLabelKey is a label added to volume locators in the case of FADA volume clone/snap restore
const FADAPodLabelKey = "pure-pod-name" // Used to plumb in the pod name for volume cloning

// When create is called for an existing volume, this function is called to make sure
// the SDK only returns that the volume is ready when the status is UP
func (s *VolumeServer) waitForVolumeReady(ctx context.Context, id string) (*api.Volume, error) {
Expand Down Expand Up @@ -106,7 +109,6 @@ func (s *VolumeServer) create(
spec *api.VolumeSpec,
additionalCloneLabels map[string]string,
) (string, error) {

// Check if the volume has already been created or is in process of creation
volName := locator.GetName()
v, err := util.VolumeFromName(ctx, s.driver(ctx), volName)
Expand Down Expand Up @@ -171,8 +173,18 @@ func (s *VolumeServer) create(
}

// Create a snapshot from the parent
// Only include the FADA pod label
var labels map[string]string = nil
if locator.GetVolumeLabels() != nil {
if pod, ok := locator.GetVolumeLabels()[FADAPodLabelKey]; ok {
labels = map[string]string{
FADAPodLabelKey: pod,
}
}
}
id, err = s.driver(ctx).Snapshot(ctx, parent.GetId(), false, &api.VolumeLocator{
Name: volName,
Name: volName,
VolumeLabels: labels,
}, false)
if err != nil {
if err == kvdb.ErrNotFound {
Expand Down Expand Up @@ -335,7 +347,8 @@ func (s *VolumeServer) Clone(
}

locator := &api.VolumeLocator{
Name: req.GetName(),
Name: req.GetName(),
VolumeLabels: req.GetAdditionalLabels(),
}
source := &api.Source{
Parent: req.GetParentId(),
Expand Down
8 changes: 8 additions & 0 deletions api/spec/spec_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,14 @@ func (d *specHandler) UpdateSpecFromOpts(opts map[string]string, spec *api.Volum
case api.SpecBackendVolName:
volName := v
pureBackendVolName = &volName
case api.SpecPurePodName:
if spec.ProxySpec == nil {
spec.ProxySpec = &api.ProxySpec{}
}
if spec.ProxySpec.PureBlockSpec == nil {
spec.ProxySpec.PureBlockSpec = &api.PureBlockSpec{}
}
spec.ProxySpec.PureBlockSpec.PodName = v
case api.SpecPureFileExportRules:
if spec.ProxySpec == nil {
spec.ProxySpec = &api.ProxySpec{}
Expand Down
6 changes: 5 additions & 1 deletion csi/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,10 +614,14 @@ func (s *OsdCsiServer) CreateVolume(
}
newVolumeId = createResp.VolumeId
} else {
clonedMetadata := getClonedPVCMetadata(locator)
if spec.GetFADAPodName() != "" {
clonedMetadata[sdk.FADAPodLabelKey] = spec.GetFADAPodName()
}
cloneResp, err := volumes.Clone(ctx, &api.SdkVolumeCloneRequest{
Name: req.GetName(),
ParentId: source.Parent,
AdditionalLabels: getClonedPVCMetadata(locator),
AdditionalLabels: clonedMetadata,
})
if err != nil {
return nil, err
Expand Down
117 changes: 117 additions & 0 deletions csi/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/libopenstorage/openstorage/api"
"github.com/libopenstorage/openstorage/api/mock"
"github.com/libopenstorage/openstorage/api/server/sdk"
"github.com/libopenstorage/openstorage/api/spec"
authsecrets "github.com/libopenstorage/openstorage/pkg/auth/secrets"
mockLoadBalancer "github.com/libopenstorage/openstorage/pkg/loadbalancer/mock"
Expand Down Expand Up @@ -1983,6 +1984,122 @@ func TestControllerCreateVolumeFromSnapshot(t *testing.T) {
assert.Equal(t, mockParentID, volumeInfo.GetVolumeContext()[api.SpecParent])
}

func TestControllerCreateVolumeFromSnapshotFADAPod(t *testing.T) {
// Create server and client connection
s := newTestServer(t)
defer s.Stop()
c := csi.NewControllerClient(s.Conn())
s.mockClusterEnumerateNode(t, "node-1")
// Setup request
mockParentID := "parendId"
name := "myvol"
pod := "mypod"
size := int64(1234)
req := &csi.CreateVolumeRequest{
Name: name,
VolumeCapabilities: []*csi.VolumeCapability{
{},
},
CapacityRange: &csi.CapacityRange{
RequiredBytes: size,
},
VolumeContentSource: &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Snapshot{
Snapshot: &csi.VolumeContentSource_SnapshotSource{
SnapshotId: mockParentID,
},
},
},
Secrets: map[string]string{authsecrets.SecretTokenKey: systemUserToken},
Parameters: map[string]string{
api.SpecPurePodName: pod,
},
}

// Setup mock functions
id := "myid"
snapID := id + "-snap"
gomock.InOrder(

// First check on parent
s.MockDriver().
EXPECT().
Enumerate(&api.VolumeLocator{
VolumeIds: []string{mockParentID},
}, nil).
Return([]*api.Volume{{Id: mockParentID}}, nil).
Times(1),

// VolFromName (name)
s.MockDriver().
EXPECT().
Inspect(gomock.Any(), []string{name}).
Return(nil, fmt.Errorf("not found")).
Times(1),

s.MockDriver().
EXPECT().
Enumerate(gomock.Any(), nil).
Return(nil, fmt.Errorf("not found")).
Times(1),

//VolFromName parent
s.MockDriver().
EXPECT().
Inspect(gomock.Any(), gomock.Any()).
Return(
[]*api.Volume{{
Id: mockParentID,
}}, nil).
Times(1),

// create
s.MockDriver().
EXPECT().
Snapshot(gomock.Any(), gomock.Any(), gomock.Any(), &api.VolumeLocator{Name: name, VolumeLabels: map[string]string{sdk.FADAPodLabelKey: pod}}, gomock.Any()).
Return(snapID, nil).
Times(1),
s.MockDriver().
EXPECT().
Enumerate(&api.VolumeLocator{
VolumeIds: []string{snapID},
}, nil).
Return([]*api.Volume{
{
Id: id,
Source: &api.Source{Parent: mockParentID},
},
}, nil).
Times(2),

s.MockDriver().
EXPECT().
Set(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(nil).
Times(1),

s.MockDriver().
EXPECT().
Enumerate(gomock.Any(), nil).
Return([]*api.Volume{
{
Id: id,
Source: &api.Source{Parent: mockParentID},
},
}, nil).
Times(1),
)

r, err := c.CreateVolume(context.Background(), req)
assert.Nil(t, err)
assert.NotNil(t, r)
volumeInfo := r.GetVolume()

assert.Equal(t, id, volumeInfo.GetVolumeId())
assert.NotEqual(t, "true", volumeInfo.GetVolumeContext()[api.SpecSharedv4])
assert.Equal(t, mockParentID, volumeInfo.GetVolumeContext()[api.SpecParent])
}

func TestControllerCreateVolumeSnapshotThroughParameters(t *testing.T) {
// Create server and client connection
s := newTestServer(t)
Expand Down

0 comments on commit 3a7c483

Please sign in to comment.