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

Add RepairVolumeHandle to the csi translation struct #83593

Merged
merged 1 commit into from
Oct 9, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion staging/src/k8s.io/csi-translation-lib/plugins/aws_ebs.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"strconv"
"strings"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -161,6 +161,10 @@ func (t *awsElasticBlockStoreCSITranslator) GetCSIPluginName() string {
return AWSEBSDriverName
}

func (t *awsElasticBlockStoreCSITranslator) RepairVolumeHandle(volumeHandle, nodeID string) (string, error) {
return volumeHandle, nil
}

// awsVolumeRegMatch represents Regex Match for AWS volume.
var awsVolumeRegMatch = regexp.MustCompile("^vol-[^/]*$")

Expand Down
6 changes: 5 additions & 1 deletion staging/src/k8s.io/csi-translation-lib/plugins/azure_disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"regexp"
"strings"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -209,6 +209,10 @@ func (t *azureDiskCSITranslator) GetCSIPluginName() string {
return AzureDiskDriverName
}

func (t *azureDiskCSITranslator) RepairVolumeHandle(volumeHandle, nodeID string) (string, error) {
return volumeHandle, nil
}

func isManagedDisk(diskURI string) bool {
if len(diskURI) > 4 && strings.ToLower(diskURI[:4]) == "http" {
return false
Expand Down
6 changes: 5 additions & 1 deletion staging/src/k8s.io/csi-translation-lib/plugins/azure_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"fmt"
"strings"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -176,6 +176,10 @@ func (t *azureFileCSITranslator) GetCSIPluginName() string {
return AzureFileDriverName
}

func (t *azureFileCSITranslator) RepairVolumeHandle(volumeHandle, nodeID string) (string, error) {
return volumeHandle, nil
}

// get file share info according to volume id, e.g.
// input: "rg#f5713de20cde511e8ba4900#pvc-file-dynamic-17e43f84-f474-11e8-acd0-000d3a00df41"
// output: rg, f5713de20cde511e8ba4900, pvc-file-dynamic-17e43f84-f474-11e8-acd0-000d3a00df41
Expand Down
60 changes: 54 additions & 6 deletions staging/src/k8s.io/csi-translation-lib/plugins/gce_pd.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"strconv"
"strings"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
Expand All @@ -41,9 +41,14 @@ const (
// "projects/{projectName}/zones/{zoneName}/disks/{diskName}"
volIDZonalFmt = "projects/%s/zones/%s/disks/%s"
// "projects/{projectName}/regions/{regionName}/disks/{diskName}"
volIDRegionalFmt = "projects/%s/regions/%s/disks/%s"
volIDDiskNameValue = 5
volIDTotalElements = 6
volIDRegionalFmt = "projects/%s/regions/%s/disks/%s"
volIDProjectValue = 1
volIDRegionalityValue = 2
volIDZoneValue = 3
volIDDiskNameValue = 5
volIDTotalElements = 6

nodeIDFmt = "projects/%s/zones/%s/instances/%s"

// UnspecifiedValue is used for an unknown zone string
UnspecifiedValue = "UNSPECIFIED"
Expand Down Expand Up @@ -328,10 +333,53 @@ func (g *gcePersistentDiskCSITranslator) GetCSIPluginName() string {
return GCEPDDriverName
}

// RepairVolumeHandle returns a fully specified volume handle by inferring
// project, zone/region from the node ID if the volume handle has UNSPECIFIED
// sections
func (g *gcePersistentDiskCSITranslator) RepairVolumeHandle(volumeHandle, nodeID string) (string, error) {
davidz627 marked this conversation as resolved.
Show resolved Hide resolved
var err error
tok := strings.Split(volumeHandle, "/")
if len(tok) < volIDTotalElements {
return "", fmt.Errorf("volume handle has wrong number of elements; got %v, wanted %v or more", len(tok), volIDTotalElements)
}
if tok[volIDProjectValue] != UnspecifiedValue {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this right? Can a project be unspecified?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if the project is unspecified, does that guarantee the zone/region is specified?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in migration project is always unspecified (that information isn't encoded in in-tree API objects at all). Zone/region is unspecified based on whether the zone label is applied to the PV or not (I think we are not always guaranteed that is the case especially in pre-provisioned or inline volumes).

So this will always fix the project - and sometimes fix the zone/region. Logic is kind of complicated so I have the unit test

return volumeHandle, nil
}

nodeTok := strings.Split(nodeID, "/")
if len(nodeTok) < volIDTotalElements {
return "", fmt.Errorf("node handle has wrong number of elements; got %v, wanted %v or more", len(nodeTok), volIDTotalElements)
}

switch tok[volIDRegionalityValue] {
case "zones":
zone := ""
if tok[volIDZoneValue] == UnspecifiedValue {
zone = nodeTok[volIDZoneValue]
} else {
zone = tok[volIDZoneValue]
}
return fmt.Sprintf(volIDZonalFmt, nodeTok[volIDProjectValue], zone, tok[volIDDiskNameValue]), nil
case "regions":
region := ""
if tok[volIDZoneValue] == UnspecifiedValue {
region, err = getRegionFromZones([]string{nodeTok[volIDZoneValue]})
if err != nil {
return "", fmt.Errorf("failed to get region from zone %s: %v", nodeTok[volIDZoneValue], err)
}
} else {
region = tok[volIDZoneValue]
}
return fmt.Sprintf(volIDRegionalFmt, nodeTok[volIDProjectValue], region, tok[volIDDiskNameValue]), nil
default:
return "", fmt.Errorf("expected volume handle to have zones or regions regionality value, got: %s", tok[volIDRegionalityValue])
}
}

func pdNameFromVolumeID(id string) (string, error) {
splitID := strings.Split(id, "/")
if len(splitID) != volIDTotalElements {
return "", fmt.Errorf("failed to get id components. Expected projects/{project}/zones/{zone}/disks/{name}. Got: %s", id)
if len(splitID) < volIDTotalElements {
return "", fmt.Errorf("failed to get id components.Got: %v, wanted %v components or more. ", len(splitID), volIDTotalElements)
}
return splitID[volIDDiskNameValue], nil
}
Expand Down
81 changes: 80 additions & 1 deletion staging/src/k8s.io/csi-translation-lib/plugins/gce_pd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ limitations under the License.
package plugins

import (
"fmt"
"reflect"
"testing"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
)

Expand Down Expand Up @@ -204,6 +205,84 @@ func TestTranslateAllowedTopologies(t *testing.T) {
}
}

func TestRepairVolumeHandle(t *testing.T) {
testCases := []struct {
name string
volumeHandle string
nodeID string
expectedVolumeHandle string
expectedErr bool
}{
{
name: "fully specified",
volumeHandle: fmt.Sprintf(volIDZonalFmt, "foo", "bar", "baz"),
nodeID: fmt.Sprintf(nodeIDFmt, "bing", "bada", "boom"),
expectedVolumeHandle: fmt.Sprintf(volIDZonalFmt, "foo", "bar", "baz"),
},
{
name: "fully specified (regional)",
volumeHandle: fmt.Sprintf(volIDRegionalFmt, "foo", "us-central1-c", "baz"),
nodeID: fmt.Sprintf(nodeIDFmt, "bing", "bada", "boom"),
expectedVolumeHandle: fmt.Sprintf(volIDRegionalFmt, "foo", "us-central1-c", "baz"),
},
{
name: "no project",
volumeHandle: fmt.Sprintf(volIDZonalFmt, UnspecifiedValue, "bar", "baz"),
nodeID: fmt.Sprintf(nodeIDFmt, "bing", "bada", "boom"),
expectedVolumeHandle: fmt.Sprintf(volIDZonalFmt, "bing", "bar", "baz"),
},
{
name: "no project or zone",
volumeHandle: fmt.Sprintf(volIDZonalFmt, UnspecifiedValue, UnspecifiedValue, "baz"),
nodeID: fmt.Sprintf(nodeIDFmt, "bing", "bada", "boom"),
expectedVolumeHandle: fmt.Sprintf(volIDZonalFmt, "bing", "bada", "baz"),
},
{
name: "no project or region",
volumeHandle: fmt.Sprintf(volIDRegionalFmt, UnspecifiedValue, UnspecifiedValue, "baz"),
nodeID: fmt.Sprintf(nodeIDFmt, "bing", "us-central1-c", "boom"),
expectedVolumeHandle: fmt.Sprintf(volIDRegionalFmt, "bing", "us-central1", "baz"),
},
{
name: "no project (regional)",
volumeHandle: fmt.Sprintf(volIDRegionalFmt, UnspecifiedValue, "us-west1", "baz"),
nodeID: fmt.Sprintf(nodeIDFmt, "bing", "us-central1-c", "boom"),
expectedVolumeHandle: fmt.Sprintf(volIDRegionalFmt, "bing", "us-west1", "baz"),
},
{
name: "invalid handle",
volumeHandle: "foo",
nodeID: fmt.Sprintf(nodeIDFmt, "bing", "us-central1-c", "boom"),
expectedErr: true,
},
{
name: "invalid node ID",
volumeHandle: fmt.Sprintf(volIDRegionalFmt, UnspecifiedValue, "us-west1", "baz"),
nodeID: "foo",
expectedErr: true,
},
}
g := NewGCEPersistentDiskCSITranslator()
for _, tc := range testCases {
davidz627 marked this conversation as resolved.
Show resolved Hide resolved
t.Run(tc.name, func(t *testing.T) {
gotVolumeHandle, err := g.RepairVolumeHandle(tc.volumeHandle, tc.nodeID)
if err != nil && !tc.expectedErr {
if !tc.expectedErr {
t.Fatalf("Got error: %v, but expected none", err)
}
return
}
if err == nil && tc.expectedErr {
t.Fatal("Got no error, but expected one")
}

if gotVolumeHandle != tc.expectedVolumeHandle {
t.Fatalf("Got volume handle %s, but expected %s", gotVolumeHandle, tc.expectedVolumeHandle)
}
})
}
}

func TestBackwardCompatibleAccessModes(t *testing.T) {
testCases := []struct {
name string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
package plugins

import (
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
)

Expand Down Expand Up @@ -55,4 +55,7 @@ type InTreePlugin interface {

// GetCSIPluginName returns the name of the CSI plugin that supersedes the in-tree plugin
GetCSIPluginName() string

// RepairVolumeHandle generates a correct volume handle based on node ID information.
RepairVolumeHandle(volumeHandle, nodeID string) (string, error)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package plugins
import (
"fmt"

"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
storage "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -140,3 +140,7 @@ func (t *osCinderCSITranslator) GetInTreePluginName() string {
func (t *osCinderCSITranslator) GetCSIPluginName() string {
return CinderDriverName
}

func (t *osCinderCSITranslator) RepairVolumeHandle(volumeHandle, nodeID string) (string, error) {
return volumeHandle, nil
}
8 changes: 8 additions & 0 deletions staging/src/k8s.io/csi-translation-lib/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,11 @@ func (CSITranslator) IsInlineMigratable(vol *v1.Volume) bool {
}
return false
}

// RepairVolumeHandle generates a correct volume handle based on node ID information.
func (CSITranslator) RepairVolumeHandle(driverName, volumeHandle, nodeID string) (string, error) {
if plugin, ok := inTreePlugins[driverName]; ok {
return plugin.RepairVolumeHandle(volumeHandle, nodeID)
}
return "", fmt.Errorf("could not find In-Tree driver name for CSI plugin %v", driverName)
}