-
Notifications
You must be signed in to change notification settings - Fork 40.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Kubernetes CSI - in-tree plugin Attacher/Detacher API #55809
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* | ||
Copyright 2014 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 ( | ||
"crypto/sha256" | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/golang/glog" | ||
|
||
"k8s.io/api/core/v1" | ||
storage "k8s.io/api/storage/v1alpha1" | ||
meta "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/kubernetes/pkg/volume" | ||
) | ||
|
||
type csiAttacher struct { | ||
plugin *csiPlugin | ||
k8s kubernetes.Interface | ||
waitSleepTime time.Duration | ||
} | ||
|
||
// volume.Attacher methods | ||
var _ volume.Attacher = &csiAttacher{} | ||
|
||
func (c *csiAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) { | ||
if spec == nil { | ||
return "", errors.New("missing spec") | ||
} | ||
|
||
if spec.PersistentVolume == nil { | ||
return "", errors.New("missing persistent volume") | ||
} | ||
|
||
// namespace := spec.PersistentVolume.GetObjectMeta().GetNamespace() | ||
pvName := spec.PersistentVolume.GetName() | ||
attachID := fmt.Sprintf("pv-%s", hashAttachmentName(pvName, string(nodeName))) | ||
|
||
attachment := &storage.VolumeAttachment{ | ||
ObjectMeta: meta.ObjectMeta{ | ||
Name: attachID, | ||
// Namespace: namespace, TODO should VolumeAttachment namespaced ? | ||
}, | ||
Spec: storage.VolumeAttachmentSpec{ | ||
NodeName: string(nodeName), | ||
Attacher: csiPluginName, | ||
Source: storage.VolumeAttachmentSource{ | ||
PersistentVolumeName: &pvName, | ||
}, | ||
}, | ||
} | ||
attach, err := c.k8s.StorageV1alpha1().VolumeAttachments().Create(attachment) | ||
if err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should ignore AlreadyExists error here - Attach will be called periodically, but VolumeAttachment will be already there. There is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah ok, thanks will look into this - TODO P1 |
||
glog.Error(log("attacher.Attach failed: %v", err)) | ||
return "", err | ||
} | ||
glog.V(4).Info(log("volume attachment sent: [%v]", attach.GetName())) | ||
|
||
return attach.GetName(), nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should wait for a while until There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: Probably not actually do a wait loop here, but instead act on the event state change as a next step, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsafrane I thought the Attacher.WaitForAttach() method is where that is done ? But will look into AWS/GCE for clues. TODO P1. |
||
} | ||
|
||
func (c *csiAttacher) WaitForAttach(spec *volume.Spec, attachID string, pod *v1.Pod, timeout time.Duration) (string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsafrane ok I see your point. I can move the wait logic from |
||
glog.V(4).Info(log("waiting for attachment update from CSI driver [attachment.ID=%v]", attachID)) | ||
|
||
source, err := getCSISourceFromSpec(spec) | ||
if err != nil { | ||
glog.Error(log("attach.WaitForAttach failed to get volume source: %v", err)) | ||
return "", err | ||
} | ||
|
||
ticker := time.NewTicker(c.waitSleepTime) | ||
defer ticker.Stop() | ||
|
||
timer := time.NewTimer(timeout) | ||
defer timer.Stop() | ||
|
||
for { | ||
select { | ||
case <-ticker.C: | ||
glog.V(4).Info(log("probing VolumeAttachment [id=%v]", attachID)) | ||
attach, err := c.k8s.StorageV1alpha1().VolumeAttachments().Get(attachID, meta.GetOptions{}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a TODO to revisit this and replace it with a watch instead of poll. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok will do. TODO P2 |
||
if err != nil { | ||
// log error, but continue to check again | ||
glog.Error(log("attacher.WaitForAttach failed (will continue to try): %v", err)) | ||
} | ||
// attachment OK | ||
if attach.Status.Attached { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, good catch, will add a |
||
return attachID, nil | ||
} | ||
// driver reports attach error | ||
attachErr := attach.Status.AttachError | ||
if attachErr != nil { | ||
glog.Error(log("attachment for %v failed: %v", source.VolumeHandle, attachErr.Message)) | ||
return "", errors.New(attachErr.Message) | ||
} | ||
case <-timer.C: | ||
glog.Error(log("attacher.WaitForAttach timeout after %v [volume=%v; attachment.ID=%v]", timeout, source.VolumeHandle, attachID)) | ||
return "", fmt.Errorf("attachment timeout for volume %v", source.VolumeHandle) | ||
} | ||
|
||
} | ||
} | ||
|
||
func (c *csiAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) { | ||
return nil, errors.New("unimplemented") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This must be implemented. At least you should check that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, will implement |
||
} | ||
|
||
func (c *csiAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) { | ||
return "", errors.New("unimplemented") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This must be implemented and it should be the same as GetPath() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably we don't need to implement this as we don't implement MountDevice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok got it. |
||
} | ||
|
||
func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error { | ||
return errors.New("unimplemented") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This must be implemented. It should be the same as SetUp. DevicePath is probably empty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably we don't need to implement this, kubelet should use SetupAt. But it should not return an error though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsafrane the last sentence |
||
} | ||
|
||
var _ volume.Detacher = &csiAttacher{} | ||
|
||
func (c *csiAttacher) Detach(deviceName string, nodeName types.NodeName) error { | ||
return errors.New("unimplemented") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, got it. Implement |
||
} | ||
|
||
func (c *csiAttacher) UnmountDevice(deviceMountPath string) error { | ||
return errors.New("unimplemented") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This must be implemented and should follow the same pattern as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably we don't need to implement this, kubelet should use TearDown. But it should not return an error though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsafrane ok will need to understand why not returning error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. |
||
} | ||
|
||
func hashAttachmentName(pvName, nodeName string) string { | ||
result := sha256.Sum256([]byte(fmt.Sprintf("%s%s", pvName, nodeName))) | ||
return fmt.Sprintf("%x", result) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope,
VolumeAttachment
, likePersistentVolume
is non-namespaced because it is a cluster resource.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, got it.