Commit cc71b0ae authored by Silvan Kaiser's avatar Silvan Kaiser

Adding Tenant to QuobyteVolumeSource

Adds the tenant id to the QuobyteVolumeSource type and updates the quobyte api client to support looking up volume ids.
parent 54dc9db1
...@@ -2990,8 +2990,8 @@ ...@@ -2990,8 +2990,8 @@
}, },
{ {
"ImportPath": "github.com/quobyte/api", "ImportPath": "github.com/quobyte/api",
"Comment": "v0.1.1-4-g206ef832283c1a", "Comment": "v0.1.2",
"Rev": "206ef832283c1a0144bbc762be2634d49987b5ff" "Rev": "9cfd29338dd9fdaaf956b7082e5550aab5fe3841"
}, },
{ {
"ImportPath": "github.com/rancher/go-rancher/client", "ImportPath": "github.com/rancher/go-rancher/client",
......
...@@ -88245,6 +88245,10 @@ ...@@ -88245,6 +88245,10 @@
"description": "Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes", "description": "Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes",
"type": "string" "type": "string"
}, },
"tenant": {
"description": "Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin",
"type": "string"
},
"user": { "user": {
"description": "User to map volume access to Defaults to serivceaccount user", "description": "User to map volume access to Defaults to serivceaccount user",
"type": "string" "type": "string"
...@@ -918,6 +918,11 @@ type QuobyteVolumeSource struct { ...@@ -918,6 +918,11 @@ type QuobyteVolumeSource struct {
// Default is no group // Default is no group
// +optional // +optional
Group string Group string
// Tenant owning the given Quobyte volume in the Backend
// Used with dynamically provisioned Quobyte volumes, value is set by the plugin
// +optional
Tenant string
} }
// Represents a Glusterfs mount that lasts the lifetime of a pod. // Represents a Glusterfs mount that lasts the lifetime of a pod.
......
...@@ -5989,6 +5989,7 @@ func autoConvert_v1_QuobyteVolumeSource_To_core_QuobyteVolumeSource(in *v1.Quoby ...@@ -5989,6 +5989,7 @@ func autoConvert_v1_QuobyteVolumeSource_To_core_QuobyteVolumeSource(in *v1.Quoby
out.ReadOnly = in.ReadOnly out.ReadOnly = in.ReadOnly
out.User = in.User out.User = in.User
out.Group = in.Group out.Group = in.Group
out.Tenant = in.Tenant
return nil return nil
} }
...@@ -6003,6 +6004,7 @@ func autoConvert_core_QuobyteVolumeSource_To_v1_QuobyteVolumeSource(in *core.Quo ...@@ -6003,6 +6004,7 @@ func autoConvert_core_QuobyteVolumeSource_To_v1_QuobyteVolumeSource(in *core.Quo
out.ReadOnly = in.ReadOnly out.ReadOnly = in.ReadOnly
out.User = in.User out.User = in.User
out.Group = in.Group out.Group = in.Group
out.Tenant = in.Tenant
return nil return nil
} }
......
...@@ -873,6 +873,10 @@ func validateQuobyteVolumeSource(quobyte *core.QuobyteVolumeSource, fldPath *fie ...@@ -873,6 +873,10 @@ func validateQuobyteVolumeSource(quobyte *core.QuobyteVolumeSource, fldPath *fie
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(quobyte.Registry) == 0 { if len(quobyte.Registry) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("registry"), "must be a host:port pair or multiple pairs separated by commas")) allErrs = append(allErrs, field.Required(fldPath.Child("registry"), "must be a host:port pair or multiple pairs separated by commas"))
} else if len(quobyte.Tenant) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("tenant"), "must be a UUID provided by the configuration and may not be omitted "))
} else if len(quobyte.Tenant) >= 65 {
allErrs = append(allErrs, field.Required(fldPath.Child("tenant"), "must be a UUID and may not exceed a length of 64 characters"))
} else { } else {
for _, hostPortPair := range strings.Split(quobyte.Registry, ",") { for _, hostPortPair := range strings.Split(quobyte.Registry, ",") {
if _, _, err := net.SplitHostPort(hostPortPair); err != nil { if _, _, err := net.SplitHostPort(hostPortPair); err != nil {
......
...@@ -3379,6 +3379,7 @@ func TestValidateVolumes(t *testing.T) { ...@@ -3379,6 +3379,7 @@ func TestValidateVolumes(t *testing.T) {
ReadOnly: false, ReadOnly: false,
User: "root", User: "root",
Group: "root", Group: "root",
Tenant: "ThisIsSomeTenantUUID",
}, },
}, },
}, },
...@@ -3390,6 +3391,7 @@ func TestValidateVolumes(t *testing.T) { ...@@ -3390,6 +3391,7 @@ func TestValidateVolumes(t *testing.T) {
VolumeSource: core.VolumeSource{ VolumeSource: core.VolumeSource{
Quobyte: &core.QuobyteVolumeSource{ Quobyte: &core.QuobyteVolumeSource{
Volume: "/test", Volume: "/test",
Tenant: "ThisIsSomeTenantUUID",
}, },
}, },
}, },
...@@ -3406,6 +3408,7 @@ func TestValidateVolumes(t *testing.T) { ...@@ -3406,6 +3408,7 @@ func TestValidateVolumes(t *testing.T) {
Quobyte: &core.QuobyteVolumeSource{ Quobyte: &core.QuobyteVolumeSource{
Registry: "registry7861", Registry: "registry7861",
Volume: "/test", Volume: "/test",
Tenant: "ThisIsSomeTenantUUID",
}, },
}, },
}, },
...@@ -3422,6 +3425,7 @@ func TestValidateVolumes(t *testing.T) { ...@@ -3422,6 +3425,7 @@ func TestValidateVolumes(t *testing.T) {
Quobyte: &core.QuobyteVolumeSource{ Quobyte: &core.QuobyteVolumeSource{
Registry: "registry:7861,reg2", Registry: "registry:7861,reg2",
Volume: "/test", Volume: "/test",
Tenant: "ThisIsSomeTenantUUID",
}, },
}, },
}, },
...@@ -3437,6 +3441,7 @@ func TestValidateVolumes(t *testing.T) { ...@@ -3437,6 +3441,7 @@ func TestValidateVolumes(t *testing.T) {
VolumeSource: core.VolumeSource{ VolumeSource: core.VolumeSource{
Quobyte: &core.QuobyteVolumeSource{ Quobyte: &core.QuobyteVolumeSource{
Registry: "registry:7861", Registry: "registry:7861",
Tenant: "ThisIsSomeTenantUUID",
}, },
}, },
}, },
...@@ -3445,6 +3450,40 @@ func TestValidateVolumes(t *testing.T) { ...@@ -3445,6 +3450,40 @@ func TestValidateVolumes(t *testing.T) {
field: "quobyte.volume", field: "quobyte.volume",
}}, }},
}, },
{
name: "empty tenant quobyte",
vol: core.Volume{
Name: "quobyte",
VolumeSource: core.VolumeSource{
Quobyte: &core.QuobyteVolumeSource{
Registry: "registry:7861,reg2",
Volume: "/test",
Tenant: "",
},
},
},
errs: []verr{{
etype: field.ErrorTypeRequired,
field: "quobyte.tenant",
}},
},
{
name: "too long tenant quobyte",
vol: core.Volume{
Name: "quobyte",
VolumeSource: core.VolumeSource{
Quobyte: &core.QuobyteVolumeSource{
Registry: "registry:7861,reg2",
Volume: "/test",
Tenant: "this is too long to be a valid uuid so this test has to fail on the maximum length validation of the tenant.",
},
},
},
errs: []verr{{
etype: field.ErrorTypeRequired,
field: "quobyte.tenant",
}},
},
// AzureDisk // AzureDisk
{ {
name: "valid AzureDisk", name: "valid AzureDisk",
......
...@@ -326,6 +326,7 @@ func (plugin *quobytePlugin) newDeleterInternal(spec *volume.Spec) (volume.Delet ...@@ -326,6 +326,7 @@ func (plugin *quobytePlugin) newDeleterInternal(spec *volume.Spec) (volume.Delet
group: source.Group, group: source.Group,
volume: source.Volume, volume: source.Volume,
plugin: plugin, plugin: plugin,
tenant: source.Tenant,
}, },
registry: source.Registry, registry: source.Registry,
readOnly: readOnly, readOnly: readOnly,
...@@ -424,6 +425,7 @@ func (provisioner *quobyteVolumeProvisioner) Provision(selectedNode *v1.Node, al ...@@ -424,6 +425,7 @@ func (provisioner *quobyteVolumeProvisioner) Provision(selectedNode *v1.Node, al
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)), v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
} }
pv.Spec.MountOptions = provisioner.options.MountOptions pv.Spec.MountOptions = provisioner.options.MountOptions
pv.Spec.PersistentVolumeSource.Quobyte.Tenant = provisioner.tenant
return pv, nil return pv, nil
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -3421,6 +3421,11 @@ message QuobyteVolumeSource { ...@@ -3421,6 +3421,11 @@ message QuobyteVolumeSource {
// Default is no group // Default is no group
// +optional // +optional
optional string group = 5; optional string group = 5;
// Tenant owning the given Quobyte volume in the Backend
// Used with dynamically provisioned Quobyte volumes, value is set by the plugin
// +optional
optional string tenant = 6;
} }
// Represents a Rados Block Device mount that lasts the lifetime of a pod. // Represents a Rados Block Device mount that lasts the lifetime of a pod.
......
...@@ -955,6 +955,11 @@ type QuobyteVolumeSource struct { ...@@ -955,6 +955,11 @@ type QuobyteVolumeSource struct {
// Default is no group // Default is no group
// +optional // +optional
Group string `json:"group,omitempty" protobuf:"bytes,5,opt,name=group"` Group string `json:"group,omitempty" protobuf:"bytes,5,opt,name=group"`
// Tenant owning the given Quobyte volume in the Backend
// Used with dynamically provisioned Quobyte volumes, value is set by the plugin
// +optional
Tenant string `json:"tenant,omitempty" protobuf:"bytes,6,opt,name=tenant"`
} }
// FlexPersistentVolumeSource represents a generic persistent volume resource that is // FlexPersistentVolumeSource represents a generic persistent volume resource that is
......
...@@ -1678,6 +1678,7 @@ var map_QuobyteVolumeSource = map[string]string{ ...@@ -1678,6 +1678,7 @@ var map_QuobyteVolumeSource = map[string]string{
"readOnly": "ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.", "readOnly": "ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.",
"user": "User to map volume access to Defaults to serivceaccount user", "user": "User to map volume access to Defaults to serivceaccount user",
"group": "Group to map volume access to Default is no group", "group": "Group to map volume access to Default is no group",
"tenant": "Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin",
} }
func (QuobyteVolumeSource) SwaggerDoc() map[string]string { func (QuobyteVolumeSource) SwaggerDoc() map[string]string {
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
package quobyte package quobyte
import ( import (
"log"
"net/http" "net/http"
"regexp" "regexp"
) )
...@@ -15,6 +14,8 @@ const ( ...@@ -15,6 +14,8 @@ const (
RetryOncePerTarget string = "ONCE_PER_TARGET" RetryOncePerTarget string = "ONCE_PER_TARGET"
) )
var UUIDValidator = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")
type QuobyteClient struct { type QuobyteClient struct {
client *http.Client client *http.Client
url string url string
...@@ -45,24 +46,50 @@ func NewQuobyteClient(url string, username string, password string) *QuobyteClie ...@@ -45,24 +46,50 @@ func NewQuobyteClient(url string, username string, password string) *QuobyteClie
// CreateVolume creates a new Quobyte volume. Its root directory will be owned by given user and group // CreateVolume creates a new Quobyte volume. Its root directory will be owned by given user and group
func (client QuobyteClient) CreateVolume(request *CreateVolumeRequest) (string, error) { func (client QuobyteClient) CreateVolume(request *CreateVolumeRequest) (string, error) {
var response volumeUUID var response volumeUUID
tenantUUID, err := client.GetTenantUUID(request.TenantID)
if request.TenantID != "" && !IsValidUUID(request.TenantID) {
log.Printf("Tenant name resolution: Resolving %s to UUID\n", request.TenantID)
tenantUUID, err := client.ResolveTenantNameToUUID(request.TenantID)
if err != nil { if err != nil {
return "", err return "", err
} }
request.TenantID = tenantUUID request.TenantID = tenantUUID
}
if err := client.sendRequest("createVolume", request, &response); err != nil { if err = client.sendRequest("createVolume", request, &response); err != nil {
return "", err return "", err
} }
return response.VolumeUUID, nil return response.VolumeUUID, nil
} }
// GetVolumeUUID resolves the volumeUUID for the given volume and tenant name.
// This method should be used when it is not clear if the given string is volume UUID or Name.
func (client QuobyteClient) GetVolumeUUID(volume, tenant string) (string, error) {
if len(volume) != 0 && !IsValidUUID(volume) {
tenantUUID, err := client.GetTenantUUID(tenant)
if err != nil {
return "", err
}
volUUID, err := client.ResolveVolumeNameToUUID(volume, tenantUUID)
if err != nil {
return "", err
}
return volUUID, nil
}
return volume, nil
}
// GetTenantUUID resolves the tenatnUUID for the given name
// This method should be used when it is not clear if the given string is Tenant UUID or Name.
func (client QuobyteClient) GetTenantUUID(tenant string) (string, error) {
if len(tenant) != 0 && !IsValidUUID(tenant) {
tenantUUID, err := client.ResolveTenantNameToUUID(tenant)
if err != nil {
return "", err
}
return tenantUUID, nil
}
return tenant, nil
}
// ResolveVolumeNameToUUID resolves a volume name to a UUID // ResolveVolumeNameToUUID resolves a volume name to a UUID
func (client *QuobyteClient) ResolveVolumeNameToUUID(volumeName, tenant string) (string, error) { func (client *QuobyteClient) ResolveVolumeNameToUUID(volumeName, tenant string) (string, error) {
request := &resolveVolumeNameRequest{ request := &resolveVolumeNameRequest{
...@@ -77,6 +104,18 @@ func (client *QuobyteClient) ResolveVolumeNameToUUID(volumeName, tenant string) ...@@ -77,6 +104,18 @@ func (client *QuobyteClient) ResolveVolumeNameToUUID(volumeName, tenant string)
return response.VolumeUUID, nil return response.VolumeUUID, nil
} }
// DeleteVolumeByResolvingNamesToUUID deletes the volume by resolving the volume name and tenant name to
// respective UUID if required.
// This method should be used if the given volume, tenant information is name or UUID.
func (client *QuobyteClient) DeleteVolumeByResolvingNamesToUUID(volume, tenant string) error {
volumeUUID, err := client.GetVolumeUUID(volume, tenant)
if err != nil {
return err
}
return client.DeleteVolume(volumeUUID)
}
// DeleteVolume deletes a Quobyte volume // DeleteVolume deletes a Quobyte volume
func (client *QuobyteClient) DeleteVolume(UUID string) error { func (client *QuobyteClient) DeleteVolume(UUID string) error {
return client.sendRequest( return client.sendRequest(
...@@ -182,10 +221,9 @@ func (client *QuobyteClient) SetTenant(tenantName string) (string, error) { ...@@ -182,10 +221,9 @@ func (client *QuobyteClient) SetTenant(tenantName string) (string, error) {
return response.TenantID, nil return response.TenantID, nil
} }
// IsValidUUID Validates given uuid // IsValidUUID Validates the given uuid
func IsValidUUID(uuid string) bool { func IsValidUUID(uuid string) bool {
r := regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$") return UUIDValidator.MatchString(uuid)
return r.MatchString(uuid)
} }
// ResolveTenantNameToUUID Returns UUID for given name, error if not found. // ResolveTenantNameToUUID Returns UUID for given name, error if not found.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment