Skip to content

Commit

Permalink
Merge pull request kubernetes#85251 from leakingtapan/ebs-migraiton-zone
Browse files Browse the repository at this point in the history
Add CSI migration logic for EBS storageclass zone/zones/topology
  • Loading branch information
k8s-ci-robot authored Nov 16, 2019
2 parents e052900 + 3a7e0c2 commit d362b42
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 145 deletions.
1 change: 1 addition & 0 deletions staging/src/k8s.io/csi-translation-lib/plugins/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ go_test(
"azure_disk_test.go",
"azure_file_test.go",
"gce_pd_test.go",
"in_tree_volume_test.go",
],
embed = [":go_default_library"],
deps = [
Expand Down
30 changes: 24 additions & 6 deletions staging/src/k8s.io/csi-translation-lib/plugins/aws_ebs.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ import (
const (
// AWSEBSDriverName is the name of the CSI driver for EBS
AWSEBSDriverName = "ebs.csi.aws.com"
// AWSEBSTopologyKey is the zonal topology key for AWS EBS CSI Driver
AWSEBSTopologyKey = "topology.ebs.csi.aws.com/zone"
// AWSEBSInTreePluginName is the name of the intree plugin for EBS
AWSEBSInTreePluginName = "kubernetes.io/aws-ebs"
// AWSEBSTopologyKey is the zonal topology key for AWS EBS CSI driver
AWSEBSTopologyKey = "topology." + AWSEBSDriverName + "/zone"
)

var _ InTreePlugin = &awsElasticBlockStoreCSITranslator{}
Expand All @@ -49,17 +49,35 @@ func NewAWSElasticBlockStoreCSITranslator() InTreePlugin {

// TranslateInTreeStorageClassToCSI translates InTree EBS storage class parameters to CSI storage class
func (t *awsElasticBlockStoreCSITranslator) TranslateInTreeStorageClassToCSI(sc *storage.StorageClass) (*storage.StorageClass, error) {
params := map[string]string{}

var (
generatedTopologies []v1.TopologySelectorTerm
params = map[string]string{}
)
for k, v := range sc.Parameters {
switch strings.ToLower(k) {
case "fstype":
params["csi.storage.k8s.io/fstype"] = v
case fsTypeKey:
params[csiFsTypeKey] = v
case zoneKey:
generatedTopologies = generateToplogySelectors(AWSEBSTopologyKey, []string{v})
case zonesKey:
generatedTopologies = generateToplogySelectors(AWSEBSTopologyKey, strings.Split(v, ","))
default:
params[k] = v
}
}

if len(generatedTopologies) > 0 && len(sc.AllowedTopologies) > 0 {
return nil, fmt.Errorf("cannot simultaneously set allowed topologies and zone/zones parameters")
} else if len(generatedTopologies) > 0 {
sc.AllowedTopologies = generatedTopologies
} else if len(sc.AllowedTopologies) > 0 {
newTopologies, err := translateAllowedTopologies(sc.AllowedTopologies, AWSEBSTopologyKey)
if err != nil {
return nil, fmt.Errorf("failed translating allowed topologies: %v", err)
}
sc.AllowedTopologies = newTopologies
}

sc.Parameters = params

return sc, nil
Expand Down
37 changes: 5 additions & 32 deletions staging/src/k8s.io/csi-translation-lib/plugins/gce_pd.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,33 +65,6 @@ func NewGCEPersistentDiskCSITranslator() InTreePlugin {
return &gcePersistentDiskCSITranslator{}
}

func translateAllowedTopologies(terms []v1.TopologySelectorTerm) ([]v1.TopologySelectorTerm, error) {
if terms == nil {
return nil, nil
}

newTopologies := []v1.TopologySelectorTerm{}
for _, term := range terms {
newTerm := v1.TopologySelectorTerm{}
for _, exp := range term.MatchLabelExpressions {
var newExp v1.TopologySelectorLabelRequirement
if exp.Key == v1.LabelZoneFailureDomain {
newExp = v1.TopologySelectorLabelRequirement{
Key: GCEPDTopologyKey,
Values: exp.Values,
}
} else if exp.Key == GCEPDTopologyKey {
newExp = exp
} else {
return nil, fmt.Errorf("unknown topology key: %v", exp.Key)
}
newTerm.MatchLabelExpressions = append(newTerm.MatchLabelExpressions, newExp)
}
newTopologies = append(newTopologies, newTerm)
}
return newTopologies, nil
}

func generateToplogySelectors(key string, values []string) []v1.TopologySelectorTerm {
return []v1.TopologySelectorTerm{
{
Expand All @@ -112,13 +85,13 @@ func (g *gcePersistentDiskCSITranslator) TranslateInTreeStorageClassToCSI(sc *st
np := map[string]string{}
for k, v := range sc.Parameters {
switch strings.ToLower(k) {
case "fstype":
case fsTypeKey:
// prefixed fstype parameter is stripped out by external provisioner
np["csi.storage.k8s.io/fstype"] = v
np[csiFsTypeKey] = v
// Strip out zone and zones parameters and translate them into topologies instead
case "zone":
case zoneKey:
generatedTopologies = generateToplogySelectors(GCEPDTopologyKey, []string{v})
case "zones":
case zonesKey:
generatedTopologies = generateToplogySelectors(GCEPDTopologyKey, strings.Split(v, ","))
default:
np[k] = v
Expand All @@ -130,7 +103,7 @@ func (g *gcePersistentDiskCSITranslator) TranslateInTreeStorageClassToCSI(sc *st
} else if len(generatedTopologies) > 0 {
sc.AllowedTopologies = generatedTopologies
} else if len(sc.AllowedTopologies) > 0 {
newTopologies, err := translateAllowedTopologies(sc.AllowedTopologies)
newTopologies, err := translateAllowedTopologies(sc.AllowedTopologies, GCEPDTopologyKey)
if err != nil {
return nil, fmt.Errorf("failed translating allowed topologies: %v", err)
}
Expand Down
107 changes: 0 additions & 107 deletions staging/src/k8s.io/csi-translation-lib/plugins/gce_pd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,113 +98,6 @@ func TestTranslatePDInTreeStorageClassToCSI(t *testing.T) {
}
}

func TestTranslateAllowedTopologies(t *testing.T) {
testCases := []struct {
name string
topology []v1.TopologySelectorTerm
expectedToplogy []v1.TopologySelectorTerm
expErr bool
}{
{
name: "no translation",
topology: generateToplogySelectors(GCEPDTopologyKey, []string{"foo", "bar"}),
expectedToplogy: []v1.TopologySelectorTerm{
{
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
{
Key: GCEPDTopologyKey,
Values: []string{"foo", "bar"},
},
},
},
},
},
{
name: "translate",
topology: []v1.TopologySelectorTerm{
{
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
{
Key: "failure-domain.beta.kubernetes.io/zone",
Values: []string{"foo", "bar"},
},
},
},
},
expectedToplogy: []v1.TopologySelectorTerm{
{
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
{
Key: GCEPDTopologyKey,
Values: []string{"foo", "bar"},
},
},
},
},
},
{
name: "combo",
topology: []v1.TopologySelectorTerm{
{
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
{
Key: "failure-domain.beta.kubernetes.io/zone",
Values: []string{"foo", "bar"},
},
{
Key: GCEPDTopologyKey,
Values: []string{"boo", "baz"},
},
},
},
},
expectedToplogy: []v1.TopologySelectorTerm{
{
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
{
Key: GCEPDTopologyKey,
Values: []string{"foo", "bar"},
},
{
Key: GCEPDTopologyKey,
Values: []string{"boo", "baz"},
},
},
},
},
},
{
name: "some other key",
topology: []v1.TopologySelectorTerm{
{
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
{
Key: "test",
Values: []string{"foo", "bar"},
},
},
},
},
expErr: true,
},
}

for _, tc := range testCases {
t.Logf("Running test: %v", tc.name)
gotTop, err := translateAllowedTopologies(tc.topology)
if err != nil && !tc.expErr {
t.Errorf("Did not expect an error, got: %v", err)
}
if err == nil && tc.expErr {
t.Errorf("Expected an error but did not get one")
}

if !reflect.DeepEqual(gotTop, tc.expectedToplogy) {
t.Errorf("Expected topology: %v, but got: %v", tc.expectedToplogy, gotTop)
}
}
}

func TestRepairVolumeHandle(t *testing.T) {
testCases := []struct {
name string
Expand Down
41 changes: 41 additions & 0 deletions staging/src/k8s.io/csi-translation-lib/plugins/in_tree_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package plugins

import (
"errors"
"fmt"
"strings"

v1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -65,6 +66,17 @@ type InTreePlugin interface {
RepairVolumeHandle(volumeHandle, nodeID string) (string, error)
}

const (
// fsTypeKey is the deprecated storage class parameter key for fstype
fsTypeKey = "fstype"
// csiFsTypeKey is the storage class parameter key for CSI fstype
csiFsTypeKey = "csi.storage.k8s.io/fstype"
// zoneKey is the deprecated storage class parameter key for zone
zoneKey = "zone"
// zonesKey is the deprecated storage class parameter key for zones
zonesKey = "zones"
)

// replaceTopology overwrites an existing topology key by a new one.
func replaceTopology(pv *v1.PersistentVolume, oldKey, newKey string) error {
for i := range pv.Spec.NodeAffinity.Required.NodeSelectorTerms {
Expand Down Expand Up @@ -153,3 +165,32 @@ func translateTopology(pv *v1.PersistentVolume, topologyKey string) error {

return nil
}

// translateAllowedTopologies translates allowed topologies within storage class
// from legacy failure domain to given CSI topology key
func translateAllowedTopologies(terms []v1.TopologySelectorTerm, key string) ([]v1.TopologySelectorTerm, error) {
if terms == nil {
return nil, nil
}

newTopologies := []v1.TopologySelectorTerm{}
for _, term := range terms {
newTerm := v1.TopologySelectorTerm{}
for _, exp := range term.MatchLabelExpressions {
var newExp v1.TopologySelectorLabelRequirement
if exp.Key == v1.LabelZoneFailureDomain {
newExp = v1.TopologySelectorLabelRequirement{
Key: key,
Values: exp.Values,
}
} else if exp.Key == key {
newExp = exp
} else {
return nil, fmt.Errorf("unknown topology key: %v", exp.Key)
}
newTerm.MatchLabelExpressions = append(newTerm.MatchLabelExpressions, newExp)
}
newTopologies = append(newTopologies, newTerm)
}
return newTopologies, nil
}
Loading

0 comments on commit d362b42

Please sign in to comment.