Merge pull request #60118 from sbezverk/csi_core_credentials

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Adding credentials support for k8s core CSI  

PR implements changes proposed in: https://github.com/kubernetes/community/pull/1816

```release-note
CSI now allows credentials to be specified on CreateVolume/DeleteVolume, ControllerPublishVolume/ControllerUnpublishVolume, and NodePublishVolume/NodeUnpublishVolume operations
```
pull/6/head
Kubernetes Submit Queue 2018-02-24 12:36:11 -08:00 committed by GitHub
commit 8e8601a1cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 2638 additions and 1763 deletions

2
Godeps/Godeps.json generated
View File

@ -454,7 +454,7 @@
{ {
"ImportPath": "github.com/container-storage-interface/spec/lib/go/csi", "ImportPath": "github.com/container-storage-interface/spec/lib/go/csi",
"Comment": "v0.1.0-5-g7ab01a9", "Comment": "v0.1.0-5-g7ab01a9",
"Rev": "7ab01a90da87f9fef3ee1de0494962fdefaf7db7" "Rev": "91c189774c16b0661255943c09ea9d97d5a423e7"
}, },
{ {
"ImportPath": "github.com/containerd/console", "ImportPath": "github.com/containerd/console",

View File

@ -75317,6 +75317,10 @@
"volumeHandle" "volumeHandle"
], ],
"properties": { "properties": {
"controllerPublishSecretRef": {
"description": "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.",
"$ref": "#/definitions/io.k8s.api.core.v1.SecretReference"
},
"driver": { "driver": {
"description": "Driver is the name of the driver to use for this volume. Required.", "description": "Driver is the name of the driver to use for this volume. Required.",
"type": "string" "type": "string"
@ -75325,6 +75329,14 @@
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
"type": "string" "type": "string"
}, },
"nodePublishSecretRef": {
"description": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.",
"$ref": "#/definitions/io.k8s.api.core.v1.SecretReference"
},
"nodeStageSecretRef": {
"description": "NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.",
"$ref": "#/definitions/io.k8s.api.core.v1.SecretReference"
},
"readOnly": { "readOnly": {
"description": "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).", "description": "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).",
"type": "boolean" "type": "boolean"

View File

@ -20016,6 +20016,18 @@
"volumeAttributes": { "volumeAttributes": {
"type": "object", "type": "object",
"description": "Attributes of the volume to publish." "description": "Attributes of the volume to publish."
},
"controllerPublishSecretRef": {
"$ref": "v1.SecretReference",
"description": "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed."
},
"nodeStageSecretRef": {
"$ref": "v1.SecretReference",
"description": "NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed."
},
"nodePublishSecretRef": {
"$ref": "v1.SecretReference",
"description": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed."
} }
} }
}, },

View File

@ -7885,6 +7885,27 @@ Examples:<br>
<td class="tableblock halign-left valign-top"><p class="tableblock">object</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">object</p></td>
<td class="tableblock halign-left valign-top"></td> <td class="tableblock halign-left valign-top"></td>
</tr> </tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">controllerPublishSecretRef</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_secretreference">v1.SecretReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">nodeStageSecretRef</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_secretreference">v1.SecretReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">nodePublishSecretRef</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_secretreference">v1.SecretReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody> </tbody>
</table> </table>

View File

@ -30,7 +30,7 @@ func getClaimRefNamespace(pv *api.PersistentVolume) string {
} }
// Visitor is called with each object's namespace and name, and returns true if visiting should continue // Visitor is called with each object's namespace and name, and returns true if visiting should continue
type Visitor func(namespace, name string) (shouldContinue bool) type Visitor func(namespace, name string, kubeletVisible bool) (shouldContinue bool)
// VisitPVSecretNames invokes the visitor function with the name of every secret // VisitPVSecretNames invokes the visitor function with the name of every secret
// referenced by the PV spec. If visitor returns false, visiting is short-circuited. // referenced by the PV spec. If visitor returns false, visiting is short-circuited.
@ -40,11 +40,11 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
switch { switch {
case source.AzureFile != nil: case source.AzureFile != nil:
if source.AzureFile.SecretNamespace != nil && len(*source.AzureFile.SecretNamespace) > 0 { if source.AzureFile.SecretNamespace != nil && len(*source.AzureFile.SecretNamespace) > 0 {
if len(source.AzureFile.SecretName) > 0 && !visitor(*source.AzureFile.SecretNamespace, source.AzureFile.SecretName) { if len(source.AzureFile.SecretName) > 0 && !visitor(*source.AzureFile.SecretNamespace, source.AzureFile.SecretName, true /* kubeletVisible */) {
return false return false
} }
} else { } else {
if len(source.AzureFile.SecretName) > 0 && !visitor(getClaimRefNamespace(pv), source.AzureFile.SecretName) { if len(source.AzureFile.SecretName) > 0 && !visitor(getClaimRefNamespace(pv), source.AzureFile.SecretName, true /* kubeletVisible */) {
return false return false
} }
} }
@ -57,7 +57,7 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
// use the secret namespace if namespace is set // use the secret namespace if namespace is set
ns = source.CephFS.SecretRef.Namespace ns = source.CephFS.SecretRef.Namespace
} }
if !visitor(ns, source.CephFS.SecretRef.Name) { if !visitor(ns, source.CephFS.SecretRef.Name, true /* kubeletVisible */) {
return false return false
} }
} }
@ -69,7 +69,7 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
// use the secret namespace if namespace is set // use the secret namespace if namespace is set
ns = source.FlexVolume.SecretRef.Namespace ns = source.FlexVolume.SecretRef.Namespace
} }
if !visitor(ns, source.FlexVolume.SecretRef.Name) { if !visitor(ns, source.FlexVolume.SecretRef.Name, true /* kubeletVisible */) {
return false return false
} }
} }
@ -81,7 +81,7 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
// use the secret namespace if namespace is set // use the secret namespace if namespace is set
ns = source.RBD.SecretRef.Namespace ns = source.RBD.SecretRef.Namespace
} }
if !visitor(ns, source.RBD.SecretRef.Name) { if !visitor(ns, source.RBD.SecretRef.Name, true /* kubeletVisible */) {
return false return false
} }
} }
@ -91,7 +91,7 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
if source.ScaleIO.SecretRef != nil && len(source.ScaleIO.SecretRef.Namespace) > 0 { if source.ScaleIO.SecretRef != nil && len(source.ScaleIO.SecretRef.Namespace) > 0 {
ns = source.ScaleIO.SecretRef.Namespace ns = source.ScaleIO.SecretRef.Namespace
} }
if !visitor(ns, source.ScaleIO.SecretRef.Name) { if !visitor(ns, source.ScaleIO.SecretRef.Name, true /* kubeletVisible */) {
return false return false
} }
} }
@ -103,14 +103,30 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
// use the secret namespace if namespace is set // use the secret namespace if namespace is set
ns = source.ISCSI.SecretRef.Namespace ns = source.ISCSI.SecretRef.Namespace
} }
if !visitor(ns, source.ISCSI.SecretRef.Name) { if !visitor(ns, source.ISCSI.SecretRef.Name, true /* kubeletVisible */) {
return false return false
} }
} }
case source.StorageOS != nil: case source.StorageOS != nil:
if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Namespace, source.StorageOS.SecretRef.Name) { if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Namespace, source.StorageOS.SecretRef.Name, true /* kubeletVisible */) {
return false return false
} }
case source.CSI != nil:
if source.CSI.ControllerPublishSecretRef != nil {
if !visitor(source.CSI.ControllerPublishSecretRef.Namespace, source.CSI.ControllerPublishSecretRef.Name, false /* kubeletVisible */) {
return false
}
}
if source.CSI.NodePublishSecretRef != nil {
if !visitor(source.CSI.NodePublishSecretRef.Namespace, source.CSI.NodePublishSecretRef.Name, true /* kubeletVisible */) {
return false
}
}
if source.CSI.NodeStageSecretRef != nil {
if !visitor(source.CSI.NodeStageSecretRef.Namespace, source.CSI.NodeStageSecretRef.Name, true /* kubeletVisible */) {
return false
}
}
} }
return true return true
} }

View File

@ -117,11 +117,32 @@ func TestPVSecrets(t *testing.T) {
SecretRef: &api.ObjectReference{ SecretRef: &api.ObjectReference{
Name: "Spec.PersistentVolumeSource.StorageOS.SecretRef", Name: "Spec.PersistentVolumeSource.StorageOS.SecretRef",
Namespace: "storageosns"}}}}}, Namespace: "storageosns"}}}}},
{Spec: api.PersistentVolumeSpec{
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
PersistentVolumeSource: api.PersistentVolumeSource{
CSI: &api.CSIPersistentVolumeSource{
ControllerPublishSecretRef: &api.SecretReference{
Name: "Spec.PersistentVolumeSource.CSI.ControllerPublishSecretRef",
Namespace: "csi"}}}}},
{Spec: api.PersistentVolumeSpec{
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
PersistentVolumeSource: api.PersistentVolumeSource{
CSI: &api.CSIPersistentVolumeSource{
NodePublishSecretRef: &api.SecretReference{
Name: "Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
Namespace: "csi"}}}}},
{Spec: api.PersistentVolumeSpec{
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
PersistentVolumeSource: api.PersistentVolumeSource{
CSI: &api.CSIPersistentVolumeSource{
NodeStageSecretRef: &api.SecretReference{
Name: "Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
Namespace: "csi"}}}}},
} }
extractedNames := sets.NewString() extractedNames := sets.NewString()
extractedNamesWithNamespace := sets.NewString() extractedNamesWithNamespace := sets.NewString()
for _, pv := range pvs { for _, pv := range pvs {
VisitPVSecretNames(pv, func(namespace, name string) bool { VisitPVSecretNames(pv, func(namespace, name string, kubeletVisible bool) bool {
extractedNames.Insert(name) extractedNames.Insert(name)
extractedNamesWithNamespace.Insert(namespace + "/" + name) extractedNamesWithNamespace.Insert(namespace + "/" + name)
return true return true
@ -143,6 +164,9 @@ func TestPVSecrets(t *testing.T) {
"Spec.PersistentVolumeSource.ScaleIO.SecretRef", "Spec.PersistentVolumeSource.ScaleIO.SecretRef",
"Spec.PersistentVolumeSource.ISCSI.SecretRef", "Spec.PersistentVolumeSource.ISCSI.SecretRef",
"Spec.PersistentVolumeSource.StorageOS.SecretRef", "Spec.PersistentVolumeSource.StorageOS.SecretRef",
"Spec.PersistentVolumeSource.CSI.ControllerPublishSecretRef",
"Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
"Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
) )
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.PersistentVolume{})) secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.PersistentVolume{}))
secretPaths = secretPaths.Difference(excludedSecretPaths) secretPaths = secretPaths.Difference(excludedSecretPaths)
@ -184,6 +208,10 @@ func TestPVSecrets(t *testing.T) {
"iscsi/Spec.PersistentVolumeSource.ISCSI.SecretRef", "iscsi/Spec.PersistentVolumeSource.ISCSI.SecretRef",
"storageosns/Spec.PersistentVolumeSource.StorageOS.SecretRef", "storageosns/Spec.PersistentVolumeSource.StorageOS.SecretRef",
"csi/Spec.PersistentVolumeSource.CSI.ControllerPublishSecretRef",
"csi/Spec.PersistentVolumeSource.CSI.NodePublishSecretRef",
"csi/Spec.PersistentVolumeSource.CSI.NodeStageSecretRef",
) )
if missingNames := expectedNamespacedNames.Difference(extractedNamesWithNamespace); len(missingNames) > 0 { if missingNames := expectedNamespacedNames.Difference(extractedNamesWithNamespace); len(missingNames) > 0 {
t.Logf("Missing expected namespaced names:\n%s", strings.Join(missingNames.List(), "\n")) t.Logf("Missing expected namespaced names:\n%s", strings.Join(missingNames.List(), "\n"))

View File

@ -1638,6 +1638,30 @@ type CSIPersistentVolumeSource struct {
// Attributes of the volume to publish. // Attributes of the volume to publish.
// +optional // +optional
VolumeAttributes map[string]string VolumeAttributes map[string]string
// ControllerPublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// ControllerPublishVolume and ControllerUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
ControllerPublishSecretRef *SecretReference
// NodeStageSecretRef is a reference to the secret object containing sensitive
// information to pass to the CSI driver to complete the CSI NodeStageVolume
// and NodeStageVolume and NodeUnstageVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
NodeStageSecretRef *SecretReference
// NodePublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// NodePublishVolume and NodeUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
NodePublishSecretRef *SecretReference
} }
// ContainerPort represents a network port in a single container // ContainerPort represents a network port in a single container

View File

@ -623,6 +623,9 @@ func autoConvert_v1_CSIPersistentVolumeSource_To_core_CSIPersistentVolumeSource(
out.ReadOnly = in.ReadOnly out.ReadOnly = in.ReadOnly
out.FSType = in.FSType out.FSType = in.FSType
out.VolumeAttributes = *(*map[string]string)(unsafe.Pointer(&in.VolumeAttributes)) out.VolumeAttributes = *(*map[string]string)(unsafe.Pointer(&in.VolumeAttributes))
out.ControllerPublishSecretRef = (*core.SecretReference)(unsafe.Pointer(in.ControllerPublishSecretRef))
out.NodeStageSecretRef = (*core.SecretReference)(unsafe.Pointer(in.NodeStageSecretRef))
out.NodePublishSecretRef = (*core.SecretReference)(unsafe.Pointer(in.NodePublishSecretRef))
return nil return nil
} }
@ -637,6 +640,9 @@ func autoConvert_core_CSIPersistentVolumeSource_To_v1_CSIPersistentVolumeSource(
out.ReadOnly = in.ReadOnly out.ReadOnly = in.ReadOnly
out.FSType = in.FSType out.FSType = in.FSType
out.VolumeAttributes = *(*map[string]string)(unsafe.Pointer(&in.VolumeAttributes)) out.VolumeAttributes = *(*map[string]string)(unsafe.Pointer(&in.VolumeAttributes))
out.ControllerPublishSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.ControllerPublishSecretRef))
out.NodeStageSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.NodeStageSecretRef))
out.NodePublishSecretRef = (*v1.SecretReference)(unsafe.Pointer(in.NodePublishSecretRef))
return nil return nil
} }

View File

@ -1452,6 +1452,45 @@ func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldP
allErrs = append(allErrs, field.Required(fldPath.Child("volumeHandle"), "")) allErrs = append(allErrs, field.Required(fldPath.Child("volumeHandle"), ""))
} }
if csi.ControllerPublishSecretRef != nil {
if len(csi.ControllerPublishSecretRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("controllerPublishSecretRef", "name"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerPublishSecretRef.Name, fldPath.Child("name"))...)
}
if len(csi.ControllerPublishSecretRef.Namespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("controllerPublishSecretRef", "namespace"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.ControllerPublishSecretRef.Namespace, fldPath.Child("namespace"))...)
}
}
if csi.NodePublishSecretRef != nil {
if len(csi.NodePublishSecretRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef ", "name"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.NodePublishSecretRef.Name, fldPath.Child("name"))...)
}
if len(csi.NodePublishSecretRef.Namespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef ", "namespace"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.NodePublishSecretRef.Namespace, fldPath.Child("namespace"))...)
}
}
if csi.NodeStageSecretRef != nil {
if len(csi.NodeStageSecretRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("nodeStageSecretRef", "name"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.NodeStageSecretRef.Name, fldPath.Child("name"))...)
}
if len(csi.NodeStageSecretRef.Namespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("nodeStageSecretRef", "namespace"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.NodeStageSecretRef.Namespace, fldPath.Child("namespace"))...)
}
}
return allErrs return allErrs
} }

View File

@ -256,6 +256,33 @@ func (in *CSIPersistentVolumeSource) DeepCopyInto(out *CSIPersistentVolumeSource
(*out)[key] = val (*out)[key] = val
} }
} }
if in.ControllerPublishSecretRef != nil {
in, out := &in.ControllerPublishSecretRef, &out.ControllerPublishSecretRef
if *in == nil {
*out = nil
} else {
*out = new(SecretReference)
**out = **in
}
}
if in.NodeStageSecretRef != nil {
in, out := &in.NodeStageSecretRef, &out.NodeStageSecretRef
if *in == nil {
*out = nil
} else {
*out = new(SecretReference)
**out = **in
}
}
if in.NodePublishSecretRef != nil {
in, out := &in.NodePublishSecretRef, &out.NodePublishSecretRef
if *in == nil {
*out = nil
} else {
*out = new(SecretReference)
**out = **in
}
}
return return
} }

View File

@ -7,6 +7,7 @@ go_library(
"csi_client.go", "csi_client.go",
"csi_mounter.go", "csi_mounter.go",
"csi_plugin.go", "csi_plugin.go",
"csi_util.go",
], ],
importpath = "k8s.io/kubernetes/pkg/volume/csi", importpath = "k8s.io/kubernetes/pkg/volume/csi",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],

View File

@ -32,7 +32,6 @@ import (
type csiClient interface { type csiClient interface {
AssertSupportedVersion(ctx grpctx.Context, ver *csipb.Version) error AssertSupportedVersion(ctx grpctx.Context, ver *csipb.Version) error
NodeProbe(ctx grpctx.Context, ver *csipb.Version) error
NodePublishVolume( NodePublishVolume(
ctx grpctx.Context, ctx grpctx.Context,
volumeid string, volumeid string,
@ -41,9 +40,15 @@ type csiClient interface {
accessMode api.PersistentVolumeAccessMode, accessMode api.PersistentVolumeAccessMode,
volumeInfo map[string]string, volumeInfo map[string]string,
volumeAttribs map[string]string, volumeAttribs map[string]string,
nodePublishCredentials map[string]string,
fsType string, fsType string,
) error ) error
NodeUnpublishVolume(ctx grpctx.Context, volID string, targetPath string) error NodeUnpublishVolume(
ctx grpctx.Context,
volID string,
targetPath string,
nodeUnpublishCredentials map[string]string,
) error
} }
// csiClient encapsulates all csi-plugin methods // csiClient encapsulates all csi-plugin methods
@ -146,13 +151,6 @@ func (c *csiDriverClient) AssertSupportedVersion(ctx grpctx.Context, ver *csipb.
return nil return nil
} }
func (c *csiDriverClient) NodeProbe(ctx grpctx.Context, ver *csipb.Version) error {
glog.V(4).Info(log("sending NodeProbe rpc call to csi driver: [version %v]", ver))
req := &csipb.NodeProbeRequest{Version: ver}
_, err := c.nodeClient.NodeProbe(ctx, req)
return err
}
func (c *csiDriverClient) NodePublishVolume( func (c *csiDriverClient) NodePublishVolume(
ctx grpctx.Context, ctx grpctx.Context,
volID string, volID string,
@ -161,6 +159,7 @@ func (c *csiDriverClient) NodePublishVolume(
accessMode api.PersistentVolumeAccessMode, accessMode api.PersistentVolumeAccessMode,
volumeInfo map[string]string, volumeInfo map[string]string,
volumeAttribs map[string]string, volumeAttribs map[string]string,
nodePublishCredentials map[string]string,
fsType string, fsType string,
) error { ) error {
glog.V(4).Info(log("calling NodePublishVolume rpc [volid=%s,target_path=%s]", volID, targetPath)) glog.V(4).Info(log("calling NodePublishVolume rpc [volid=%s,target_path=%s]", volID, targetPath))
@ -182,7 +181,7 @@ func (c *csiDriverClient) NodePublishVolume(
Readonly: readOnly, Readonly: readOnly,
PublishInfo: volumeInfo, PublishInfo: volumeInfo,
VolumeAttributes: volumeAttribs, VolumeAttributes: volumeAttribs,
NodePublishCredentials: nodePublishCredentials,
VolumeCapability: &csipb.VolumeCapability{ VolumeCapability: &csipb.VolumeCapability{
AccessMode: &csipb.VolumeCapability_AccessMode{ AccessMode: &csipb.VolumeCapability_AccessMode{
Mode: asCSIAccessMode(accessMode), Mode: asCSIAccessMode(accessMode),
@ -199,7 +198,7 @@ func (c *csiDriverClient) NodePublishVolume(
return err return err
} }
func (c *csiDriverClient) NodeUnpublishVolume(ctx grpctx.Context, volID string, targetPath string) error { func (c *csiDriverClient) NodeUnpublishVolume(ctx grpctx.Context, volID string, targetPath string, nodeUnpublishCredentials map[string]string) error {
glog.V(4).Info(log("calling NodeUnpublishVolume rpc: [volid=%s, target_path=%s", volID, targetPath)) glog.V(4).Info(log("calling NodeUnpublishVolume rpc: [volid=%s, target_path=%s", volID, targetPath))
if volID == "" { if volID == "" {
return errors.New("missing volume id") return errors.New("missing volume id")
@ -216,6 +215,7 @@ func (c *csiDriverClient) NodeUnpublishVolume(ctx grpctx.Context, volID string,
Version: csiVersion, Version: csiVersion,
VolumeId: volID, VolumeId: volID,
TargetPath: targetPath, TargetPath: targetPath,
NodeUnpublishCredentials: nodeUnpublishCredentials,
} }
_, err := c.nodeClient.NodeUnpublishVolume(ctx, req) _, err := c.nodeClient.NodeUnpublishVolume(ctx, req)

View File

@ -68,28 +68,6 @@ func TestClientAssertSupportedVersion(t *testing.T) {
} }
} }
func TestClientNodeProbe(t *testing.T) {
testCases := []struct {
testName string
ver *csipb.Version
mustFail bool
err error
}{
{testName: "supported version", ver: &csipb.Version{Major: 0, Minor: 1, Patch: 0}},
{testName: "grpc error", ver: &csipb.Version{Major: 0, Minor: 1, Patch: 0}, mustFail: true, err: errors.New("grpc error")},
}
for _, tc := range testCases {
t.Logf("test case: %s", tc.testName)
client := setupClient(t)
client.nodeClient.(*fake.NodeClient).SetNextError(tc.err)
err := client.NodeProbe(grpctx.Background(), tc.ver)
if tc.mustFail && err == nil {
t.Error("test must fail, but err = nil")
}
}
}
func TestClientNodePublishVolume(t *testing.T) { func TestClientNodePublishVolume(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
@ -119,6 +97,7 @@ func TestClientNodePublishVolume(t *testing.T) {
api.ReadWriteOnce, api.ReadWriteOnce,
map[string]string{"device": "/dev/null"}, map[string]string{"device": "/dev/null"},
map[string]string{"attr0": "val0"}, map[string]string{"attr0": "val0"},
map[string]string{},
tc.fsType, tc.fsType,
) )
@ -147,7 +126,8 @@ func TestClientNodeUnpublishVolume(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Logf("test case: %s", tc.name) t.Logf("test case: %s", tc.name)
client.nodeClient.(*fake.NodeClient).SetNextError(tc.err) client.nodeClient.(*fake.NodeClient).SetNextError(tc.err)
err := client.NodeUnpublishVolume(grpctx.Background(), tc.volID, tc.targetPath) nodeUnpublishCredentials := map[string]string{}
err := client.NodeUnpublishVolume(grpctx.Background(), tc.volID, tc.targetPath, nodeUnpublishCredentials)
if tc.mustFail && err == nil { if tc.mustFail && err == nil {
t.Error("test must fail, but err is nil") t.Error("test must fail, but err is nil")
} }

View File

@ -87,8 +87,6 @@ func getTargetPath(uid types.UID, specVolumeID string, host volume.VolumeHost) s
var _ volume.Mounter = &csiMountMgr{} var _ volume.Mounter = &csiMountMgr{}
func (c *csiMountMgr) CanMount() error { func (c *csiMountMgr) CanMount() error {
//TODO (vladimirvivien) use this method to probe controller using CSI.NodeProbe() call
// to ensure Node service is ready in the CSI plugin
return nil return nil
} }
@ -129,13 +127,6 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
return err return err
} }
// probe driver
// TODO (vladimirvivien) move probe call where it is done only when it is needed.
if err := csi.NodeProbe(ctx, csiVersion); err != nil {
glog.Error(log("mounter.SetUpAt failed to probe driver: %v", err))
return err
}
// search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName // search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
if c.volumeInfo == nil { if c.volumeInfo == nil {
attachment, err := c.k8s.StorageV1beta1().VolumeAttachments().Get(attachID, meta.GetOptions{}) attachment, err := c.k8s.StorageV1beta1().VolumeAttachments().Get(attachID, meta.GetOptions{})
@ -188,7 +179,10 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
if len(fsType) == 0 { if len(fsType) == 0 {
fsType = defaultFSType fsType = defaultFSType
} }
nodePublishCredentials := map[string]string{}
if csiSource.NodePublishSecretRef != nil {
nodePublishCredentials = getCredentialsFromSecret(c.k8s, csiSource.NodePublishSecretRef)
}
err = csi.NodePublishVolume( err = csi.NodePublishVolume(
ctx, ctx,
c.volumeID, c.volumeID,
@ -197,6 +191,7 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
accessMode, accessMode,
c.volumeInfo, c.volumeInfo,
attribs, attribs,
nodePublishCredentials,
fsType, fsType,
) )
@ -244,6 +239,12 @@ func (c *csiMountMgr) TearDownAt(dir string) error {
return nil return nil
} }
csiSource, err := getCSISourceFromSpec(c.spec)
if err != nil {
glog.Error(log("mounter.TearDownAt failed to get CSI persistent source: %v", err))
return err
}
// load volume info from file // load volume info from file
dataDir := path.Dir(dir) // dropoff /mount at end dataDir := path.Dir(dir) // dropoff /mount at end
data, err := loadVolumeData(dataDir, volDataFileName) data, err := loadVolumeData(dataDir, volDataFileName)
@ -273,7 +274,11 @@ func (c *csiMountMgr) TearDownAt(dir string) error {
return err return err
} }
if err := csi.NodeUnpublishVolume(ctx, volID, dir); err != nil { nodeUnpublishCredentials := map[string]string{}
if csiSource.NodePublishSecretRef != nil {
nodeUnpublishCredentials = getCredentialsFromSecret(c.k8s, csiSource.NodePublishSecretRef)
}
if err := csi.NodeUnpublishVolume(ctx, volID, dir, nodeUnpublishCredentials); err != nil {
glog.Errorf(log("mounter.TearDownAt failed: %v", err)) glog.Errorf(log("mounter.TearDownAt failed: %v", err))
return err return err
} }

View File

@ -0,0 +1,38 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package csi
import (
"github.com/golang/glog"
api "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
func getCredentialsFromSecret(k8s kubernetes.Interface, secretRef *api.SecretReference) map[string]string {
credentials := map[string]string{}
secret, err := k8s.CoreV1().Secrets(secretRef.Namespace).Get(secretRef.Name, meta.GetOptions{})
if err != nil {
glog.Warningf("failed to find the secret %s in the namespace %s with error: %v\n", secretRef.Name, secretRef.Namespace, err)
return credentials
}
for key, value := range secret.Data {
credentials[key] = string(value)
}
return credentials
}

View File

@ -67,6 +67,16 @@ func (f *IdentityClient) GetPluginInfo(ctx context.Context, in *csipb.GetPluginI
return nil, nil return nil, nil
} }
// GetPluginCapabilities implements csi method
func (f *IdentityClient) GetPluginCapabilities(ctx context.Context, in *csipb.GetPluginCapabilitiesRequest, opts ...grpc.CallOption) (*csipb.GetPluginCapabilitiesResponse, error) {
return nil, nil
}
// Probe implements csi method
func (f *IdentityClient) Probe(ctx context.Context, in *csipb.ProbeRequest, opts ...grpc.CallOption) (*csipb.ProbeResponse, error) {
return nil, nil
}
// NodeClient returns CSI node client // NodeClient returns CSI node client
type NodeClient struct { type NodeClient struct {
nodePublishedVolumes map[string]string nodePublishedVolumes map[string]string
@ -110,17 +120,6 @@ func (f *NodeClient) NodePublishVolume(ctx grpctx.Context, req *csipb.NodePublis
return &csipb.NodePublishVolumeResponse{}, nil return &csipb.NodePublishVolumeResponse{}, nil
} }
// NodeProbe implements csi NodeProbe
func (f *NodeClient) NodeProbe(ctx context.Context, req *csipb.NodeProbeRequest, opts ...grpc.CallOption) (*csipb.NodeProbeResponse, error) {
if f.nextErr != nil {
return nil, f.nextErr
}
if req.Version == nil {
return nil, errors.New("missing version")
}
return &csipb.NodeProbeResponse{}, nil
}
// NodeUnpublishVolume implements csi method // NodeUnpublishVolume implements csi method
func (f *NodeClient) NodeUnpublishVolume(ctx context.Context, req *csipb.NodeUnpublishVolumeRequest, opts ...grpc.CallOption) (*csipb.NodeUnpublishVolumeResponse, error) { func (f *NodeClient) NodeUnpublishVolume(ctx context.Context, req *csipb.NodeUnpublishVolumeRequest, opts ...grpc.CallOption) (*csipb.NodeUnpublishVolumeResponse, error) {
if f.nextErr != nil { if f.nextErr != nil {
@ -147,6 +146,16 @@ func (f *NodeClient) NodeGetCapabilities(ctx context.Context, in *csipb.NodeGetC
return nil, nil return nil, nil
} }
// NodeStageVolume implements csi method
func (f *NodeClient) NodeStageVolume(ctx context.Context, in *csipb.NodeStageVolumeRequest, opts ...grpc.CallOption) (*csipb.NodeStageVolumeResponse, error) {
return nil, nil
}
// NodeUnstageVolume implements csi method
func (f *NodeClient) NodeUnstageVolume(ctx context.Context, in *csipb.NodeUnstageVolumeRequest, opts ...grpc.CallOption) (*csipb.NodeUnstageVolumeResponse, error) {
return nil, nil
}
// ControllerClient represents a CSI Controller client // ControllerClient represents a CSI Controller client
type ControllerClient struct { type ControllerClient struct {
nextCapabilities []*csipb.ControllerServiceCapability nextCapabilities []*csipb.ControllerServiceCapability
@ -224,8 +233,3 @@ func (f *ControllerClient) ListVolumes(ctx context.Context, in *csipb.ListVolume
func (f *ControllerClient) GetCapacity(ctx context.Context, in *csipb.GetCapacityRequest, opts ...grpc.CallOption) (*csipb.GetCapacityResponse, error) { func (f *ControllerClient) GetCapacity(ctx context.Context, in *csipb.GetCapacityRequest, opts ...grpc.CallOption) (*csipb.GetCapacityResponse, error) {
return nil, nil return nil, nil
} }
// ControllerProbe implements csi method
func (f *ControllerClient) ControllerProbe(ctx context.Context, in *csipb.ControllerProbeRequest, opts ...grpc.CallOption) (*csipb.ControllerProbeResponse, error) {
return nil, nil
}

View File

@ -253,9 +253,11 @@ func (g *Graph) AddPV(pv *api.PersistentVolume) {
// since we don't know the other end of the pvc -> pod -> node chain (or it may not even exist yet), we can't decorate these edges with kubernetes node info // since we don't know the other end of the pvc -> pod -> node chain (or it may not even exist yet), we can't decorate these edges with kubernetes node info
g.graph.SetEdge(simple.Edge{F: pvVertex, T: g.getOrCreateVertex_locked(pvcVertexType, pv.Spec.ClaimRef.Namespace, pv.Spec.ClaimRef.Name)}) g.graph.SetEdge(simple.Edge{F: pvVertex, T: g.getOrCreateVertex_locked(pvcVertexType, pv.Spec.ClaimRef.Namespace, pv.Spec.ClaimRef.Name)})
pvutil.VisitPVSecretNames(pv, func(namespace, secret string) bool { pvutil.VisitPVSecretNames(pv, func(namespace, secret string, kubeletVisible bool) bool {
// This grants access to the named secret in the same namespace as the bound PVC // This grants access to the named secret in the same namespace as the bound PVC
if kubeletVisible {
g.graph.SetEdge(simple.Edge{F: g.getOrCreateVertex_locked(secretVertexType, namespace, secret), T: pvVertex}) g.graph.SetEdge(simple.Edge{F: g.getOrCreateVertex_locked(secretVertexType, namespace, secret), T: pvVertex})
}
return true return true
}) })
} }

File diff suppressed because it is too large Load Diff

View File

@ -195,6 +195,30 @@ message CSIPersistentVolumeSource {
// Attributes of the volume to publish. // Attributes of the volume to publish.
// +optional // +optional
map<string, string> volumeAttributes = 5; map<string, string> volumeAttributes = 5;
// ControllerPublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// ControllerPublishVolume and ControllerUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
optional SecretReference controllerPublishSecretRef = 6;
// NodeStageSecretRef is a reference to the secret object containing sensitive
// information to pass to the CSI driver to complete the CSI NodeStageVolume
// and NodeStageVolume and NodeUnstageVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
optional SecretReference nodeStageSecretRef = 7;
// NodePublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// NodePublishVolume and NodeUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
optional SecretReference nodePublishSecretRef = 8;
} }
// Adds and removes POSIX capabilities from running containers. // Adds and removes POSIX capabilities from running containers.

View File

@ -1753,6 +1753,30 @@ type CSIPersistentVolumeSource struct {
// Attributes of the volume to publish. // Attributes of the volume to publish.
// +optional // +optional
VolumeAttributes map[string]string `json:"volumeAttributes,omitempty" protobuf:"bytes,5,rep,name=volumeAttributes"` VolumeAttributes map[string]string `json:"volumeAttributes,omitempty" protobuf:"bytes,5,rep,name=volumeAttributes"`
// ControllerPublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// ControllerPublishVolume and ControllerUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
ControllerPublishSecretRef *SecretReference `json:"controllerPublishSecretRef,omitempty" protobuf:"bytes,6,opt,name=controllerPublishSecretRef"`
// NodeStageSecretRef is a reference to the secret object containing sensitive
// information to pass to the CSI driver to complete the CSI NodeStageVolume
// and NodeStageVolume and NodeUnstageVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
NodeStageSecretRef *SecretReference `json:"nodeStageSecretRef,omitempty" protobuf:"bytes,7,opt,name=nodeStageSecretRef"`
// NodePublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// NodePublishVolume and NodeUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secrets are passed.
// +optional
NodePublishSecretRef *SecretReference `json:"nodePublishSecretRef,omitempty" protobuf:"bytes,8,opt,name=nodePublishSecretRef"`
} }
// ContainerPort represents a network port in a single container. // ContainerPort represents a network port in a single container.

View File

@ -123,6 +123,9 @@ var map_CSIPersistentVolumeSource = map[string]string{
"readOnly": "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).", "readOnly": "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).",
"fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", "fsType": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.",
"volumeAttributes": "Attributes of the volume to publish.", "volumeAttributes": "Attributes of the volume to publish.",
"controllerPublishSecretRef": "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.",
"nodeStageSecretRef": "NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.",
"nodePublishSecretRef": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.",
} }
func (CSIPersistentVolumeSource) SwaggerDoc() map[string]string { func (CSIPersistentVolumeSource) SwaggerDoc() map[string]string {

View File

@ -256,6 +256,33 @@ func (in *CSIPersistentVolumeSource) DeepCopyInto(out *CSIPersistentVolumeSource
(*out)[key] = val (*out)[key] = val
} }
} }
if in.ControllerPublishSecretRef != nil {
in, out := &in.ControllerPublishSecretRef, &out.ControllerPublishSecretRef
if *in == nil {
*out = nil
} else {
*out = new(SecretReference)
**out = **in
}
}
if in.NodeStageSecretRef != nil {
in, out := &in.NodeStageSecretRef, &out.NodeStageSecretRef
if *in == nil {
*out = nil
} else {
*out = new(SecretReference)
**out = **in
}
}
if in.NodePublishSecretRef != nil {
in, out := &in.NodePublishSecretRef, &out.NodePublishSecretRef
if *in == nil {
*out = nil
} else {
*out = new(SecretReference)
**out = **in
}
}
return return
} }

File diff suppressed because it is too large Load Diff