-
Notifications
You must be signed in to change notification settings - Fork 38.6k
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
Ubernetes-Lite GCE: Label volumes with zone information #19995
Changes from all commits
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 |
---|---|---|
|
@@ -26,6 +26,7 @@ import ( | |
client "k8s.io/kubernetes/pkg/client/unversioned" | ||
"k8s.io/kubernetes/pkg/cloudprovider" | ||
"k8s.io/kubernetes/pkg/cloudprovider/providers/aws" | ||
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce" | ||
) | ||
|
||
func init() { | ||
|
@@ -40,8 +41,9 @@ var _ = admission.Interface(&persistentVolumeLabel{}) | |
type persistentVolumeLabel struct { | ||
*admission.Handler | ||
|
||
mutex sync.Mutex | ||
ebsVolumes aws.Volumes | ||
mutex sync.Mutex | ||
ebsVolumes aws.Volumes | ||
gceCloudProvider *gce.GCECloud | ||
} | ||
|
||
// NewPersistentVolumeLabel returns an admission.Interface implementation which adds labels to PersistentVolume CREATE requests, | ||
|
@@ -75,6 +77,13 @@ func (l *persistentVolumeLabel) Admit(a admission.Attributes) (err error) { | |
} | ||
volumeLabels = labels | ||
} | ||
if volume.Spec.GCEPersistentDisk != 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. @markturansky @saad-ali This should be part of volume plugins, I think. It should NOT be an open-coded list of cases. I'll file a new issue. 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. Yes - this came up in review a few lines up ^^^. I agree with the criticism, but it isn't clear to me how best to fix it. |
||
labels, err := l.findGCEPDLabels(volume) | ||
if err != nil { | ||
return admission.NewForbidden(a, fmt.Errorf("error querying GCE PD volume %s: %v", volume.Spec.GCEPersistentDisk.PDName, err)) | ||
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 broke provisioning (because we don't have an e2e test that would have prevented this change). The provisioner creates a dummy PV in response to a claim that requires provisioning. The "dummy" ID on the struct was required to pass validation. A controller reconciles this PV with the infrastructure and creates a resource asynchronously. There is no "dummy" PD in the provider yet, so we can't look it up for labels. This admission controller makes it required that the PD exist. It could be, too, that making a placeholder PV is wrong. I am open to suggestions. 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. we can put the "dummy" constant in pkg/volume (as opposed to a plugin) and use that in admission and the plugins themselves. it is leaky as hell but is far less work than adding plugins and a framework to the API server. Should we consider it as a short term fix until we come up with a better way? |
||
} | ||
volumeLabels = labels | ||
} | ||
|
||
if len(volumeLabels) != 0 { | ||
if volume.Labels == nil { | ||
|
@@ -129,3 +138,40 @@ func (l *persistentVolumeLabel) getEBSVolumes() (aws.Volumes, error) { | |
} | ||
return l.ebsVolumes, nil | ||
} | ||
|
||
func (l *persistentVolumeLabel) findGCEPDLabels(volume *api.PersistentVolume) (map[string]string, error) { | ||
provider, err := l.getGCECloudProvider() | ||
if err != nil { | ||
return nil, err | ||
} | ||
if provider == nil { | ||
return nil, fmt.Errorf("unable to build GCE cloud provider for PD") | ||
} | ||
|
||
labels, err := provider.GetAutoLabelsForPD(volume.Spec.GCEPersistentDisk.PDName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return labels, err | ||
} | ||
|
||
// getGCECloudProvider returns the GCE cloud provider, for use for querying volume labels | ||
func (l *persistentVolumeLabel) getGCECloudProvider() (*gce.GCECloud, error) { | ||
l.mutex.Lock() | ||
defer l.mutex.Unlock() | ||
|
||
if l.gceCloudProvider == nil { | ||
cloudProvider, err := cloudprovider.GetCloudProvider("gce", nil) | ||
if err != nil || cloudProvider == nil { | ||
return nil, err | ||
} | ||
gceCloudProvider, ok := cloudProvider.(*gce.GCECloud) | ||
if !ok { | ||
// GetCloudProvider has gone very wrong | ||
return nil, fmt.Errorf("error retrieving GCE cloud provider") | ||
} | ||
l.gceCloudProvider = gceCloudProvider | ||
} | ||
return l.gceCloudProvider, nil | ||
} |
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.
Can we avoid putting provider-specific fields in this provider-independent library?
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.
I don't see how - suggestions welcome.
I wanted to implement a Volumes interface, and you can see from the AWS implementation that I did. It was originally intended to be cross-cloud provider, but there were a number of objections raised so I moved Volumes into the aws package. The primary objection was (I believe) that a cloud could implement different types of volumes (e.g. even AWS has NFS & EBS now).
And volume.Spec isn't provider independent either.
One option would be to define aws.Volumes in cloudprovider, but have it take a PersistentVolumeSource. But that isn't quite right either, and would be a fairly large refactor.
In short, I'd love to, but I don't see how to do it in an acceptable way.
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.
Fair enough. I'm happy for us to perhaps try to take on a refactor in a separate PR, after v 1.2.
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.
If volumes are plugins and Kubelet (and others) don't know what plugins exist, anything that escapes those packages is leaky.
Would we instead want to give the same list of plugins to the API server that we give to Kubelet and ControllerManager?
The API server would do the same iteration and CanSupport on each plugin, find the appropriate one, and call Validate(pv) and Admit(pv).
Each volume would know how to validate itself in the API. Missing plugins would cause a real API error. Any business logic regarding labelling and so forth is also internalized to the plugin.
@thockin thoughts?