Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PWX-37650: Supports adding the pod name as a locator label for clone/snap restore #2452

Merged
merged 1 commit into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading