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",
"Comment": "v0.1.0-5-g7ab01a9",
"Rev": "7ab01a90da87f9fef3ee1de0494962fdefaf7db7"
"Rev": "91c189774c16b0661255943c09ea9d97d5a423e7"
},
{
"ImportPath": "github.com/containerd/console",

View File

@ -75317,6 +75317,10 @@
"volumeHandle"
],
"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": {
"description": "Driver is the name of the driver to use for this volume. Required.",
"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.",
"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": {
"description": "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).",
"type": "boolean"

View File

@ -20016,6 +20016,18 @@
"volumeAttributes": {
"type": "object",
"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"></td>
</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>
</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
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
// 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 {
case source.AzureFile != nil:
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
}
} 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
}
}
@ -57,7 +57,7 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
// use the secret namespace if namespace is set
ns = source.CephFS.SecretRef.Namespace
}
if !visitor(ns, source.CephFS.SecretRef.Name) {
if !visitor(ns, source.CephFS.SecretRef.Name, true /* kubeletVisible */) {
return false
}
}
@ -69,7 +69,7 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
// use the secret namespace if namespace is set
ns = source.FlexVolume.SecretRef.Namespace
}
if !visitor(ns, source.FlexVolume.SecretRef.Name) {
if !visitor(ns, source.FlexVolume.SecretRef.Name, true /* kubeletVisible */) {
return false
}
}
@ -81,7 +81,7 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
// use the secret namespace if namespace is set
ns = source.RBD.SecretRef.Namespace
}
if !visitor(ns, source.RBD.SecretRef.Name) {
if !visitor(ns, source.RBD.SecretRef.Name, true /* kubeletVisible */) {
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 {
ns = source.ScaleIO.SecretRef.Namespace
}
if !visitor(ns, source.ScaleIO.SecretRef.Name) {
if !visitor(ns, source.ScaleIO.SecretRef.Name, true /* kubeletVisible */) {
return false
}
}
@ -103,14 +103,30 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
// use the secret namespace if namespace is set
ns = source.ISCSI.SecretRef.Namespace
}
if !visitor(ns, source.ISCSI.SecretRef.Name) {
if !visitor(ns, source.ISCSI.SecretRef.Name, true /* kubeletVisible */) {
return false
}
}
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
}
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
}

View File

@ -117,11 +117,32 @@ func TestPVSecrets(t *testing.T) {
SecretRef: &api.ObjectReference{
Name: "Spec.PersistentVolumeSource.StorageOS.SecretRef",
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()
extractedNamesWithNamespace := sets.NewString()
for _, pv := range pvs {
VisitPVSecretNames(pv, func(namespace, name string) bool {
VisitPVSecretNames(pv, func(namespace, name string, kubeletVisible bool) bool {
extractedNames.Insert(name)
extractedNamesWithNamespace.Insert(namespace + "/" + name)
return true
@ -143,6 +164,9 @@ func TestPVSecrets(t *testing.T) {
"Spec.PersistentVolumeSource.ScaleIO.SecretRef",
"Spec.PersistentVolumeSource.ISCSI.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 = secretPaths.Difference(excludedSecretPaths)
@ -184,6 +208,10 @@ func TestPVSecrets(t *testing.T) {
"iscsi/Spec.PersistentVolumeSource.ISCSI.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 {
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.
// +optional
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

View File

@ -623,6 +623,9 @@ func autoConvert_v1_CSIPersistentVolumeSource_To_core_CSIPersistentVolumeSource(
out.ReadOnly = in.ReadOnly
out.FSType = in.FSType
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
}
@ -637,6 +640,9 @@ func autoConvert_core_CSIPersistentVolumeSource_To_v1_CSIPersistentVolumeSource(
out.ReadOnly = in.ReadOnly
out.FSType = in.FSType
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
}

View File

@ -1452,6 +1452,45 @@ func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldP
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
}

View File

@ -256,6 +256,33 @@ func (in *CSIPersistentVolumeSource) DeepCopyInto(out *CSIPersistentVolumeSource
(*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
}

View File

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

View File

@ -32,7 +32,6 @@ import (
type csiClient interface {
AssertSupportedVersion(ctx grpctx.Context, ver *csipb.Version) error
NodeProbe(ctx grpctx.Context, ver *csipb.Version) error
NodePublishVolume(
ctx grpctx.Context,
volumeid string,
@ -41,9 +40,15 @@ type csiClient interface {
accessMode api.PersistentVolumeAccessMode,
volumeInfo map[string]string,
volumeAttribs map[string]string,
nodePublishCredentials map[string]string,
fsType string,
) 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
@ -146,13 +151,6 @@ func (c *csiDriverClient) AssertSupportedVersion(ctx grpctx.Context, ver *csipb.
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(
ctx grpctx.Context,
volID string,
@ -161,6 +159,7 @@ func (c *csiDriverClient) NodePublishVolume(
accessMode api.PersistentVolumeAccessMode,
volumeInfo map[string]string,
volumeAttribs map[string]string,
nodePublishCredentials map[string]string,
fsType string,
) error {
glog.V(4).Info(log("calling NodePublishVolume rpc [volid=%s,target_path=%s]", volID, targetPath))
@ -176,13 +175,13 @@ func (c *csiDriverClient) NodePublishVolume(
}
req := &csipb.NodePublishVolumeRequest{
Version: csiVersion,
VolumeId: volID,
TargetPath: targetPath,
Readonly: readOnly,
PublishInfo: volumeInfo,
VolumeAttributes: volumeAttribs,
Version: csiVersion,
VolumeId: volID,
TargetPath: targetPath,
Readonly: readOnly,
PublishInfo: volumeInfo,
VolumeAttributes: volumeAttribs,
NodePublishCredentials: nodePublishCredentials,
VolumeCapability: &csipb.VolumeCapability{
AccessMode: &csipb.VolumeCapability_AccessMode{
Mode: asCSIAccessMode(accessMode),
@ -199,7 +198,7 @@ func (c *csiDriverClient) NodePublishVolume(
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))
if volID == "" {
return errors.New("missing volume id")
@ -213,9 +212,10 @@ func (c *csiDriverClient) NodeUnpublishVolume(ctx grpctx.Context, volID string,
}
req := &csipb.NodeUnpublishVolumeRequest{
Version: csiVersion,
VolumeId: volID,
TargetPath: targetPath,
Version: csiVersion,
VolumeId: volID,
TargetPath: targetPath,
NodeUnpublishCredentials: nodeUnpublishCredentials,
}
_, 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) {
testCases := []struct {
name string
@ -119,6 +97,7 @@ func TestClientNodePublishVolume(t *testing.T) {
api.ReadWriteOnce,
map[string]string{"device": "/dev/null"},
map[string]string{"attr0": "val0"},
map[string]string{},
tc.fsType,
)
@ -147,7 +126,8 @@ func TestClientNodeUnpublishVolume(t *testing.T) {
for _, tc := range testCases {
t.Logf("test case: %s", tc.name)
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 {
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{}
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
}
@ -129,13 +127,6 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
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
if c.volumeInfo == nil {
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 {
fsType = defaultFSType
}
nodePublishCredentials := map[string]string{}
if csiSource.NodePublishSecretRef != nil {
nodePublishCredentials = getCredentialsFromSecret(c.k8s, csiSource.NodePublishSecretRef)
}
err = csi.NodePublishVolume(
ctx,
c.volumeID,
@ -197,6 +191,7 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
accessMode,
c.volumeInfo,
attribs,
nodePublishCredentials,
fsType,
)
@ -244,6 +239,12 @@ func (c *csiMountMgr) TearDownAt(dir string) error {
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
dataDir := path.Dir(dir) // dropoff /mount at end
data, err := loadVolumeData(dataDir, volDataFileName)
@ -273,7 +274,11 @@ func (c *csiMountMgr) TearDownAt(dir string) error {
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))
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
}
// 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
type NodeClient struct {
nodePublishedVolumes map[string]string
@ -110,17 +120,6 @@ func (f *NodeClient) NodePublishVolume(ctx grpctx.Context, req *csipb.NodePublis
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
func (f *NodeClient) NodeUnpublishVolume(ctx context.Context, req *csipb.NodeUnpublishVolumeRequest, opts ...grpc.CallOption) (*csipb.NodeUnpublishVolumeResponse, error) {
if f.nextErr != nil {
@ -147,6 +146,16 @@ func (f *NodeClient) NodeGetCapabilities(ctx context.Context, in *csipb.NodeGetC
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
type ControllerClient struct {
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) {
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
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
g.graph.SetEdge(simple.Edge{F: g.getOrCreateVertex_locked(secretVertexType, namespace, secret), T: pvVertex})
if kubeletVisible {
g.graph.SetEdge(simple.Edge{F: g.getOrCreateVertex_locked(secretVertexType, namespace, secret), T: pvVertex})
}
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.
// +optional
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.

View File

@ -1753,6 +1753,30 @@ type CSIPersistentVolumeSource struct {
// Attributes of the volume to publish.
// +optional
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.

View File

@ -117,12 +117,15 @@ func (Binding) SwaggerDoc() map[string]string {
}
var map_CSIPersistentVolumeSource = map[string]string{
"": "Represents storage that is managed by an external CSI volume driver (Beta feature)",
"driver": "Driver is the name of the driver to use for this volume. Required.",
"volumeHandle": "VolumeHandle is the unique volume name returned by the CSI volume plugins CreateVolume to refer to the volume on all subsequent calls. Required.",
"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.",
"volumeAttributes": "Attributes of the volume to publish.",
"": "Represents storage that is managed by an external CSI volume driver (Beta feature)",
"driver": "Driver is the name of the driver to use for this volume. Required.",
"volumeHandle": "VolumeHandle is the unique volume name returned by the CSI volume plugins CreateVolume to refer to the volume on all subsequent calls. Required.",
"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.",
"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 {

View File

@ -256,6 +256,33 @@ func (in *CSIPersistentVolumeSource) DeepCopyInto(out *CSIPersistentVolumeSource
(*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
}

File diff suppressed because it is too large Load Diff