mirror of https://github.com/k3s-io/k3s
Merge pull request #49727 from gnufied/expand-pvc-plugin-changes
Automatic merge from submit-queue (batch tested with PRs 49727, 51792) Implement Controller for growing persistent volumes This PR implements API and controller plane changes necessary for doing controller side resize. xref : https://github.com/kubernetes/community/pull/657 Also xref https://github.com/kubernetes/features/issues/284 ``` Add alpha support for allowing users to grow persistent volumes. Currently we only support volume types that just require control plane resize (such as glusterfs) and don't need separate file system resize. ```pull/6/head
commit
cd417b583c
|
@ -65753,6 +65753,37 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.core.v1.PersistentVolumeClaimCondition": {
|
||||
"description": "PersistentVolumeClaimCondition contails details about state of pvc",
|
||||
"required": [
|
||||
"type",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"lastProbeTime": {
|
||||
"description": "Last time we probed the condition.",
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
|
||||
},
|
||||
"lastTransitionTime": {
|
||||
"description": "Last time the condition transitioned from one status to another.",
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
|
||||
},
|
||||
"message": {
|
||||
"description": "Human-readable message indicating details about last transition.",
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"description": "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized.",
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.api.core.v1.PersistentVolumeClaimList": {
|
||||
"description": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.",
|
||||
"required": [
|
||||
|
@ -65832,6 +65863,15 @@
|
|||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity"
|
||||
}
|
||||
},
|
||||
"conditions": {
|
||||
"description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io.k8s.api.core.v1.PersistentVolumeClaimCondition"
|
||||
},
|
||||
"x-kubernetes-patch-merge-key": "type",
|
||||
"x-kubernetes-patch-strategy": "merge"
|
||||
},
|
||||
"phase": {
|
||||
"description": "Phase represents the current phase of PersistentVolumeClaim.",
|
||||
"type": "string"
|
||||
|
@ -70708,6 +70748,10 @@
|
|||
"provisioner"
|
||||
],
|
||||
"properties": {
|
||||
"allowVolumeExpansion": {
|
||||
"description": "AllowVolumeExpansion shows whether the storage class allow volume expand",
|
||||
"type": "boolean"
|
||||
},
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
|
@ -70791,6 +70835,10 @@
|
|||
"provisioner"
|
||||
],
|
||||
"properties": {
|
||||
"allowVolumeExpansion": {
|
||||
"description": "AllowVolumeExpansion shows whether the storage class allow volume expand",
|
||||
"type": "boolean"
|
||||
},
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
|
|
|
@ -6524,6 +6524,45 @@
|
|||
"capacity": {
|
||||
"type": "object",
|
||||
"description": "Represents the actual resources of the underlying volume."
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "v1.PersistentVolumeClaimCondition"
|
||||
},
|
||||
"description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.PersistentVolumeClaimCondition": {
|
||||
"id": "v1.PersistentVolumeClaimCondition",
|
||||
"description": "PersistentVolumeClaimCondition contails details about state of pvc",
|
||||
"required": [
|
||||
"type",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastProbeTime": {
|
||||
"type": "string",
|
||||
"description": "Last time we probed the condition."
|
||||
},
|
||||
"lastTransitionTime": {
|
||||
"type": "string",
|
||||
"description": "Last time the condition transitioned from one status to another."
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized."
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Human-readable message indicating details about last transition."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -9142,6 +9142,45 @@
|
|||
"capacity": {
|
||||
"type": "object",
|
||||
"description": "Represents the actual resources of the underlying volume."
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "v1.PersistentVolumeClaimCondition"
|
||||
},
|
||||
"description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.PersistentVolumeClaimCondition": {
|
||||
"id": "v1.PersistentVolumeClaimCondition",
|
||||
"description": "PersistentVolumeClaimCondition contails details about state of pvc",
|
||||
"required": [
|
||||
"type",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastProbeTime": {
|
||||
"type": "string",
|
||||
"description": "Last time we probed the condition."
|
||||
},
|
||||
"lastTransitionTime": {
|
||||
"type": "string",
|
||||
"description": "Last time the condition transitioned from one status to another."
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized."
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Human-readable message indicating details about last transition."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -795,6 +795,10 @@
|
|||
"type": "string"
|
||||
},
|
||||
"description": "Dynamically provisioned PersistentVolumes of this storage class are created with these mountOptions, e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid."
|
||||
},
|
||||
"allowVolumeExpansion": {
|
||||
"type": "boolean",
|
||||
"description": "AllowVolumeExpansion shows whether the storage class allow volume expand"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -795,6 +795,10 @@
|
|||
"type": "string"
|
||||
},
|
||||
"description": "Dynamically provisioned PersistentVolumes of this storage class are created with these mountOptions, e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid."
|
||||
},
|
||||
"allowVolumeExpansion": {
|
||||
"type": "boolean",
|
||||
"description": "AllowVolumeExpansion shows whether the storage class allow volume expand"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -20099,6 +20099,45 @@
|
|||
"capacity": {
|
||||
"type": "object",
|
||||
"description": "Represents the actual resources of the underlying volume."
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "v1.PersistentVolumeClaimCondition"
|
||||
},
|
||||
"description": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'."
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.PersistentVolumeClaimCondition": {
|
||||
"id": "v1.PersistentVolumeClaimCondition",
|
||||
"description": "PersistentVolumeClaimCondition contails details about state of pvc",
|
||||
"required": [
|
||||
"type",
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastProbeTime": {
|
||||
"type": "string",
|
||||
"description": "Last time we probed the condition."
|
||||
},
|
||||
"lastTransitionTime": {
|
||||
"type": "string",
|
||||
"description": "Last time the condition transitioned from one status to another."
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized."
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Human-readable message indicating details about last transition."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -37,6 +37,7 @@ go_library(
|
|||
"//plugin/pkg/admission/namespace/exists:go_default_library",
|
||||
"//plugin/pkg/admission/noderestriction:go_default_library",
|
||||
"//plugin/pkg/admission/persistentvolume/label:go_default_library",
|
||||
"//plugin/pkg/admission/persistentvolume/resize:go_default_library",
|
||||
"//plugin/pkg/admission/podnodeselector:go_default_library",
|
||||
"//plugin/pkg/admission/podpreset:go_default_library",
|
||||
"//plugin/pkg/admission/podtolerationrestriction:go_default_library",
|
||||
|
|
|
@ -41,6 +41,7 @@ import (
|
|||
"k8s.io/kubernetes/plugin/pkg/admission/namespace/exists"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/noderestriction"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/label"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/resize"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/podnodeselector"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/podpreset"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction"
|
||||
|
@ -81,4 +82,5 @@ func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
|
|||
serviceaccount.Register(plugins)
|
||||
setdefault.Register(plugins)
|
||||
webhook.Register(plugins)
|
||||
resize.Register(plugins)
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ go_library(
|
|||
"//pkg/controller/statefulset:go_default_library",
|
||||
"//pkg/controller/ttl:go_default_library",
|
||||
"//pkg/controller/volume/attachdetach:go_default_library",
|
||||
"//pkg/controller/volume/expand:go_default_library",
|
||||
"//pkg/controller/volume/persistentvolume:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/quota/install:go_default_library",
|
||||
|
|
|
@ -357,6 +357,7 @@ func NewControllerInitializers() map[string]InitFunc {
|
|||
controllers["route"] = startRouteController
|
||||
controllers["persistentvolume-binder"] = startPersistentVolumeBinderController
|
||||
controllers["attachdetach"] = startAttachDetachController
|
||||
controllers["persistentvolume-expander"] = startVolumeExpandController
|
||||
|
||||
return controllers
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ import (
|
|||
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
||||
ttlcontroller "k8s.io/kubernetes/pkg/controller/ttl"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/attachdetach"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/expand"
|
||||
persistentvolumecontroller "k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
quotainstall "k8s.io/kubernetes/pkg/quota/install"
|
||||
|
@ -189,6 +190,24 @@ func startAttachDetachController(ctx ControllerContext) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func startVolumeExpandController(ctx ControllerContext) (bool, error) {
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
expandController, expandControllerErr := expand.NewExpandController(
|
||||
ctx.ClientBuilder.ClientOrDie("expand-controller"),
|
||||
ctx.InformerFactory.Core().V1().PersistentVolumeClaims(),
|
||||
ctx.InformerFactory.Core().V1().PersistentVolumes(),
|
||||
ctx.Cloud,
|
||||
ProbeExpandableVolumePlugins(ctx.Options.VolumeConfiguration))
|
||||
|
||||
if expandControllerErr != nil {
|
||||
return true, fmt.Errorf("Failed to start volume expand controller : %v", expandControllerErr)
|
||||
}
|
||||
go expandController.Run(ctx.Stop)
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func startEndpointController(ctx ControllerContext) (bool, error) {
|
||||
go endpointcontroller.NewEndpointController(
|
||||
ctx.InformerFactory.Core().V1().Pods(),
|
||||
|
|
|
@ -88,6 +88,25 @@ func GetDynamicPluginProber(config componentconfig.VolumeConfiguration) volume.D
|
|||
return flexvolume.GetDynamicPluginProber(config.FlexVolumePluginDir)
|
||||
}
|
||||
|
||||
// ProbeExpandableVolumePlugins returns volume plugins which are expandable
|
||||
func ProbeExpandableVolumePlugins(config componentconfig.VolumeConfiguration) []volume.VolumePlugin {
|
||||
allPlugins := []volume.VolumePlugin{}
|
||||
|
||||
allPlugins = append(allPlugins, aws_ebs.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, gce_pd.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, cinder.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, portworx.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, glusterfs.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, rbd.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, scaleio.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, storageos.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...)
|
||||
return allPlugins
|
||||
}
|
||||
|
||||
// ProbeControllerVolumePlugins collects all persistent volume plugins into an
|
||||
// easy to use list. Only volume plugins that implement any of
|
||||
// provisioner/recycler/deleter interface should be returned.
|
||||
|
|
|
@ -2482,6 +2482,13 @@ When an object is created, the system will populate this list with the current s
|
|||
<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">conditions</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to <em>ResizeStarted</em>.</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_persistentvolumeclaimcondition">v1.PersistentVolumeClaimCondition</a> array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -2519,6 +2526,75 @@ When an object is created, the system will populate this list with the current s
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_persistentvolumeclaimcondition">v1.PersistentVolumeClaimCondition</h3>
|
||||
<div class="paragraph">
|
||||
<p>PersistentVolumeClaimCondition contails details about state of pvc</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all" style="width:100%; ">
|
||||
<colgroup>
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tableblock halign-left valign-top">Name</th>
|
||||
<th class="tableblock halign-left valign-top">Description</th>
|
||||
<th class="tableblock halign-left valign-top">Required</th>
|
||||
<th class="tableblock halign-left valign-top">Schema</th>
|
||||
<th class="tableblock halign-left valign-top">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">type</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">status</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">lastProbeTime</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Last time we probed the condition.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">lastTransitionTime</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Last time the condition transitioned from one status to another.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">reason</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Unique, this should be a short, machine understandable string that gives the reason for condition’s last transition. If it reports "ResizeStarted" that means the underlying persistent volume is being resized.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">message</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Human-readable message indicating details about last transition.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_secretvolumesource">v1.SecretVolumeSource</h3>
|
||||
|
|
|
@ -2808,6 +2808,13 @@ When an object is created, the system will populate this list with the current s
|
|||
<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">conditions</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to <em>ResizeStarted</em>.</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_persistentvolumeclaimcondition">v1.PersistentVolumeClaimCondition</a> array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -3094,6 +3101,75 @@ When an object is created, the system will populate this list with the current s
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_persistentvolumeclaimcondition">v1.PersistentVolumeClaimCondition</h3>
|
||||
<div class="paragraph">
|
||||
<p>PersistentVolumeClaimCondition contails details about state of pvc</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all" style="width:100%; ">
|
||||
<colgroup>
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tableblock halign-left valign-top">Name</th>
|
||||
<th class="tableblock halign-left valign-top">Description</th>
|
||||
<th class="tableblock halign-left valign-top">Required</th>
|
||||
<th class="tableblock halign-left valign-top">Schema</th>
|
||||
<th class="tableblock halign-left valign-top">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">type</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">status</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">lastProbeTime</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Last time we probed the condition.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">lastTransitionTime</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Last time the condition transitioned from one status to another.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">reason</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Unique, this should be a short, machine understandable string that gives the reason for condition’s last transition. If it reports "ResizeStarted" that means the underlying persistent volume is being resized.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">message</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Human-readable message indicating details about last transition.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_secretvolumesource">v1.SecretVolumeSource</h3>
|
||||
|
|
|
@ -989,6 +989,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">allowVolumeExpansion</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">AllowVolumeExpansion shows whether the storage class allow volume expand</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">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -951,6 +951,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">allowVolumeExpansion</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">AllowVolumeExpansion shows whether the storage class allow volume expand</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">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -1632,6 +1632,54 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_gitrepovolumesource">v1.GitRepoVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a volume that is populated with the contents of a git repository. Git repo volumes do not support ownership management. Git repo volumes support SELinux relabeling.</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all" style="width:100%; ">
|
||||
<colgroup>
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tableblock halign-left valign-top">Name</th>
|
||||
<th class="tableblock halign-left valign-top">Description</th>
|
||||
<th class="tableblock halign-left valign-top">Required</th>
|
||||
<th class="tableblock halign-left valign-top">Schema</th>
|
||||
<th class="tableblock halign-left valign-top">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">repository</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Repository URL</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">revision</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Commit hash for the specified revision.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">directory</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Target directory name. Must not contain or start with <em>..</em>. If <em>.</em> is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_endpointslist">v1.EndpointsList</h3>
|
||||
|
@ -1687,54 +1735,6 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_gitrepovolumesource">v1.GitRepoVolumeSource</h3>
|
||||
<div class="paragraph">
|
||||
<p>Represents a volume that is populated with the contents of a git repository. Git repo volumes do not support ownership management. Git repo volumes support SELinux relabeling.</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all" style="width:100%; ">
|
||||
<colgroup>
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tableblock halign-left valign-top">Name</th>
|
||||
<th class="tableblock halign-left valign-top">Description</th>
|
||||
<th class="tableblock halign-left valign-top">Required</th>
|
||||
<th class="tableblock halign-left valign-top">Schema</th>
|
||||
<th class="tableblock halign-left valign-top">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">repository</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Repository URL</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">revision</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Commit hash for the specified revision.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">directory</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Target directory name. Must not contain or start with <em>..</em>. If <em>.</em> is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_replicationcontrollercondition">v1.ReplicationControllerCondition</h3>
|
||||
|
@ -3582,6 +3582,13 @@ When an object is created, the system will populate this list with the current s
|
|||
<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">conditions</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to <em>ResizeStarted</em>.</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_persistentvolumeclaimcondition">v1.PersistentVolumeClaimCondition</a> array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -3644,6 +3651,75 @@ The resulting set of endpoints can be viewed as:<br>
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_persistentvolumeclaimcondition">v1.PersistentVolumeClaimCondition</h3>
|
||||
<div class="paragraph">
|
||||
<p>PersistentVolumeClaimCondition contails details about state of pvc</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all" style="width:100%; ">
|
||||
<colgroup>
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
<col style="width:20%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tableblock halign-left valign-top">Name</th>
|
||||
<th class="tableblock halign-left valign-top">Description</th>
|
||||
<th class="tableblock halign-left valign-top">Required</th>
|
||||
<th class="tableblock halign-left valign-top">Schema</th>
|
||||
<th class="tableblock halign-left valign-top">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">type</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">status</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">lastProbeTime</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Last time we probed the condition.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">lastTransitionTime</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Last time the condition transitioned from one status to another.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">reason</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Unique, this should be a short, machine understandable string that gives the reason for condition’s last transition. If it reports "ResizeStarted" that means the underlying persistent volume is being resized.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">message</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Human-readable message indicating details about last transition.</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">string</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_v1_secretvolumesource">v1.SecretVolumeSource</h3>
|
||||
|
|
|
@ -190,6 +190,7 @@ pkg/controller/volume/attachdetach
|
|||
pkg/controller/volume/attachdetach/statusupdater
|
||||
pkg/controller/volume/attachdetach/testing
|
||||
pkg/controller/volume/events
|
||||
pkg/controller/volume/expand
|
||||
pkg/controller/volume/persistentvolume
|
||||
pkg/controller/volume/persistentvolume/options
|
||||
pkg/credentialprovider
|
||||
|
|
|
@ -547,6 +547,27 @@ type PersistentVolumeClaimSpec struct {
|
|||
StorageClassName *string
|
||||
}
|
||||
|
||||
type PersistentVolumeClaimConditionType string
|
||||
|
||||
// These are valid conditions of Pvc
|
||||
const (
|
||||
// An user trigger resize of pvc has been started
|
||||
PersistentVolumeClaimResizing PersistentVolumeClaimConditionType = "Resizing"
|
||||
)
|
||||
|
||||
type PersistentVolumeClaimCondition struct {
|
||||
Type PersistentVolumeClaimConditionType
|
||||
Status ConditionStatus
|
||||
// +optional
|
||||
LastProbeTime metav1.Time
|
||||
// +optional
|
||||
LastTransitionTime metav1.Time
|
||||
// +optional
|
||||
Reason string
|
||||
// +optional
|
||||
Message string
|
||||
}
|
||||
|
||||
type PersistentVolumeClaimStatus struct {
|
||||
// Phase represents the current phase of PersistentVolumeClaim
|
||||
// +optional
|
||||
|
@ -557,6 +578,8 @@ type PersistentVolumeClaimStatus struct {
|
|||
// Represents the actual resources of the underlying volume
|
||||
// +optional
|
||||
Capacity ResourceList
|
||||
// +optional
|
||||
Conditions []PersistentVolumeClaimCondition
|
||||
}
|
||||
|
||||
type PersistentVolumeAccessMode string
|
||||
|
|
|
@ -233,6 +233,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
|
|||
Convert_api_PersistentVolume_To_v1_PersistentVolume,
|
||||
Convert_v1_PersistentVolumeClaim_To_api_PersistentVolumeClaim,
|
||||
Convert_api_PersistentVolumeClaim_To_v1_PersistentVolumeClaim,
|
||||
Convert_v1_PersistentVolumeClaimCondition_To_api_PersistentVolumeClaimCondition,
|
||||
Convert_api_PersistentVolumeClaimCondition_To_v1_PersistentVolumeClaimCondition,
|
||||
Convert_v1_PersistentVolumeClaimList_To_api_PersistentVolumeClaimList,
|
||||
Convert_api_PersistentVolumeClaimList_To_v1_PersistentVolumeClaimList,
|
||||
Convert_v1_PersistentVolumeClaimSpec_To_api_PersistentVolumeClaimSpec,
|
||||
|
@ -2942,6 +2944,36 @@ func Convert_api_PersistentVolumeClaim_To_v1_PersistentVolumeClaim(in *api.Persi
|
|||
return autoConvert_api_PersistentVolumeClaim_To_v1_PersistentVolumeClaim(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_PersistentVolumeClaimCondition_To_api_PersistentVolumeClaimCondition(in *v1.PersistentVolumeClaimCondition, out *api.PersistentVolumeClaimCondition, s conversion.Scope) error {
|
||||
out.Type = api.PersistentVolumeClaimConditionType(in.Type)
|
||||
out.Status = api.ConditionStatus(in.Status)
|
||||
out.LastProbeTime = in.LastProbeTime
|
||||
out.LastTransitionTime = in.LastTransitionTime
|
||||
out.Reason = in.Reason
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_PersistentVolumeClaimCondition_To_api_PersistentVolumeClaimCondition is an autogenerated conversion function.
|
||||
func Convert_v1_PersistentVolumeClaimCondition_To_api_PersistentVolumeClaimCondition(in *v1.PersistentVolumeClaimCondition, out *api.PersistentVolumeClaimCondition, s conversion.Scope) error {
|
||||
return autoConvert_v1_PersistentVolumeClaimCondition_To_api_PersistentVolumeClaimCondition(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_PersistentVolumeClaimCondition_To_v1_PersistentVolumeClaimCondition(in *api.PersistentVolumeClaimCondition, out *v1.PersistentVolumeClaimCondition, s conversion.Scope) error {
|
||||
out.Type = v1.PersistentVolumeClaimConditionType(in.Type)
|
||||
out.Status = v1.ConditionStatus(in.Status)
|
||||
out.LastProbeTime = in.LastProbeTime
|
||||
out.LastTransitionTime = in.LastTransitionTime
|
||||
out.Reason = in.Reason
|
||||
out.Message = in.Message
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_api_PersistentVolumeClaimCondition_To_v1_PersistentVolumeClaimCondition is an autogenerated conversion function.
|
||||
func Convert_api_PersistentVolumeClaimCondition_To_v1_PersistentVolumeClaimCondition(in *api.PersistentVolumeClaimCondition, out *v1.PersistentVolumeClaimCondition, s conversion.Scope) error {
|
||||
return autoConvert_api_PersistentVolumeClaimCondition_To_v1_PersistentVolumeClaimCondition(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_PersistentVolumeClaimList_To_api_PersistentVolumeClaimList(in *v1.PersistentVolumeClaimList, out *api.PersistentVolumeClaimList, s conversion.Scope) error {
|
||||
out.ListMeta = in.ListMeta
|
||||
out.Items = *(*[]api.PersistentVolumeClaim)(unsafe.Pointer(&in.Items))
|
||||
|
@ -3000,6 +3032,7 @@ func autoConvert_v1_PersistentVolumeClaimStatus_To_api_PersistentVolumeClaimStat
|
|||
out.Phase = api.PersistentVolumeClaimPhase(in.Phase)
|
||||
out.AccessModes = *(*[]api.PersistentVolumeAccessMode)(unsafe.Pointer(&in.AccessModes))
|
||||
out.Capacity = *(*api.ResourceList)(unsafe.Pointer(&in.Capacity))
|
||||
out.Conditions = *(*[]api.PersistentVolumeClaimCondition)(unsafe.Pointer(&in.Conditions))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -3012,6 +3045,7 @@ func autoConvert_api_PersistentVolumeClaimStatus_To_v1_PersistentVolumeClaimStat
|
|||
out.Phase = v1.PersistentVolumeClaimPhase(in.Phase)
|
||||
out.AccessModes = *(*[]v1.PersistentVolumeAccessMode)(unsafe.Pointer(&in.AccessModes))
|
||||
out.Capacity = *(*v1.ResourceList)(unsafe.Pointer(&in.Capacity))
|
||||
out.Conditions = *(*[]v1.PersistentVolumeClaimCondition)(unsafe.Pointer(&in.Conditions))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1588,10 +1588,31 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *api.PersistentVolumeCla
|
|||
oldPvc.Spec.VolumeName = newPvc.Spec.VolumeName
|
||||
defer func() { oldPvc.Spec.VolumeName = "" }()
|
||||
}
|
||||
// changes to Spec are not allowed, but updates to label/and some annotations are OK.
|
||||
// no-op updates pass validation.
|
||||
if !apiequality.Semantic.DeepEqual(newPvc.Spec, oldPvc.Spec) {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "field is immutable after creation"))
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
newPVCSpecCopy := newPvc.Spec.DeepCopy()
|
||||
|
||||
// lets make sure storage values are same.
|
||||
if newPvc.Status.Phase == api.ClaimBound && newPVCSpecCopy.Resources.Requests != nil {
|
||||
newPVCSpecCopy.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"]
|
||||
}
|
||||
|
||||
oldSize := oldPvc.Spec.Resources.Requests["storage"]
|
||||
newSize := newPvc.Spec.Resources.Requests["storage"]
|
||||
|
||||
if !apiequality.Semantic.DeepEqual(*newPVCSpecCopy, oldPvc.Spec) {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "is immutable after creation except resources.requests for bound claims"))
|
||||
}
|
||||
if newSize.Cmp(oldSize) < 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "resources", "requests", "storage"), "field can not be less than previous value"))
|
||||
}
|
||||
|
||||
} else {
|
||||
// changes to Spec are not allowed, but updates to label/and some annotations are OK.
|
||||
// no-op updates pass validation.
|
||||
if !apiequality.Semantic.DeepEqual(newPvc.Spec, oldPvc.Spec) {
|
||||
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "field is immutable after creation"))
|
||||
}
|
||||
}
|
||||
|
||||
// storageclass annotation should be immutable after creation
|
||||
|
@ -1611,6 +1632,10 @@ func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *api.PersistentVol
|
|||
if len(newPvc.Spec.AccessModes) == 0 {
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("Spec", "accessModes"), ""))
|
||||
}
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) && len(newPvc.Status.Conditions) > 0 {
|
||||
conditionPath := field.NewPath("status", "conditions")
|
||||
allErrs = append(allErrs, field.Forbidden(conditionPath, "invalid field"))
|
||||
}
|
||||
capPath := field.NewPath("status", "capacity")
|
||||
for r, qty := range newPvc.Status.Capacity {
|
||||
allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...)
|
||||
|
|
|
@ -530,6 +530,17 @@ func testVolumeClaim(name string, namespace string, spec api.PersistentVolumeCla
|
|||
}
|
||||
}
|
||||
|
||||
func testVolumeClaimWithStatus(
|
||||
name, namespace string,
|
||||
spec api.PersistentVolumeClaimSpec,
|
||||
status api.PersistentVolumeClaimStatus) *api.PersistentVolumeClaim {
|
||||
return &api.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
|
||||
Spec: spec,
|
||||
Status: status,
|
||||
}
|
||||
}
|
||||
|
||||
func testVolumeClaimStorageClass(name string, namespace string, annval string, spec api.PersistentVolumeClaimSpec) *api.PersistentVolumeClaim {
|
||||
annotations := map[string]string{
|
||||
v1.BetaStorageClassAnnotation: annval,
|
||||
|
@ -728,7 +739,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
||||
validClaim := testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{
|
||||
validClaim := testVolumeClaimWithStatus("foo", "ns", api.PersistentVolumeClaimSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
api.ReadOnlyMany,
|
||||
|
@ -738,7 +749,10 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||
api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
|
||||
},
|
||||
},
|
||||
}, api.PersistentVolumeClaimStatus{
|
||||
Phase: api.ClaimBound,
|
||||
})
|
||||
|
||||
validClaimStorageClass := testVolumeClaimStorageClass("foo", "ns", "fast", api.PersistentVolumeClaimSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{
|
||||
api.ReadOnlyMany,
|
||||
|
@ -828,50 +842,125 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||
},
|
||||
VolumeName: "volume",
|
||||
})
|
||||
validSizeUpdate := testVolumeClaimWithStatus("foo", "ns", api.PersistentVolumeClaimSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
api.ReadOnlyMany,
|
||||
},
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{
|
||||
api.ResourceName(api.ResourceStorage): resource.MustParse("15G"),
|
||||
},
|
||||
},
|
||||
}, api.PersistentVolumeClaimStatus{
|
||||
Phase: api.ClaimBound,
|
||||
})
|
||||
|
||||
invalidSizeUpdate := testVolumeClaimWithStatus("foo", "ns", api.PersistentVolumeClaimSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
api.ReadOnlyMany,
|
||||
},
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{
|
||||
api.ResourceName(api.ResourceStorage): resource.MustParse("5G"),
|
||||
},
|
||||
},
|
||||
}, api.PersistentVolumeClaimStatus{
|
||||
Phase: api.ClaimBound,
|
||||
})
|
||||
|
||||
unboundSizeUpdate := testVolumeClaimWithStatus("foo", "ns", api.PersistentVolumeClaimSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
api.ReadOnlyMany,
|
||||
},
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{
|
||||
api.ResourceName(api.ResourceStorage): resource.MustParse("12G"),
|
||||
},
|
||||
},
|
||||
}, api.PersistentVolumeClaimStatus{
|
||||
Phase: api.ClaimPending,
|
||||
})
|
||||
|
||||
scenarios := map[string]struct {
|
||||
isExpectedFailure bool
|
||||
oldClaim *api.PersistentVolumeClaim
|
||||
newClaim *api.PersistentVolumeClaim
|
||||
enableResize bool
|
||||
}{
|
||||
"valid-update-volumeName-only": {
|
||||
isExpectedFailure: false,
|
||||
oldClaim: validClaim,
|
||||
newClaim: validUpdateClaim,
|
||||
enableResize: false,
|
||||
},
|
||||
"valid-no-op-update": {
|
||||
isExpectedFailure: false,
|
||||
oldClaim: validUpdateClaim,
|
||||
newClaim: validUpdateClaim,
|
||||
enableResize: false,
|
||||
},
|
||||
"invalid-update-change-resources-on-bound-claim": {
|
||||
isExpectedFailure: true,
|
||||
oldClaim: validUpdateClaim,
|
||||
newClaim: invalidUpdateClaimResources,
|
||||
enableResize: false,
|
||||
},
|
||||
"invalid-update-change-access-modes-on-bound-claim": {
|
||||
isExpectedFailure: true,
|
||||
oldClaim: validUpdateClaim,
|
||||
newClaim: invalidUpdateClaimAccessModes,
|
||||
enableResize: false,
|
||||
},
|
||||
"invalid-update-change-storage-class-annotation-after-creation": {
|
||||
isExpectedFailure: true,
|
||||
oldClaim: validClaimStorageClass,
|
||||
newClaim: invalidUpdateClaimStorageClass,
|
||||
enableResize: false,
|
||||
},
|
||||
"valid-update-mutable-annotation": {
|
||||
isExpectedFailure: false,
|
||||
oldClaim: validClaimAnnotation,
|
||||
newClaim: validUpdateClaimMutableAnnotation,
|
||||
enableResize: false,
|
||||
},
|
||||
"valid-update-add-annotation": {
|
||||
isExpectedFailure: false,
|
||||
oldClaim: validClaim,
|
||||
newClaim: validAddClaimAnnotation,
|
||||
enableResize: false,
|
||||
},
|
||||
"valid-size-update-resize-disabled": {
|
||||
isExpectedFailure: true,
|
||||
oldClaim: validClaim,
|
||||
newClaim: validSizeUpdate,
|
||||
enableResize: false,
|
||||
},
|
||||
"valid-size-update-resize-enabled": {
|
||||
isExpectedFailure: false,
|
||||
oldClaim: validClaim,
|
||||
newClaim: validSizeUpdate,
|
||||
enableResize: true,
|
||||
},
|
||||
"invalid-size-update-resize-enabled": {
|
||||
isExpectedFailure: true,
|
||||
oldClaim: validClaim,
|
||||
newClaim: invalidSizeUpdate,
|
||||
enableResize: true,
|
||||
},
|
||||
"unbound-size-update-resize-enabled": {
|
||||
isExpectedFailure: true,
|
||||
oldClaim: validClaim,
|
||||
newClaim: unboundSizeUpdate,
|
||||
enableResize: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, scenario := range scenarios {
|
||||
// ensure we have a resource version specified for updates
|
||||
togglePVExpandFeature(scenario.enableResize, t)
|
||||
scenario.oldClaim.ResourceVersion = "1"
|
||||
scenario.newClaim.ResourceVersion = "1"
|
||||
errs := ValidatePersistentVolumeClaimUpdate(scenario.newClaim, scenario.oldClaim)
|
||||
|
@ -884,6 +973,23 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func togglePVExpandFeature(toggleFlag bool, t *testing.T) {
|
||||
if toggleFlag {
|
||||
// Enable alpha feature LocalStorageCapacityIsolation
|
||||
err := utilfeature.DefaultFeatureGate.Set("ExpandPersistentVolumes=true")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to enable feature gate for ExpandPersistentVolumes: %v", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err := utilfeature.DefaultFeatureGate.Set("ExpandPersistentVolumes=false")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to disable feature gate for ExpandPersistentVolumes: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateKeyToPath(t *testing.T) {
|
||||
testCases := []struct {
|
||||
kp api.KeyToPath
|
||||
|
@ -9232,6 +9338,68 @@ func TestValidateLimitRange(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestValidatePersistentVolumeClaimStatusUpdate(t *testing.T) {
|
||||
validClaim := testVolumeClaim("foo", "ns", api.PersistentVolumeClaimSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
api.ReadOnlyMany,
|
||||
},
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{
|
||||
api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
|
||||
},
|
||||
},
|
||||
})
|
||||
validConditionUpdate := testVolumeClaimWithStatus("foo", "ns", api.PersistentVolumeClaimSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{
|
||||
api.ReadWriteOnce,
|
||||
api.ReadOnlyMany,
|
||||
},
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: api.ResourceList{
|
||||
api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
|
||||
},
|
||||
},
|
||||
}, api.PersistentVolumeClaimStatus{
|
||||
Phase: api.ClaimPending,
|
||||
Conditions: []api.PersistentVolumeClaimCondition{
|
||||
{Type: api.PersistentVolumeClaimResizing, Status: api.ConditionTrue},
|
||||
},
|
||||
})
|
||||
scenarios := map[string]struct {
|
||||
isExpectedFailure bool
|
||||
oldClaim *api.PersistentVolumeClaim
|
||||
newClaim *api.PersistentVolumeClaim
|
||||
enableResize bool
|
||||
}{
|
||||
"condition-update-with-disabled-feature-gate": {
|
||||
isExpectedFailure: true,
|
||||
oldClaim: validClaim,
|
||||
newClaim: validConditionUpdate,
|
||||
enableResize: false,
|
||||
},
|
||||
"condition-update-with-enabled-feature-gate": {
|
||||
isExpectedFailure: false,
|
||||
oldClaim: validClaim,
|
||||
newClaim: validConditionUpdate,
|
||||
enableResize: true,
|
||||
},
|
||||
}
|
||||
for name, scenario := range scenarios {
|
||||
// ensure we have a resource version specified for updates
|
||||
togglePVExpandFeature(scenario.enableResize, t)
|
||||
scenario.oldClaim.ResourceVersion = "1"
|
||||
scenario.newClaim.ResourceVersion = "1"
|
||||
errs := ValidatePersistentVolumeClaimStatusUpdate(scenario.newClaim, scenario.oldClaim)
|
||||
if len(errs) == 0 && scenario.isExpectedFailure {
|
||||
t.Errorf("Unexpected success for scenario: %s", name)
|
||||
}
|
||||
if len(errs) > 0 && !scenario.isExpectedFailure {
|
||||
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateResourceQuota(t *testing.T) {
|
||||
spec := api.ResourceQuotaSpec{
|
||||
Hard: api.ResourceList{
|
||||
|
|
|
@ -427,6 +427,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
|||
in.(*PersistentVolumeClaim).DeepCopyInto(out.(*PersistentVolumeClaim))
|
||||
return nil
|
||||
}, InType: reflect.TypeOf(&PersistentVolumeClaim{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
in.(*PersistentVolumeClaimCondition).DeepCopyInto(out.(*PersistentVolumeClaimCondition))
|
||||
return nil
|
||||
}, InType: reflect.TypeOf(&PersistentVolumeClaimCondition{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
in.(*PersistentVolumeClaimList).DeepCopyInto(out.(*PersistentVolumeClaimList))
|
||||
return nil
|
||||
|
@ -3513,6 +3517,24 @@ func (in *PersistentVolumeClaim) DeepCopyObject() runtime.Object {
|
|||
}
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PersistentVolumeClaimCondition) DeepCopyInto(out *PersistentVolumeClaimCondition) {
|
||||
*out = *in
|
||||
in.LastProbeTime.DeepCopyInto(&out.LastProbeTime)
|
||||
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistentVolumeClaimCondition.
|
||||
func (in *PersistentVolumeClaimCondition) DeepCopy() *PersistentVolumeClaimCondition {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PersistentVolumeClaimCondition)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PersistentVolumeClaimList) DeepCopyInto(out *PersistentVolumeClaimList) {
|
||||
*out = *in
|
||||
|
@ -3602,6 +3624,13 @@ func (in *PersistentVolumeClaimStatus) DeepCopyInto(out *PersistentVolumeClaimSt
|
|||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]PersistentVolumeClaimCondition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,12 @@ type StorageClass struct {
|
|||
// PersistentVolumes of this storage class are created with
|
||||
// +optional
|
||||
MountOptions []string
|
||||
|
||||
// AllowVolumeExpansion shows whether the storage class allow volume expand
|
||||
// If the field is nil or not set, it would amount to expansion disabled
|
||||
// for all PVs created from this storageclass.
|
||||
// +optional
|
||||
AllowVolumeExpansion *bool
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
|
|
@ -51,6 +51,7 @@ func autoConvert_v1_StorageClass_To_storage_StorageClass(in *v1.StorageClass, ou
|
|||
out.Parameters = *(*map[string]string)(unsafe.Pointer(&in.Parameters))
|
||||
out.ReclaimPolicy = (*api.PersistentVolumeReclaimPolicy)(unsafe.Pointer(in.ReclaimPolicy))
|
||||
out.MountOptions = *(*[]string)(unsafe.Pointer(&in.MountOptions))
|
||||
out.AllowVolumeExpansion = (*bool)(unsafe.Pointer(in.AllowVolumeExpansion))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -65,6 +66,7 @@ func autoConvert_storage_StorageClass_To_v1_StorageClass(in *storage.StorageClas
|
|||
out.Parameters = *(*map[string]string)(unsafe.Pointer(&in.Parameters))
|
||||
out.ReclaimPolicy = (*core_v1.PersistentVolumeReclaimPolicy)(unsafe.Pointer(in.ReclaimPolicy))
|
||||
out.MountOptions = *(*[]string)(unsafe.Pointer(&in.MountOptions))
|
||||
out.AllowVolumeExpansion = (*bool)(unsafe.Pointer(in.AllowVolumeExpansion))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ func autoConvert_v1beta1_StorageClass_To_storage_StorageClass(in *v1beta1.Storag
|
|||
out.Parameters = *(*map[string]string)(unsafe.Pointer(&in.Parameters))
|
||||
out.ReclaimPolicy = (*api.PersistentVolumeReclaimPolicy)(unsafe.Pointer(in.ReclaimPolicy))
|
||||
out.MountOptions = *(*[]string)(unsafe.Pointer(&in.MountOptions))
|
||||
out.AllowVolumeExpansion = (*bool)(unsafe.Pointer(in.AllowVolumeExpansion))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -65,6 +66,7 @@ func autoConvert_storage_StorageClass_To_v1beta1_StorageClass(in *storage.Storag
|
|||
out.Parameters = *(*map[string]string)(unsafe.Pointer(&in.Parameters))
|
||||
out.ReclaimPolicy = (*v1.PersistentVolumeReclaimPolicy)(unsafe.Pointer(in.ReclaimPolicy))
|
||||
out.MountOptions = *(*[]string)(unsafe.Pointer(&in.MountOptions))
|
||||
out.AllowVolumeExpansion = (*bool)(unsafe.Pointer(in.AllowVolumeExpansion))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -13,9 +13,11 @@ go_library(
|
|||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -27,6 +29,7 @@ go_test(
|
|||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -23,9 +23,11 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
apivalidation "k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
// ValidateStorageClass validates a StorageClass.
|
||||
|
@ -34,6 +36,7 @@ func ValidateStorageClass(storageClass *storage.StorageClass) field.ErrorList {
|
|||
allErrs = append(allErrs, validateProvisioner(storageClass.Provisioner, field.NewPath("provisioner"))...)
|
||||
allErrs = append(allErrs, validateParameters(storageClass.Parameters, field.NewPath("parameters"))...)
|
||||
allErrs = append(allErrs, validateReclaimPolicy(storageClass.ReclaimPolicy, field.NewPath("reclaimPolicy"))...)
|
||||
allErrs = append(allErrs, validateAllowVolumeExpansion(storageClass.AllowVolumeExpansion, field.NewPath("allowVolumeExpansion"))...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
@ -108,3 +111,13 @@ func validateReclaimPolicy(reclaimPolicy *api.PersistentVolumeReclaimPolicy, fld
|
|||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateAllowVolumeExpansion tests that if ExpandPersistentVolumes feature gate is disabled, whether the AllowVolumeExpansion filed
|
||||
// of storage class is set
|
||||
func validateAllowVolumeExpansion(allowExpand *bool, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if allowExpand != nil && !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath, "field is disabled by feature-gate ExpandPersistentVolumes"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
)
|
||||
|
@ -123,3 +124,36 @@ func TestValidateStorageClass(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlphaExpandPersistentVolumesFeatureValidation(t *testing.T) {
|
||||
deleteReclaimPolicy := api.PersistentVolumeReclaimPolicy("Delete")
|
||||
falseVar := false
|
||||
testSC := &storage.StorageClass{
|
||||
// empty parameters
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||
Provisioner: "kubernetes.io/foo-provisioner",
|
||||
Parameters: map[string]string{},
|
||||
ReclaimPolicy: &deleteReclaimPolicy,
|
||||
AllowVolumeExpansion: &falseVar,
|
||||
}
|
||||
|
||||
// Enable alpha feature ExpandPersistentVolumes
|
||||
err := utilfeature.DefaultFeatureGate.Set("ExpandPersistentVolumes=true")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to enable feature gate for ExpandPersistentVolumes: %v", err)
|
||||
return
|
||||
}
|
||||
if errs := ValidateStorageClass(testSC); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
// Disable alpha feature ExpandPersistentVolumes
|
||||
err = utilfeature.DefaultFeatureGate.Set("ExpandPersistentVolumes=false")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to disable feature gate for ExpandPersistentVolumes: %v", err)
|
||||
return
|
||||
}
|
||||
if errs := ValidateStorageClass(testSC); len(errs) == 0 {
|
||||
t.Errorf("expected failure, but got no error")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -74,6 +74,15 @@ func (in *StorageClass) DeepCopyInto(out *StorageClass) {
|
|||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AllowVolumeExpansion != nil {
|
||||
in, out := &in.AllowVolumeExpansion, &out.AllowVolumeExpansion
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ filegroup(
|
|||
"//pkg/controller/ttl:all-srcs",
|
||||
"//pkg/controller/volume/attachdetach:all-srcs",
|
||||
"//pkg/controller/volume/events:all-srcs",
|
||||
"//pkg/controller/volume/expand:all-srcs",
|
||||
"//pkg/controller/volume/persistentvolume:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"expand_controller.go",
|
||||
"pvc_populator.go",
|
||||
"sync_volume_resize.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/controller/volume/expand/cache:go_default_library",
|
||||
"//pkg/controller/volume/expand/util:go_default_library",
|
||||
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
|
||||
"//pkg/util/io:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util/operationexecutor:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/controller/volume/expand/cache:all-srcs",
|
||||
"//pkg/controller/volume/expand/util:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
approvers:
|
||||
- saad-ali
|
||||
- jsafrane
|
||||
- gnufied
|
|
@ -0,0 +1,53 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["volume_resize_map.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/controller/volume/expand/util:go_default_library",
|
||||
"//pkg/util/strings:go_default_library",
|
||||
"//pkg/volume/util/types:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["volume_resize_map_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/volume/util/types:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
Copyright 2017 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 cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
commontypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/expand/util"
|
||||
"k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/volume/util/types"
|
||||
)
|
||||
|
||||
// VolumeResizeMap defines an interface that serves as a cache for holding pending resizing requests
|
||||
type VolumeResizeMap interface {
|
||||
// AddPVCUpdate adds pvc for resizing
|
||||
AddPVCUpdate(pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume)
|
||||
// DeletePVC deletes pvc that is scheduled for resizing
|
||||
DeletePVC(pvc *v1.PersistentVolumeClaim)
|
||||
// GetPVCsWithResizeRequest returns all pending pvc resize requests
|
||||
GetPVCsWithResizeRequest() []*PVCWithResizeRequest
|
||||
// MarkAsResized marks a pvc as fully resized
|
||||
MarkAsResized(*PVCWithResizeRequest, resource.Quantity) error
|
||||
// UpdatePVSize updates just pv size after cloudprovider resizing is successful
|
||||
UpdatePVSize(*PVCWithResizeRequest, resource.Quantity) error
|
||||
}
|
||||
|
||||
type volumeResizeMap struct {
|
||||
// map of unique pvc name and resize requests that are pending or inflight
|
||||
pvcrs map[types.UniquePVCName]*PVCWithResizeRequest
|
||||
// kube client for making API calls
|
||||
kubeClient clientset.Interface
|
||||
// for guarding access to pvcrs map
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// PVCWithResizeRequest struct defines data structure that stores state needed for
|
||||
// performing file system resize
|
||||
type PVCWithResizeRequest struct {
|
||||
// PVC that needs to be resized
|
||||
PVC *v1.PersistentVolumeClaim
|
||||
// persistentvolume
|
||||
PersistentVolume *v1.PersistentVolume
|
||||
// Current volume size
|
||||
CurrentSize resource.Quantity
|
||||
// Expended volume size
|
||||
ExpectedSize resource.Quantity
|
||||
}
|
||||
|
||||
// UniquePVCKey returns unique key of the PVC based on its UID
|
||||
func (pvcr *PVCWithResizeRequest) UniquePVCKey() types.UniquePVCName {
|
||||
return types.UniquePVCName(pvcr.PVC.UID)
|
||||
}
|
||||
|
||||
// QualifiedName returns namespace and name combination of the PVC
|
||||
func (pvcr *PVCWithResizeRequest) QualifiedName() string {
|
||||
return strings.JoinQualifiedName(pvcr.PVC.Namespace, pvcr.PVC.Name)
|
||||
}
|
||||
|
||||
// NewVolumeResizeMap returns new VolumeResizeMap which acts as a cache
|
||||
// for holding pending resize requests.
|
||||
func NewVolumeResizeMap(kubeClient clientset.Interface) VolumeResizeMap {
|
||||
resizeMap := &volumeResizeMap{}
|
||||
resizeMap.pvcrs = make(map[types.UniquePVCName]*PVCWithResizeRequest)
|
||||
resizeMap.kubeClient = kubeClient
|
||||
return resizeMap
|
||||
}
|
||||
|
||||
// AddPVCUpdate adds pvc for resizing
|
||||
func (resizeMap *volumeResizeMap) AddPVCUpdate(pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume) {
|
||||
if pvc.Namespace != pv.Spec.ClaimRef.Namespace || pvc.Name != pv.Spec.ClaimRef.Name {
|
||||
glog.V(4).Infof("Persistent Volume is not mapped to PVC being updated : %s", util.ClaimToClaimKey(pvc))
|
||||
return
|
||||
}
|
||||
|
||||
if pvc.Status.Phase != v1.ClaimBound {
|
||||
return
|
||||
}
|
||||
|
||||
resizeMap.Lock()
|
||||
defer resizeMap.Unlock()
|
||||
|
||||
pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||
pvcStatusSize := pvc.Status.Capacity[v1.ResourceStorage]
|
||||
|
||||
if pvcStatusSize.Cmp(pvcSize) >= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Adding pvc %s with Size %s/%s for resizing", util.ClaimToClaimKey(pvc), pvcSize.String(), pvcStatusSize.String())
|
||||
|
||||
pvcRequest := &PVCWithResizeRequest{
|
||||
PVC: pvc,
|
||||
CurrentSize: pvcStatusSize,
|
||||
ExpectedSize: pvcSize,
|
||||
PersistentVolume: pv,
|
||||
}
|
||||
resizeMap.pvcrs[types.UniquePVCName(pvc.UID)] = pvcRequest
|
||||
}
|
||||
|
||||
// GetPVCsWithResizeRequest returns all pending pvc resize requests
|
||||
func (resizeMap *volumeResizeMap) GetPVCsWithResizeRequest() []*PVCWithResizeRequest {
|
||||
resizeMap.Lock()
|
||||
defer resizeMap.Unlock()
|
||||
|
||||
pvcrs := []*PVCWithResizeRequest{}
|
||||
for _, pvcr := range resizeMap.pvcrs {
|
||||
pvcrs = append(pvcrs, pvcr)
|
||||
}
|
||||
// Empty out pvcrs map, we will add back failed resize requests later
|
||||
resizeMap.pvcrs = map[types.UniquePVCName]*PVCWithResizeRequest{}
|
||||
return pvcrs
|
||||
}
|
||||
|
||||
// DeletePVC removes given pvc object from list of pvcs that needs resizing.
|
||||
// deleting a pvc in this map doesn't affect operations that are already inflight.
|
||||
func (resizeMap *volumeResizeMap) DeletePVC(pvc *v1.PersistentVolumeClaim) {
|
||||
resizeMap.Lock()
|
||||
defer resizeMap.Unlock()
|
||||
pvcUniqueName := types.UniquePVCName(pvc.UID)
|
||||
glog.V(5).Infof("Removing PVC %v from resize map", pvcUniqueName)
|
||||
delete(resizeMap.pvcrs, pvcUniqueName)
|
||||
}
|
||||
|
||||
// MarkAsResized marks a pvc as fully resized
|
||||
func (resizeMap *volumeResizeMap) MarkAsResized(pvcr *PVCWithResizeRequest, newSize resource.Quantity) error {
|
||||
resizeMap.Lock()
|
||||
defer resizeMap.Unlock()
|
||||
|
||||
emptyCondition := []v1.PersistentVolumeClaimCondition{}
|
||||
|
||||
err := resizeMap.updatePVCCapacityAndConditions(pvcr, newSize, emptyCondition)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("Error updating PV spec capacity for volume %q with : %v", pvcr.QualifiedName(), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdatePVSize updates just pv size after cloudprovider resizing is successful
|
||||
func (resizeMap *volumeResizeMap) UpdatePVSize(pvcr *PVCWithResizeRequest, newSize resource.Quantity) error {
|
||||
resizeMap.Lock()
|
||||
defer resizeMap.Unlock()
|
||||
|
||||
oldPv := pvcr.PersistentVolume
|
||||
pvClone := oldPv.DeepCopy()
|
||||
|
||||
oldData, err := json.Marshal(pvClone)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unexpected error marshaling PV : %q with error %v", pvClone.Name, err)
|
||||
}
|
||||
|
||||
pvClone.Spec.Capacity[v1.ResourceStorage] = newSize
|
||||
|
||||
newData, err := json.Marshal(pvClone)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unexpected error marshaling PV : %q with error %v", pvClone.Name, err)
|
||||
}
|
||||
|
||||
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, pvClone)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error Creating two way merge patch for PV : %q with error %v", pvClone.Name, err)
|
||||
}
|
||||
|
||||
_, updateErr := resizeMap.kubeClient.CoreV1().PersistentVolumes().Patch(pvClone.Name, commontypes.StrategicMergePatchType, patchBytes)
|
||||
|
||||
if updateErr != nil {
|
||||
glog.V(4).Infof("Error updating pv %q with error : %v", pvClone.Name, updateErr)
|
||||
return updateErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (resizeMap *volumeResizeMap) updatePVCCapacityAndConditions(pvcr *PVCWithResizeRequest, newSize resource.Quantity, pvcConditions []v1.PersistentVolumeClaimCondition) error {
|
||||
|
||||
claimClone := pvcr.PVC.DeepCopy()
|
||||
|
||||
claimClone.Status.Capacity[v1.ResourceStorage] = newSize
|
||||
claimClone.Status.Conditions = pvcConditions
|
||||
|
||||
_, updateErr := resizeMap.kubeClient.CoreV1().PersistentVolumeClaims(claimClone.Namespace).UpdateStatus(claimClone)
|
||||
if updateErr != nil {
|
||||
glog.V(4).Infof("updating PersistentVolumeClaim[%s] status: failed: %v", pvcr.QualifiedName(), updateErr)
|
||||
return updateErr
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Copyright 2017 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 cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/kubernetes/pkg/volume/util/types"
|
||||
)
|
||||
|
||||
func Test_AddValidPvcUpdate(t *testing.T) {
|
||||
resizeMap := createTestVolumeResizeMap()
|
||||
claim1 := testVolumeClaim("foo", "ns", v1.PersistentVolumeClaimSpec{
|
||||
AccessModes: []v1.PersistentVolumeAccessMode{
|
||||
v1.ReadWriteOnce,
|
||||
v1.ReadOnlyMany,
|
||||
},
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse("10G"),
|
||||
},
|
||||
},
|
||||
VolumeName: "foo",
|
||||
})
|
||||
|
||||
claimClone := claim1.DeepCopy()
|
||||
claimClone.Spec.Resources.Requests[v1.ResourceStorage] = resource.MustParse("12G")
|
||||
pv := getPersistentVolume("foo", resource.MustParse("10G"), claim1)
|
||||
resizeMap.AddPVCUpdate(claimClone, pv)
|
||||
pvcr := resizeMap.GetPVCsWithResizeRequest()
|
||||
if len(pvcr) != 1 {
|
||||
t.Fatalf("Expected 1 pvc resize request got 0")
|
||||
}
|
||||
assert.Equal(t, resource.MustParse("12G"), pvcr[0].ExpectedSize)
|
||||
assert.Equal(t, 0, len(resizeMap.pvcrs))
|
||||
}
|
||||
|
||||
func createTestVolumeResizeMap() *volumeResizeMap {
|
||||
fakeClient := &fake.Clientset{}
|
||||
resizeMap := &volumeResizeMap{}
|
||||
resizeMap.pvcrs = make(map[types.UniquePVCName]*PVCWithResizeRequest)
|
||||
resizeMap.kubeClient = fakeClient
|
||||
return resizeMap
|
||||
}
|
||||
|
||||
func testVolumeClaim(name string, namespace string, spec v1.PersistentVolumeClaimSpec) *v1.PersistentVolumeClaim {
|
||||
return &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
|
||||
Spec: spec,
|
||||
Status: v1.PersistentVolumeClaimStatus{
|
||||
Phase: v1.ClaimBound,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getPersistentVolume(volumeName string, capacity resource.Quantity, pvc *v1.PersistentVolumeClaim) *v1.PersistentVolume {
|
||||
return &v1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: volumeName},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceName(v1.ResourceStorage): capacity,
|
||||
},
|
||||
ClaimRef: &v1.ObjectReference{
|
||||
Namespace: pvc.Namespace,
|
||||
Name: pvc.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
Copyright 2017 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 expand implements interfaces that attempt to resize a pvc
|
||||
// by adding pvc to a volume resize map from which PVCs are picked and
|
||||
// resized
|
||||
package expand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
corelisters "k8s.io/client-go/listers/core/v1"
|
||||
kcache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
||||
"k8s.io/kubernetes/pkg/util/io"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||
)
|
||||
|
||||
const (
|
||||
// How often resizing loop runs
|
||||
syncLoopPeriod time.Duration = 30 * time.Second
|
||||
// How often pvc populator runs
|
||||
populatorLoopPeriod time.Duration = 2 * time.Minute
|
||||
)
|
||||
|
||||
// ExpandController expands the pvs
|
||||
type ExpandController interface {
|
||||
Run(stopCh <-chan struct{})
|
||||
}
|
||||
|
||||
type expandController struct {
|
||||
// kubeClient is the kube API client used by volumehost to communicate with
|
||||
// the API server.
|
||||
kubeClient clientset.Interface
|
||||
|
||||
// pvcLister is the shared PVC lister used to fetch and store PVC
|
||||
// objects from the API server. It is shared with other controllers and
|
||||
// therefore the PVC objects in its store should be treated as immutable.
|
||||
pvcLister corelisters.PersistentVolumeClaimLister
|
||||
pvcsSynced kcache.InformerSynced
|
||||
|
||||
pvLister corelisters.PersistentVolumeLister
|
||||
pvSynced kcache.InformerSynced
|
||||
|
||||
// cloud provider used by volume host
|
||||
cloud cloudprovider.Interface
|
||||
|
||||
// volumePluginMgr used to initialize and fetch volume plugins
|
||||
volumePluginMgr volume.VolumePluginMgr
|
||||
|
||||
// recorder is used to record events in the API server
|
||||
recorder record.EventRecorder
|
||||
|
||||
// Volume resize map of volumes that needs resizing
|
||||
resizeMap cache.VolumeResizeMap
|
||||
|
||||
// Worker goroutine to process resize requests from resizeMap
|
||||
syncResize SyncVolumeResize
|
||||
|
||||
// Operation executor
|
||||
opExecutor operationexecutor.OperationExecutor
|
||||
|
||||
// populator for periodically polling all PVCs
|
||||
pvcPopulator PVCPopulator
|
||||
}
|
||||
|
||||
func NewExpandController(
|
||||
kubeClient clientset.Interface,
|
||||
pvcInformer coreinformers.PersistentVolumeClaimInformer,
|
||||
pvInformer coreinformers.PersistentVolumeInformer,
|
||||
cloud cloudprovider.Interface,
|
||||
plugins []volume.VolumePlugin) (ExpandController, error) {
|
||||
|
||||
expc := &expandController{
|
||||
kubeClient: kubeClient,
|
||||
cloud: cloud,
|
||||
pvcLister: pvcInformer.Lister(),
|
||||
pvcsSynced: pvcInformer.Informer().HasSynced,
|
||||
pvLister: pvInformer.Lister(),
|
||||
pvSynced: pvInformer.Informer().HasSynced,
|
||||
}
|
||||
|
||||
if err := expc.volumePluginMgr.InitPlugins(plugins, nil, expc); err != nil {
|
||||
return nil, fmt.Errorf("Could not initialize volume plugins for Expand Controller : %+v", err)
|
||||
}
|
||||
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
eventBroadcaster.StartLogging(glog.Infof)
|
||||
eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(kubeClient.CoreV1().RESTClient()).Events("")})
|
||||
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "volume_expand"})
|
||||
|
||||
expc.opExecutor = operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(
|
||||
kubeClient,
|
||||
&expc.volumePluginMgr,
|
||||
recorder,
|
||||
false))
|
||||
|
||||
expc.resizeMap = cache.NewVolumeResizeMap(expc.kubeClient)
|
||||
|
||||
pvcInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{
|
||||
UpdateFunc: expc.pvcUpdate,
|
||||
DeleteFunc: expc.deletePVC,
|
||||
})
|
||||
|
||||
expc.syncResize = NewSyncVolumeResize(syncLoopPeriod, expc.opExecutor, expc.resizeMap, kubeClient)
|
||||
expc.pvcPopulator = NewPVCPopulator(
|
||||
populatorLoopPeriod,
|
||||
expc.resizeMap,
|
||||
expc.pvcLister,
|
||||
expc.pvLister,
|
||||
kubeClient)
|
||||
return expc, nil
|
||||
}
|
||||
|
||||
func (expc *expandController) Run(stopCh <-chan struct{}) {
|
||||
defer runtime.HandleCrash()
|
||||
glog.Infof("Starting expand controller")
|
||||
defer glog.Infof("Shutting down expand controller")
|
||||
|
||||
if !controller.WaitForCacheSync("expand", stopCh, expc.pvcsSynced, expc.pvSynced) {
|
||||
return
|
||||
}
|
||||
|
||||
// Run volume sync work goroutine
|
||||
go expc.syncResize.Run(stopCh)
|
||||
// Start the pvc populator loop
|
||||
go expc.pvcPopulator.Run(stopCh)
|
||||
<-stopCh
|
||||
}
|
||||
|
||||
func (expc *expandController) deletePVC(obj interface{}) {
|
||||
pvc, ok := obj.(*v1.PersistentVolumeClaim)
|
||||
|
||||
if pvc == nil || !ok {
|
||||
return
|
||||
}
|
||||
|
||||
expc.resizeMap.DeletePVC(pvc)
|
||||
}
|
||||
|
||||
func (expc *expandController) pvcUpdate(oldObj, newObj interface{}) {
|
||||
oldPvc, ok := oldObj.(*v1.PersistentVolumeClaim)
|
||||
|
||||
if oldPvc == nil || !ok {
|
||||
return
|
||||
}
|
||||
|
||||
newPVC, ok := newObj.(*v1.PersistentVolumeClaim)
|
||||
|
||||
if newPVC == nil || !ok {
|
||||
return
|
||||
}
|
||||
pv, err := getPersistentVolume(newPVC, expc.pvLister)
|
||||
if err != nil {
|
||||
glog.V(5).Infof("Error getting Persistent Volume for pvc %q : %v", newPVC.UID, err)
|
||||
return
|
||||
}
|
||||
expc.resizeMap.AddPVCUpdate(newPVC, pv)
|
||||
}
|
||||
|
||||
func getPersistentVolume(pvc *v1.PersistentVolumeClaim, pvLister corelisters.PersistentVolumeLister) (*v1.PersistentVolume, error) {
|
||||
volumeName := pvc.Spec.VolumeName
|
||||
pv, err := pvLister.Get(volumeName)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find PV %q in PV informer cache with error : %v", volumeName, err)
|
||||
}
|
||||
|
||||
return pv.DeepCopy(), nil
|
||||
}
|
||||
|
||||
// Implementing VolumeHost interface
|
||||
func (expc *expandController) GetPluginDir(pluginName string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (expc *expandController) GetPodVolumeDir(podUID types.UID, pluginName string, volumeName string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (expc *expandController) GetPodPluginDir(podUID types.UID, pluginName string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (expc *expandController) GetKubeClient() clientset.Interface {
|
||||
return expc.kubeClient
|
||||
}
|
||||
|
||||
func (expc *expandController) NewWrapperMounter(volName string, spec volume.Spec, pod *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
|
||||
return nil, fmt.Errorf("NewWrapperMounter not supported by expand controller's VolumeHost implementation")
|
||||
}
|
||||
|
||||
func (expc *expandController) NewWrapperUnmounter(volName string, spec volume.Spec, podUID types.UID) (volume.Unmounter, error) {
|
||||
return nil, fmt.Errorf("NewWrapperUnmounter not supported by expand controller's VolumeHost implementation")
|
||||
}
|
||||
|
||||
func (expc *expandController) GetCloudProvider() cloudprovider.Interface {
|
||||
return expc.cloud
|
||||
}
|
||||
|
||||
func (expc *expandController) GetMounter(pluginName string) mount.Interface {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (expc *expandController) GetExec(pluginName string) mount.Exec {
|
||||
return mount.NewOsExec()
|
||||
}
|
||||
|
||||
func (expc *expandController) GetWriter() io.Writer {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (expc *expandController) GetHostName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (expc *expandController) GetHostIP() (net.IP, error) {
|
||||
return nil, fmt.Errorf("GetHostIP not supported by expand controller's VolumeHost implementation")
|
||||
}
|
||||
|
||||
func (expc *expandController) GetNodeAllocatable() (v1.ResourceList, error) {
|
||||
return v1.ResourceList{}, nil
|
||||
}
|
||||
|
||||
func (expc *expandController) GetSecretFunc() func(namespace, name string) (*v1.Secret, error) {
|
||||
return func(_, _ string) (*v1.Secret, error) {
|
||||
return nil, fmt.Errorf("GetSecret unsupported in expandController")
|
||||
}
|
||||
}
|
||||
|
||||
func (expc *expandController) GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error) {
|
||||
return func(_, _ string) (*v1.ConfigMap, error) {
|
||||
return nil, fmt.Errorf("GetConfigMap unsupported in expandController")
|
||||
}
|
||||
}
|
||||
|
||||
func (expc *expandController) GetNodeLabels() (map[string]string, error) {
|
||||
return nil, fmt.Errorf("GetNodeLabels unsupported in expandController")
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
Copyright 2017 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 reconciler implements interfaces that attempt to reconcile the
|
||||
// desired state of the with the actual state of the world by triggering
|
||||
// actions.
|
||||
|
||||
package expand
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
corelisters "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
||||
)
|
||||
|
||||
// PVCPopulator iterates through PVCs and checks if for bound PVCs
|
||||
// their size doesn't match with Persistent Volume size
|
||||
type PVCPopulator interface {
|
||||
Run(stopCh <-chan struct{})
|
||||
}
|
||||
|
||||
type pvcPopulator struct {
|
||||
loopPeriod time.Duration
|
||||
resizeMap cache.VolumeResizeMap
|
||||
pvcLister corelisters.PersistentVolumeClaimLister
|
||||
pvLister corelisters.PersistentVolumeLister
|
||||
kubeClient clientset.Interface
|
||||
}
|
||||
|
||||
func NewPVCPopulator(
|
||||
loopPeriod time.Duration,
|
||||
resizeMap cache.VolumeResizeMap,
|
||||
pvcLister corelisters.PersistentVolumeClaimLister,
|
||||
pvLister corelisters.PersistentVolumeLister,
|
||||
kubeClient clientset.Interface) PVCPopulator {
|
||||
populator := &pvcPopulator{
|
||||
loopPeriod: loopPeriod,
|
||||
pvcLister: pvcLister,
|
||||
pvLister: pvLister,
|
||||
resizeMap: resizeMap,
|
||||
kubeClient: kubeClient,
|
||||
}
|
||||
return populator
|
||||
}
|
||||
|
||||
func (populator *pvcPopulator) Run(stopCh <-chan struct{}) {
|
||||
wait.Until(populator.Sync, populator.loopPeriod, stopCh)
|
||||
}
|
||||
|
||||
func (populator *pvcPopulator) Sync() {
|
||||
pvcs, err := populator.pvcLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
glog.Errorf("Listing PVCs failed in populator : %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, pvc := range pvcs {
|
||||
pv, err := getPersistentVolume(pvc, populator.pvLister)
|
||||
|
||||
if err != nil {
|
||||
glog.V(5).Infof("Error getting persistent volume for pvc %q : %v", pvc.UID, err)
|
||||
continue
|
||||
}
|
||||
populator.resizeMap.AddPVCUpdate(pvc, pv)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
Copyright 2017 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 expand
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/expand/util"
|
||||
"k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
|
||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||
)
|
||||
|
||||
type SyncVolumeResize interface {
|
||||
Run(stopCh <-chan struct{})
|
||||
}
|
||||
|
||||
type syncResize struct {
|
||||
loopPeriod time.Duration
|
||||
resizeMap cache.VolumeResizeMap
|
||||
opsExecutor operationexecutor.OperationExecutor
|
||||
kubeClient clientset.Interface
|
||||
}
|
||||
|
||||
// NewSyncVolumeResize returns actual volume resize handler
|
||||
func NewSyncVolumeResize(
|
||||
loopPeriod time.Duration,
|
||||
opsExecutor operationexecutor.OperationExecutor,
|
||||
resizeMap cache.VolumeResizeMap,
|
||||
kubeClient clientset.Interface) SyncVolumeResize {
|
||||
rc := &syncResize{
|
||||
loopPeriod: loopPeriod,
|
||||
opsExecutor: opsExecutor,
|
||||
resizeMap: resizeMap,
|
||||
kubeClient: kubeClient,
|
||||
}
|
||||
return rc
|
||||
}
|
||||
|
||||
func (rc *syncResize) Run(stopCh <-chan struct{}) {
|
||||
wait.Until(rc.Sync, rc.loopPeriod, stopCh)
|
||||
}
|
||||
|
||||
func (rc *syncResize) Sync() {
|
||||
// Resize PVCs that require resize
|
||||
for _, pvcWithResizeRequest := range rc.resizeMap.GetPVCsWithResizeRequest() {
|
||||
uniqueVolumeKey := v1.UniqueVolumeName(pvcWithResizeRequest.UniquePVCKey())
|
||||
updatedClaim, err := markPVCResizeInProgress(pvcWithResizeRequest, rc.kubeClient)
|
||||
if err != nil {
|
||||
glog.V(5).Infof("Error setting PVC %s in progress with error : %v", pvcWithResizeRequest.QualifiedName(), err)
|
||||
continue
|
||||
}
|
||||
if updatedClaim != nil {
|
||||
pvcWithResizeRequest.PVC = updatedClaim
|
||||
}
|
||||
|
||||
if rc.opsExecutor.IsOperationPending(uniqueVolumeKey, "") {
|
||||
glog.V(10).Infof("Operation for PVC %v is already pending", pvcWithResizeRequest.QualifiedName())
|
||||
continue
|
||||
}
|
||||
glog.V(5).Infof("Starting opsExecutor.ExpandVolume for volume %s", pvcWithResizeRequest.QualifiedName())
|
||||
growFuncError := rc.opsExecutor.ExpandVolume(pvcWithResizeRequest, rc.resizeMap)
|
||||
if growFuncError != nil && !exponentialbackoff.IsExponentialBackoff(growFuncError) {
|
||||
glog.Errorf("Error growing pvc %s with %v", pvcWithResizeRequest.QualifiedName(), growFuncError)
|
||||
}
|
||||
if growFuncError == nil {
|
||||
glog.V(5).Infof("Started opsExecutor.ExpandVolume for volume %s", pvcWithResizeRequest.QualifiedName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func markPVCResizeInProgress(pvcWithResizeRequest *cache.PVCWithResizeRequest, kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
||||
// Mark PVC as Resize Started
|
||||
progressCondition := v1.PersistentVolumeClaimCondition{
|
||||
Type: v1.PersistentVolumeClaimResizing,
|
||||
Status: v1.ConditionTrue,
|
||||
LastTransitionTime: metav1.Now(),
|
||||
}
|
||||
conditions := []v1.PersistentVolumeClaimCondition{progressCondition}
|
||||
|
||||
return util.UpdatePVCCondition(pvcWithResizeRequest.PVC, conditions, kubeClient)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["util.go"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright 2017 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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// ClaimToClaimKey return namespace/name string for pvc
|
||||
func ClaimToClaimKey(claim *v1.PersistentVolumeClaim) string {
|
||||
return fmt.Sprintf("%s/%s", claim.Namespace, claim.Name)
|
||||
}
|
||||
|
||||
// UpdatePVCCondition updates pvc with given condition status
|
||||
func UpdatePVCCondition(pvc *v1.PersistentVolumeClaim,
|
||||
pvcConditions []v1.PersistentVolumeClaimCondition,
|
||||
kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
||||
|
||||
claimClone := pvc.DeepCopy()
|
||||
claimClone.Status.Conditions = pvcConditions
|
||||
updatedClaim, updateErr := kubeClient.CoreV1().PersistentVolumeClaims(claimClone.Namespace).UpdateStatus(claimClone)
|
||||
if updateErr != nil {
|
||||
glog.V(4).Infof("updating PersistentVolumeClaim[%s] status: failed: %v", ClaimToClaimKey(pvc), updateErr)
|
||||
return nil, updateErr
|
||||
}
|
||||
return updatedClaim, nil
|
||||
}
|
|
@ -114,6 +114,11 @@ const (
|
|||
// New local storage types to support local storage capacity isolation
|
||||
LocalStorageCapacityIsolation utilfeature.Feature = "LocalStorageCapacityIsolation"
|
||||
|
||||
// owner: @gnufied
|
||||
// alpha: v1.8
|
||||
// Ability to Expand persistent volumes
|
||||
ExpandPersistentVolumes utilfeature.Feature = "ExpandPersistentVolumes"
|
||||
|
||||
// owner: @verb
|
||||
// alpha: v1.8
|
||||
//
|
||||
|
@ -179,6 +184,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
|
|||
EnableEquivalenceClassCache: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
TaintNodesByCondition: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
MountPropagation: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
ExpandPersistentVolumes: {Default: false, PreRelease: utilfeature.Alpha},
|
||||
|
||||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||
// unintentionally on either side:
|
||||
|
|
|
@ -45,6 +45,7 @@ const (
|
|||
FailedAttachVolume = "FailedAttachVolume"
|
||||
FailedDetachVolume = "FailedDetachVolume"
|
||||
FailedMountVolume = "FailedMount"
|
||||
VolumeResizeFailed = "VolumeResizeFailed"
|
||||
FailedUnMountVolume = "FailedUnMount"
|
||||
WarnAlreadyMountedVolume = "AlreadyMountedVolume"
|
||||
SuccessfulDetachVolume = "SuccessfulDetachVolume"
|
||||
|
|
|
@ -25,6 +25,7 @@ go_library(
|
|||
"//pkg/api/helper/qos:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/api/validation:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/kubeapiserver/admission/util:go_default_library",
|
||||
"//pkg/quota:go_default_library",
|
||||
"//pkg/quota/generic:go_default_library",
|
||||
|
@ -36,6 +37,7 @@ go_library(
|
|||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
|
|
|
@ -27,11 +27,13 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/helper"
|
||||
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubeapiserver/admission/util"
|
||||
"k8s.io/kubernetes/pkg/quota"
|
||||
"k8s.io/kubernetes/pkg/quota/generic"
|
||||
|
@ -147,6 +149,10 @@ func (p *pvcEvaluator) Handles(a admission.Attributes) bool {
|
|||
if op == admission.Create {
|
||||
return true
|
||||
}
|
||||
if op == admission.Update && utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
return true
|
||||
}
|
||||
|
||||
updateUninitialized, err := util.IsUpdatingUninitializedObject(a)
|
||||
if err != nil {
|
||||
// fail closed, will try to give an evaluation.
|
||||
|
|
|
@ -16,10 +16,12 @@ go_library(
|
|||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/apis/storage/validation:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -21,9 +21,11 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/apis/storage/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
// storageClassStrategy implements behavior for StorageClass objects
|
||||
|
@ -42,7 +44,11 @@ func (storageClassStrategy) NamespaceScoped() bool {
|
|||
|
||||
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
|
||||
func (storageClassStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
|
||||
_ = obj.(*storage.StorageClass)
|
||||
class := obj.(*storage.StorageClass)
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
class.AllowVolumeExpansion = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (storageClassStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
|
||||
|
@ -60,8 +66,13 @@ func (storageClassStrategy) AllowCreateOnUpdate() bool {
|
|||
|
||||
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a PV
|
||||
func (storageClassStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
|
||||
_ = obj.(*storage.StorageClass)
|
||||
_ = old.(*storage.StorageClass)
|
||||
newClass := obj.(*storage.StorageClass)
|
||||
oldClass := old.(*storage.StorageClass)
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
newClass.AllowVolumeExpansion = nil
|
||||
oldClass.AllowVolumeExpansion = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (storageClassStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
|
||||
|
|
|
@ -127,6 +127,10 @@ func (plugin *glusterfsPlugin) SupportsBulkVolumeVerification() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (plugin *glusterfsPlugin) RequiresFSResize() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *glusterfsPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
|
||||
return []v1.PersistentVolumeAccessMode{
|
||||
v1.ReadWriteOnce,
|
||||
|
@ -1046,3 +1050,49 @@ func parseClassParameters(params map[string]string, kubeClient clientset.Interfa
|
|||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func (plugin *glusterfsPlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) {
|
||||
pvSpec := spec.PersistentVolume.Spec
|
||||
glog.V(2).Infof("Request to expand volume: %s ", pvSpec.Glusterfs.Path)
|
||||
volumeName := pvSpec.Glusterfs.Path
|
||||
|
||||
// Fetch the volume for expansion.
|
||||
volumeID := dstrings.TrimPrefix(volumeName, volPrefix)
|
||||
|
||||
//Get details of SC.
|
||||
class, err := volutil.GetClassForVolume(plugin.host.GetKubeClient(), spec.PersistentVolume)
|
||||
if err != nil {
|
||||
return oldSize, err
|
||||
}
|
||||
cfg, err := parseClassParameters(class.Parameters, plugin.host.GetKubeClient())
|
||||
if err != nil {
|
||||
return oldSize, err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Expanding volume %q with configuration %+v", volumeID, cfg)
|
||||
|
||||
//Create REST server connection
|
||||
cli := gcli.NewClient(cfg.url, cfg.user, cfg.secretValue)
|
||||
if cli == nil {
|
||||
glog.Errorf("failed to create glusterfs rest client")
|
||||
return oldSize, fmt.Errorf("failed to create glusterfs rest client, REST server authentication failed")
|
||||
}
|
||||
|
||||
// Find out delta size
|
||||
expansionSize := (newSize.Value() - oldSize.Value())
|
||||
expansionSizeGB := int(volume.RoundUpSize(expansionSize, 1024*1024*1024))
|
||||
|
||||
// Make volume expansion request
|
||||
volumeExpandReq := &gapi.VolumeExpandRequest{Size: expansionSizeGB}
|
||||
|
||||
// Expand the volume
|
||||
volumeInfoRes, err := cli.VolumeExpand(volumeID, volumeExpandReq)
|
||||
if err != nil {
|
||||
glog.Errorf("error when expanding the volume :%v", err)
|
||||
return oldSize, err
|
||||
}
|
||||
|
||||
glog.V(2).Infof("volume %s expanded to new size %d successfully", volumeName, volumeInfoRes.Size)
|
||||
newVolumeSize := resource.MustParse(fmt.Sprintf("%dG", volumeInfoRes.Size))
|
||||
return newVolumeSize, nil
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
|
@ -201,6 +202,14 @@ type AttachableVolumePlugin interface {
|
|||
GetDeviceMountRefs(deviceMountPath string) ([]string, error)
|
||||
}
|
||||
|
||||
// ExpandableVolumePlugin is an extended interface of VolumePlugin and is used for volumes that can be
|
||||
// expanded
|
||||
type ExpandableVolumePlugin interface {
|
||||
VolumePlugin
|
||||
ExpandVolumeDevice(spec *Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error)
|
||||
RequiresFSResize() bool
|
||||
}
|
||||
|
||||
// VolumeHost is an interface that plugins can use to access the kubelet.
|
||||
type VolumeHost interface {
|
||||
// GetPluginDir returns the absolute path to a directory under which
|
||||
|
@ -642,6 +651,32 @@ func (pm *VolumePluginMgr) FindAttachablePluginByName(name string) (AttachableVo
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// FindExpandablePluginBySpec fetches a persistent volume plugin by spec.
|
||||
func (pm *VolumePluginMgr) FindExpandablePluginBySpec(spec *Spec) (ExpandableVolumePlugin, error) {
|
||||
volumePlugin, err := pm.FindPluginBySpec(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if expandableVolumePlugin, ok := volumePlugin.(ExpandableVolumePlugin); ok {
|
||||
return expandableVolumePlugin, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// FindExpandablePluginBySpec fetches a persistent volume plugin by name.
|
||||
func (pm *VolumePluginMgr) FindExpandablePluginByName(name string) (ExpandableVolumePlugin, error) {
|
||||
volumePlugin, err := pm.FindPluginByName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if expandableVolumePlugin, ok := volumePlugin.(ExpandableVolumePlugin); ok {
|
||||
return expandableVolumePlugin, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewPersistentVolumeRecyclerPodTemplate creates a template for a recycler
|
||||
// pod. By default, a recycler pod simply runs "rm -rf" on a volume and tests
|
||||
// for emptiness. Most attributes of the template will be correct for most
|
||||
|
|
|
@ -13,6 +13,7 @@ go_library(
|
|||
"operation_generator.go",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/controller/volume/expand/cache:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/kubelet/events:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
|
@ -37,6 +38,7 @@ go_test(
|
|||
srcs = ["operation_executor_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/controller/volume/expand/cache:go_default_library",
|
||||
"//pkg/util/mount:go_default_library",
|
||||
"//pkg/volume:go_default_library",
|
||||
"//pkg/volume/util/types:go_default_library",
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
expandcache "k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
|
@ -119,6 +120,8 @@ type OperationExecutor interface {
|
|||
// IsOperationPending returns true if an operation for the given volumeName and podName is pending,
|
||||
// otherwise it returns false
|
||||
IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) bool
|
||||
// Expand Volume will grow size available to PVC
|
||||
ExpandVolume(*expandcache.PVCWithResizeRequest, expandcache.VolumeResizeMap) error
|
||||
}
|
||||
|
||||
// NewOperationExecutor returns a new instance of OperationExecutor.
|
||||
|
@ -719,6 +722,17 @@ func (oe *operationExecutor) UnmountDevice(
|
|||
deviceToDetach.VolumeName, "" /* podName */, unmountDeviceFunc, opCompleteFunc)
|
||||
}
|
||||
|
||||
func (oe *operationExecutor) ExpandVolume(pvcWithResizeRequest *expandcache.PVCWithResizeRequest, resizeMap expandcache.VolumeResizeMap) error {
|
||||
expandFunc, pluginName, err := oe.operationGenerator.GenerateExpandVolumeFunc(pvcWithResizeRequest, resizeMap)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uniqueVolumeKey := v1.UniqueVolumeName(pvcWithResizeRequest.UniquePVCKey())
|
||||
opCompleteFunc := util.OperationCompleteHook(pluginName, "expand_volume")
|
||||
return oe.pendingOperations.Run(uniqueVolumeKey, "", expandFunc, opCompleteFunc)
|
||||
}
|
||||
|
||||
func (oe *operationExecutor) VerifyControllerAttachedVolume(
|
||||
volumeToMount VolumeToMount,
|
||||
nodeName types.NodeName,
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
expandcache "k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||
|
@ -282,6 +283,14 @@ func (fopg *fakeOperationGenerator) GenerateVerifyControllerAttachedVolumeFunc(v
|
|||
}, "", nil
|
||||
}
|
||||
|
||||
func (fopg *fakeOperationGenerator) GenerateExpandVolumeFunc(pvcWithResizeRequest *expandcache.PVCWithResizeRequest,
|
||||
resizeMap expandcache.VolumeResizeMap) (func() error, string, error) {
|
||||
return func() error {
|
||||
startOperationAndBlock(fopg.ch, fopg.quit)
|
||||
return nil
|
||||
}, "", nil
|
||||
}
|
||||
|
||||
func (fopg *fakeOperationGenerator) GenerateBulkVolumeVerifyFunc(
|
||||
pluginNodeVolumes map[types.NodeName][]*volume.Spec,
|
||||
pluginNane string,
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/record"
|
||||
expandcache "k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kevents "k8s.io/kubernetes/pkg/kubelet/events"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
@ -100,6 +101,8 @@ type OperationGenerator interface {
|
|||
map[types.NodeName][]*volume.Spec,
|
||||
string,
|
||||
map[*volume.Spec]v1.UniqueVolumeName, ActualStateOfWorldAttacherUpdater) (func() error, error)
|
||||
|
||||
GenerateExpandVolumeFunc(*expandcache.PVCWithResizeRequest, expandcache.VolumeResizeMap) (func() error, string, error)
|
||||
}
|
||||
|
||||
func (og *operationGenerator) GenerateVolumesAreAttachedFunc(
|
||||
|
@ -726,6 +729,59 @@ func (og *operationGenerator) verifyVolumeIsSafeToDetach(
|
|||
return nil
|
||||
}
|
||||
|
||||
func (og *operationGenerator) GenerateExpandVolumeFunc(
|
||||
pvcWithResizeRequest *expandcache.PVCWithResizeRequest,
|
||||
resizeMap expandcache.VolumeResizeMap) (func() error, string, error) {
|
||||
|
||||
volumeSpec := volume.NewSpecFromPersistentVolume(pvcWithResizeRequest.PersistentVolume, false)
|
||||
|
||||
volumePlugin, err := og.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec)
|
||||
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("Error finding plugin for expanding volume: %q with error %v", pvcWithResizeRequest.QualifiedName(), err)
|
||||
}
|
||||
|
||||
expandFunc := func() error {
|
||||
newSize := pvcWithResizeRequest.ExpectedSize
|
||||
pvSize := pvcWithResizeRequest.PersistentVolume.Spec.Capacity[v1.ResourceStorage]
|
||||
if pvSize.Cmp(newSize) < 0 {
|
||||
updatedSize, expandErr := volumePlugin.ExpandVolumeDevice(
|
||||
volumeSpec,
|
||||
pvcWithResizeRequest.ExpectedSize,
|
||||
pvcWithResizeRequest.CurrentSize)
|
||||
|
||||
if expandErr != nil {
|
||||
glog.Errorf("Error expanding volume %q of plugin %s : %v", pvcWithResizeRequest.QualifiedName(), volumePlugin.GetPluginName(), expandErr)
|
||||
og.recorder.Eventf(pvcWithResizeRequest.PVC, v1.EventTypeWarning, kevents.VolumeResizeFailed, expandErr.Error())
|
||||
return expandErr
|
||||
}
|
||||
newSize = updatedSize
|
||||
updateErr := resizeMap.UpdatePVSize(pvcWithResizeRequest, newSize)
|
||||
|
||||
if updateErr != nil {
|
||||
glog.V(4).Infof("Error updating PV spec capacity for volume %q with : %v", pvcWithResizeRequest.QualifiedName(), updateErr)
|
||||
og.recorder.Eventf(pvcWithResizeRequest.PVC, v1.EventTypeWarning, kevents.VolumeResizeFailed, updateErr.Error())
|
||||
return updateErr
|
||||
}
|
||||
}
|
||||
|
||||
// No Cloudprovider resize needed, lets mark resizing as done
|
||||
if !volumePlugin.RequiresFSResize() {
|
||||
glog.V(4).Infof("Controller resizing done for PVC %s", pvcWithResizeRequest.QualifiedName())
|
||||
err := resizeMap.MarkAsResized(pvcWithResizeRequest, newSize)
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("Error marking pvc %s as resized : %v", pvcWithResizeRequest.QualifiedName(), err)
|
||||
og.recorder.Eventf(pvcWithResizeRequest.PVC, v1.EventTypeWarning, kevents.VolumeResizeFailed, err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
return expandFunc, volumePlugin.GetPluginName(), nil
|
||||
}
|
||||
|
||||
func checkMountOptionSupport(og *operationGenerator, volumeToMount VolumeToMount, plugin volume.VolumePlugin) error {
|
||||
mountOptions := volume.MountOptionFromSpec(volumeToMount.VolumeSpec)
|
||||
|
||||
|
|
|
@ -21,3 +21,6 @@ import "k8s.io/apimachinery/pkg/types"
|
|||
|
||||
// UniquePodName defines the type to key pods off of
|
||||
type UniquePodName types.UID
|
||||
|
||||
// UniquePVCName defines the type to key pvc off
|
||||
type UniquePVCName types.UID
|
||||
|
|
|
@ -28,6 +28,7 @@ filegroup(
|
|||
"//plugin/pkg/admission/namespace/exists:all-srcs",
|
||||
"//plugin/pkg/admission/noderestriction:all-srcs",
|
||||
"//plugin/pkg/admission/persistentvolume/label:all-srcs",
|
||||
"//plugin/pkg/admission/persistentvolume/resize:all-srcs",
|
||||
"//plugin/pkg/admission/podnodeselector:all-srcs",
|
||||
"//plugin/pkg/admission/podpreset:all-srcs",
|
||||
"//plugin/pkg/admission/podtolerationrestriction:all-srcs",
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["admission_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/storage:go_default_library",
|
||||
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["admission.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/helper:go_default_library",
|
||||
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||
"//pkg/client/listers/core/internalversion:go_default_library",
|
||||
"//pkg/client/listers/storage/internalversion:go_default_library",
|
||||
"//pkg/kubeapiserver/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
Copyright 2017 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 resize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
apihelper "k8s.io/kubernetes/pkg/api/helper"
|
||||
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||
pvlister "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
|
||||
storagelisters "k8s.io/kubernetes/pkg/client/listers/storage/internalversion"
|
||||
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||
)
|
||||
|
||||
const (
|
||||
// PluginName is the name of pvc resize admission plugin
|
||||
PluginName = "PersistentVolumeClaimResize"
|
||||
)
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
|
||||
plugin := newPlugin()
|
||||
return plugin, nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ admission.Interface = &persistentVolumeClaimResize{}
|
||||
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&persistentVolumeClaimResize{})
|
||||
|
||||
type persistentVolumeClaimResize struct {
|
||||
*admission.Handler
|
||||
|
||||
pvLister pvlister.PersistentVolumeLister
|
||||
scLister storagelisters.StorageClassLister
|
||||
}
|
||||
|
||||
func newPlugin() *persistentVolumeClaimResize {
|
||||
return &persistentVolumeClaimResize{
|
||||
Handler: admission.NewHandler(admission.Update),
|
||||
}
|
||||
}
|
||||
|
||||
func (pvcr *persistentVolumeClaimResize) SetInternalKubeInformerFactory(f informers.SharedInformerFactory) {
|
||||
pvcInformer := f.Core().InternalVersion().PersistentVolumes()
|
||||
pvcr.pvLister = pvcInformer.Lister()
|
||||
scInformer := f.Storage().InternalVersion().StorageClasses()
|
||||
pvcr.scLister = scInformer.Lister()
|
||||
pvcr.SetReadyFunc(func() bool {
|
||||
return pvcInformer.Informer().HasSynced() && scInformer.Informer().HasSynced()
|
||||
})
|
||||
}
|
||||
|
||||
// Validate ensures lister is set.
|
||||
func (pvcr *persistentVolumeClaimResize) Validate() error {
|
||||
if pvcr.pvLister == nil {
|
||||
return fmt.Errorf("missing persistent volume lister")
|
||||
}
|
||||
if pvcr.scLister == nil {
|
||||
return fmt.Errorf("missing storageclass lister")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pvcr *persistentVolumeClaimResize) Admit(a admission.Attributes) error {
|
||||
if a.GetResource().GroupResource() != api.Resource("persistentvolumeclaims") {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(a.GetSubresource()) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
pvc, ok := a.GetObject().(*api.PersistentVolumeClaim)
|
||||
// if we can't convert then we don't handle this object so just return
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
oldPvc, ok := a.GetOldObject().(*api.PersistentVolumeClaim)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Growing Persistent volumes is only allowed for PVCs for which their StorageClass
|
||||
// explicitly allows it
|
||||
if !pvcr.allowResize(pvc, oldPvc) {
|
||||
return admission.NewForbidden(a, fmt.Errorf("only dynamically provisioned pvc can be resized and "+
|
||||
"the storageclass that provisions the pvc must support resize"))
|
||||
}
|
||||
|
||||
// volume plugin must support resize
|
||||
pv, err := pvcr.pvLister.Get(pvc.Spec.VolumeName)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !pvcr.checkVolumePlugin(pv) {
|
||||
return admission.NewForbidden(a, fmt.Errorf("volume plugin does not support resize"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Growing Persistent volumes is only allowed for PVCs for which their StorageClass
|
||||
// explicitly allows it.
|
||||
func (pvcr *persistentVolumeClaimResize) allowResize(pvc, oldPvc *api.PersistentVolumeClaim) bool {
|
||||
pvcStorageClass := apihelper.GetPersistentVolumeClaimClass(pvc)
|
||||
oldPvcStorageClass := apihelper.GetPersistentVolumeClaimClass(oldPvc)
|
||||
if pvcStorageClass == "" || oldPvcStorageClass == "" || pvcStorageClass != oldPvcStorageClass {
|
||||
return false
|
||||
}
|
||||
sc, err := pvcr.scLister.Get(pvcStorageClass)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if sc.AllowVolumeExpansion != nil {
|
||||
return *sc.AllowVolumeExpansion
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checkVolumePlugin checks whether the volume plugin supports resize
|
||||
func (pvcr *persistentVolumeClaimResize) checkVolumePlugin(pv *api.PersistentVolume) bool {
|
||||
if pv.Spec.Glusterfs != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
Copyright 2017 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 resize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
)
|
||||
|
||||
func getResourceList(storage string) api.ResourceList {
|
||||
res := api.ResourceList{}
|
||||
if storage != "" {
|
||||
res[api.ResourceStorage] = resource.MustParse(storage)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func TestPVCResizeAdmission(t *testing.T) {
|
||||
goldClassName := "gold"
|
||||
trueVal := true
|
||||
falseVar := false
|
||||
goldClass := &storage.StorageClass{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "StorageClass",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: goldClassName,
|
||||
},
|
||||
Provisioner: "kubernetes.io/glusterfs",
|
||||
AllowVolumeExpansion: &trueVal,
|
||||
}
|
||||
silverClassName := "silver"
|
||||
silverClass := &storage.StorageClass{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "StorageClass",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: silverClassName,
|
||||
},
|
||||
Provisioner: "kubernetes.io/glusterfs",
|
||||
AllowVolumeExpansion: &falseVar,
|
||||
}
|
||||
expectNoError := func(err error) bool {
|
||||
return err == nil
|
||||
}
|
||||
expectDynamicallyProvisionedError := func(err error) bool {
|
||||
return strings.Contains(err.Error(), "only dynamically provisioned pvc can be resized and "+
|
||||
"the storageclass that provisions the pvc must support resize")
|
||||
}
|
||||
expectVolumePluginError := func(err error) bool {
|
||||
return strings.Contains(err.Error(), "volume plugin does not support resize")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
resource schema.GroupVersionResource
|
||||
subresource string
|
||||
oldObj runtime.Object
|
||||
newObj runtime.Object
|
||||
|
||||
checkError func(error) bool
|
||||
}{
|
||||
{
|
||||
name: "pvc-resize, update, no error",
|
||||
resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
|
||||
oldObj: &api.PersistentVolumeClaim{
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume1",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: getResourceList("1Gi"),
|
||||
},
|
||||
StorageClassName: &goldClassName,
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{
|
||||
Capacity: getResourceList("1Gi"),
|
||||
},
|
||||
},
|
||||
newObj: &api.PersistentVolumeClaim{
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume1",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: getResourceList("2Gi"),
|
||||
},
|
||||
StorageClassName: &goldClassName,
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{
|
||||
Capacity: getResourceList("2Gi"),
|
||||
},
|
||||
},
|
||||
checkError: expectNoError,
|
||||
},
|
||||
{
|
||||
name: "pvc-resize, update, volume plugin error",
|
||||
resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
|
||||
oldObj: &api.PersistentVolumeClaim{
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume2",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: getResourceList("1Gi"),
|
||||
},
|
||||
StorageClassName: &goldClassName,
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{
|
||||
Capacity: getResourceList("1Gi"),
|
||||
},
|
||||
},
|
||||
newObj: &api.PersistentVolumeClaim{
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume2",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: getResourceList("2Gi"),
|
||||
},
|
||||
StorageClassName: &goldClassName,
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{
|
||||
Capacity: getResourceList("2Gi"),
|
||||
},
|
||||
},
|
||||
checkError: expectVolumePluginError,
|
||||
},
|
||||
{
|
||||
name: "pvc-resize, update, dynamically provisioned error",
|
||||
resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
|
||||
oldObj: &api.PersistentVolumeClaim{
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume3",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: getResourceList("1Gi"),
|
||||
},
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{
|
||||
Capacity: getResourceList("1Gi"),
|
||||
},
|
||||
},
|
||||
newObj: &api.PersistentVolumeClaim{
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume3",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: getResourceList("2Gi"),
|
||||
},
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{
|
||||
Capacity: getResourceList("2Gi"),
|
||||
},
|
||||
},
|
||||
checkError: expectDynamicallyProvisionedError,
|
||||
},
|
||||
{
|
||||
name: "pvc-resize, update, dynamically provisioned error",
|
||||
resource: api.SchemeGroupVersion.WithResource("persistentvolumeclaims"),
|
||||
oldObj: &api.PersistentVolumeClaim{
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume4",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: getResourceList("1Gi"),
|
||||
},
|
||||
StorageClassName: &silverClassName,
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{
|
||||
Capacity: getResourceList("1Gi"),
|
||||
},
|
||||
},
|
||||
newObj: &api.PersistentVolumeClaim{
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
VolumeName: "volume4",
|
||||
Resources: api.ResourceRequirements{
|
||||
Requests: getResourceList("2Gi"),
|
||||
},
|
||||
StorageClassName: &silverClassName,
|
||||
},
|
||||
Status: api.PersistentVolumeClaimStatus{
|
||||
Capacity: getResourceList("2Gi"),
|
||||
},
|
||||
},
|
||||
checkError: expectDynamicallyProvisionedError,
|
||||
},
|
||||
}
|
||||
|
||||
ctrl := newPlugin()
|
||||
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
|
||||
ctrl.SetInternalKubeInformerFactory(informerFactory)
|
||||
err := ctrl.Validate()
|
||||
if err != nil {
|
||||
t.Fatalf("neither pv lister nor storageclass lister can be nil")
|
||||
}
|
||||
|
||||
pv1 := &api.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "volume1"},
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
Glusterfs: &api.GlusterfsVolumeSource{
|
||||
EndpointsName: "http://localhost:8080/",
|
||||
Path: "/heketi",
|
||||
ReadOnly: false,
|
||||
},
|
||||
},
|
||||
StorageClassName: goldClassName,
|
||||
},
|
||||
}
|
||||
pv2 := &api.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "volume2"},
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
HostPath: &api.HostPathVolumeSource{},
|
||||
},
|
||||
StorageClassName: goldClassName,
|
||||
},
|
||||
}
|
||||
|
||||
pvs := []*api.PersistentVolume{}
|
||||
pvs = append(pvs, pv1, pv2)
|
||||
|
||||
for _, pv := range pvs {
|
||||
err := informerFactory.Core().InternalVersion().PersistentVolumes().Informer().GetStore().Add(pv)
|
||||
if err != nil {
|
||||
fmt.Println("add pv error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
scs := []*storage.StorageClass{}
|
||||
scs = append(scs, goldClass, silverClass)
|
||||
for _, sc := range scs {
|
||||
err := informerFactory.Storage().InternalVersion().StorageClasses().Informer().GetStore().Add(sc)
|
||||
if err != nil {
|
||||
fmt.Println("add storageclass error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
operation := admission.Update
|
||||
attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, nil)
|
||||
|
||||
err := ctrl.Admit(attributes)
|
||||
fmt.Println(tc.name)
|
||||
fmt.Println(err)
|
||||
if !tc.checkError(err) {
|
||||
t.Errorf("%v: unexpected err: %v", tc.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,9 @@ import (
|
|||
"github.com/golang/glog"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
const saRolePrefix = "system:controller:"
|
||||
|
@ -121,6 +123,23 @@ func buildControllerRoles() ([]rbac.ClusterRole, []rbac.ClusterRoleBinding) {
|
|||
eventsRule(),
|
||||
},
|
||||
})
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "expand-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update", "patch").Groups(legacyGroup).Resources("persistentvolumes").RuleOrDie(),
|
||||
rbac.NewRule("update", "patch").Groups(legacyGroup).Resources("persistentvolumeclaims/status").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
|
||||
// glusterfs
|
||||
rbac.NewRule("get", "list", "watch").Groups(storageGroup).Resources("storageclasses").RuleOrDie(),
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("services", "endpoints").RuleOrDie(),
|
||||
rbac.NewRule("get").Groups(legacyGroup).Resources("secrets").RuleOrDie(),
|
||||
eventsRule(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "generic-garbage-collector"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2133,6 +2133,31 @@ message PersistentVolumeClaim {
|
|||
optional PersistentVolumeClaimStatus status = 3;
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimCondition contails details about state of pvc
|
||||
message PersistentVolumeClaimCondition {
|
||||
optional string type = 1;
|
||||
|
||||
optional string status = 2;
|
||||
|
||||
// Last time we probed the condition.
|
||||
// +optional
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastProbeTime = 3;
|
||||
|
||||
// Last time the condition transitioned from one status to another.
|
||||
// +optional
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastTransitionTime = 4;
|
||||
|
||||
// Unique, this should be a short, machine understandable string that gives the reason
|
||||
// for condition's last transition. If it reports "ResizeStarted" that means the underlying
|
||||
// persistent volume is being resized.
|
||||
// +optional
|
||||
optional string reason = 5;
|
||||
|
||||
// Human-readable message indicating details about last transition.
|
||||
// +optional
|
||||
optional string message = 6;
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimList is a list of PersistentVolumeClaim items.
|
||||
message PersistentVolumeClaimList {
|
||||
// Standard list metadata.
|
||||
|
@ -2186,6 +2211,13 @@ message PersistentVolumeClaimStatus {
|
|||
// Represents the actual resources of the underlying volume.
|
||||
// +optional
|
||||
map<string, k8s.io.apimachinery.pkg.api.resource.Quantity> capacity = 3;
|
||||
|
||||
// Current Condition of persistent volume claim. If underlying persistent volume is being
|
||||
// resized then the Condition will be set to 'ResizeStarted'.
|
||||
// +optional
|
||||
// +patchMergeKey=type
|
||||
// +patchStrategy=merge
|
||||
repeated PersistentVolumeClaimCondition conditions = 4;
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
|
||||
|
|
|
@ -630,6 +630,34 @@ type PersistentVolumeClaimSpec struct {
|
|||
StorageClassName *string `json:"storageClassName,omitempty" protobuf:"bytes,5,opt,name=storageClassName"`
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type
|
||||
type PersistentVolumeClaimConditionType string
|
||||
|
||||
const (
|
||||
// PersistentVolumeClaimResizing - a user trigger resize of pvc has been started
|
||||
PersistentVolumeClaimResizing PersistentVolumeClaimConditionType = "Resizing"
|
||||
)
|
||||
|
||||
// PersistentVolumeClaimCondition contails details about state of pvc
|
||||
type PersistentVolumeClaimCondition struct {
|
||||
Type PersistentVolumeClaimConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=PersistentVolumeClaimConditionType"`
|
||||
Status ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=ConditionStatus"`
|
||||
// Last time we probed the condition.
|
||||
// +optional
|
||||
LastProbeTime metav1.Time `json:"lastProbeTime,omitempty" protobuf:"bytes,3,opt,name=lastProbeTime"`
|
||||
// Last time the condition transitioned from one status to another.
|
||||
// +optional
|
||||
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastTransitionTime"`
|
||||
// Unique, this should be a short, machine understandable string that gives the reason
|
||||
// for condition's last transition. If it reports "ResizeStarted" that means the underlying
|
||||
// persistent volume is being resized.
|
||||
// +optional
|
||||
Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
|
||||
// Human-readable message indicating details about last transition.
|
||||
// +optional
|
||||
Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimStatus is the current status of a persistent volume claim.
|
||||
type PersistentVolumeClaimStatus struct {
|
||||
// Phase represents the current phase of PersistentVolumeClaim.
|
||||
|
@ -642,6 +670,12 @@ type PersistentVolumeClaimStatus struct {
|
|||
// Represents the actual resources of the underlying volume.
|
||||
// +optional
|
||||
Capacity ResourceList `json:"capacity,omitempty" protobuf:"bytes,3,rep,name=capacity,casttype=ResourceList,castkey=ResourceName"`
|
||||
// Current Condition of persistent volume claim. If underlying persistent volume is being
|
||||
// resized then the Condition will be set to 'ResizeStarted'.
|
||||
// +optional
|
||||
// +patchMergeKey=type
|
||||
// +patchStrategy=merge
|
||||
Conditions []PersistentVolumeClaimCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,4,rep,name=conditions"`
|
||||
}
|
||||
|
||||
type PersistentVolumeAccessMode string
|
||||
|
|
|
@ -1122,6 +1122,18 @@ func (PersistentVolumeClaim) SwaggerDoc() map[string]string {
|
|||
return map_PersistentVolumeClaim
|
||||
}
|
||||
|
||||
var map_PersistentVolumeClaimCondition = map[string]string{
|
||||
"": "PersistentVolumeClaimCondition contails details about state of pvc",
|
||||
"lastProbeTime": "Last time we probed the condition.",
|
||||
"lastTransitionTime": "Last time the condition transitioned from one status to another.",
|
||||
"reason": "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized.",
|
||||
"message": "Human-readable message indicating details about last transition.",
|
||||
}
|
||||
|
||||
func (PersistentVolumeClaimCondition) SwaggerDoc() map[string]string {
|
||||
return map_PersistentVolumeClaimCondition
|
||||
}
|
||||
|
||||
var map_PersistentVolumeClaimList = map[string]string{
|
||||
"": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.",
|
||||
"metadata": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
|
@ -1150,6 +1162,7 @@ var map_PersistentVolumeClaimStatus = map[string]string{
|
|||
"phase": "Phase represents the current phase of PersistentVolumeClaim.",
|
||||
"accessModes": "AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1",
|
||||
"capacity": "Represents the actual resources of the underlying volume.",
|
||||
"conditions": "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.",
|
||||
}
|
||||
|
||||
func (PersistentVolumeClaimStatus) SwaggerDoc() map[string]string {
|
||||
|
|
|
@ -427,6 +427,10 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
|||
in.(*PersistentVolumeClaim).DeepCopyInto(out.(*PersistentVolumeClaim))
|
||||
return nil
|
||||
}, InType: reflect.TypeOf(&PersistentVolumeClaim{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
in.(*PersistentVolumeClaimCondition).DeepCopyInto(out.(*PersistentVolumeClaimCondition))
|
||||
return nil
|
||||
}, InType: reflect.TypeOf(&PersistentVolumeClaimCondition{})},
|
||||
conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||
in.(*PersistentVolumeClaimList).DeepCopyInto(out.(*PersistentVolumeClaimList))
|
||||
return nil
|
||||
|
@ -3499,6 +3503,24 @@ func (in *PersistentVolumeClaim) DeepCopyObject() runtime.Object {
|
|||
}
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PersistentVolumeClaimCondition) DeepCopyInto(out *PersistentVolumeClaimCondition) {
|
||||
*out = *in
|
||||
in.LastProbeTime.DeepCopyInto(&out.LastProbeTime)
|
||||
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PersistentVolumeClaimCondition.
|
||||
func (in *PersistentVolumeClaimCondition) DeepCopy() *PersistentVolumeClaimCondition {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PersistentVolumeClaimCondition)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PersistentVolumeClaimList) DeepCopyInto(out *PersistentVolumeClaimList) {
|
||||
*out = *in
|
||||
|
@ -3588,6 +3610,13 @@ func (in *PersistentVolumeClaimStatus) DeepCopyInto(out *PersistentVolumeClaimSt
|
|||
(*out)[key] = val.DeepCopy()
|
||||
}
|
||||
}
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]PersistentVolumeClaimCondition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,16 @@ func (m *StorageClass) MarshalTo(dAtA []byte) (int, error) {
|
|||
i += copy(dAtA[i:], s)
|
||||
}
|
||||
}
|
||||
if m.AllowVolumeExpansion != nil {
|
||||
dAtA[i] = 0x30
|
||||
i++
|
||||
if *m.AllowVolumeExpansion {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
@ -229,6 +239,9 @@ func (m *StorageClass) Size() (n int) {
|
|||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.AllowVolumeExpansion != nil {
|
||||
n += 2
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -279,6 +292,7 @@ func (this *StorageClass) String() string {
|
|||
`Parameters:` + mapStringForParameters + `,`,
|
||||
`ReclaimPolicy:` + valueToStringGenerated(this.ReclaimPolicy) + `,`,
|
||||
`MountOptions:` + fmt.Sprintf("%v", this.MountOptions) + `,`,
|
||||
`AllowVolumeExpansion:` + valueToStringGenerated(this.AllowVolumeExpansion) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
|
@ -565,6 +579,27 @@ func (m *StorageClass) Unmarshal(dAtA []byte) error {
|
|||
}
|
||||
m.MountOptions = append(m.MountOptions, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
case 6:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AllowVolumeExpansion", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
b := bool(v != 0)
|
||||
m.AllowVolumeExpansion = &b
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
|
@ -807,40 +842,43 @@ func init() {
|
|||
}
|
||||
|
||||
var fileDescriptorGenerated = []byte{
|
||||
// 558 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xcd, 0x6e, 0xd3, 0x40,
|
||||
0x10, 0x8e, 0x13, 0x22, 0x35, 0x9b, 0x44, 0x44, 0x06, 0x24, 0x2b, 0x07, 0x27, 0x2a, 0x97, 0x08,
|
||||
0x89, 0xdd, 0xa6, 0x2d, 0xa8, 0x42, 0x82, 0x43, 0x50, 0x25, 0x90, 0xa8, 0x1a, 0x19, 0x89, 0x03,
|
||||
0xe2, 0xc0, 0xc6, 0x19, 0x9c, 0xc5, 0x3f, 0x6b, 0xed, 0xae, 0x2d, 0xe5, 0xc6, 0x23, 0xf0, 0x3c,
|
||||
0x3c, 0x41, 0x8e, 0x3d, 0xf6, 0x14, 0x11, 0xf3, 0x06, 0x1c, 0x39, 0x21, 0xdb, 0x21, 0x76, 0x9a,
|
||||
0x54, 0xf4, 0xb6, 0x33, 0xf3, 0x7d, 0xdf, 0xce, 0xcc, 0x37, 0xe8, 0x95, 0x7b, 0x26, 0x31, 0xe3,
|
||||
0xc4, 0x8d, 0x26, 0x20, 0x02, 0x50, 0x20, 0x49, 0x0c, 0xc1, 0x94, 0x0b, 0xb2, 0x2e, 0xd0, 0x90,
|
||||
0x11, 0xa9, 0xb8, 0xa0, 0x0e, 0x90, 0x78, 0x48, 0x1c, 0x08, 0x40, 0x50, 0x05, 0x53, 0x1c, 0x0a,
|
||||
0xae, 0xb8, 0xfe, 0x28, 0x87, 0x61, 0x1a, 0x32, 0xbc, 0x86, 0xe1, 0x78, 0xd8, 0x7d, 0xea, 0x30,
|
||||
0x35, 0x8b, 0x26, 0xd8, 0xe6, 0x3e, 0x71, 0xb8, 0xc3, 0x49, 0x86, 0x9e, 0x44, 0x5f, 0xb2, 0x28,
|
||||
0x0b, 0xb2, 0x57, 0xae, 0xd2, 0x7d, 0xb2, 0xf7, 0xb3, 0x09, 0x28, 0xba, 0xf3, 0x63, 0xf7, 0xb4,
|
||||
0xc0, 0xfa, 0xd4, 0x9e, 0xb1, 0x00, 0xc4, 0x9c, 0x84, 0xae, 0x93, 0x26, 0x24, 0xf1, 0x41, 0xd1,
|
||||
0x3d, 0x7d, 0x76, 0xc9, 0x6d, 0x2c, 0x11, 0x05, 0x8a, 0xf9, 0xb0, 0x43, 0x78, 0xfe, 0x3f, 0x82,
|
||||
0xb4, 0x67, 0xe0, 0xd3, 0x1d, 0xde, 0xc9, 0x6d, 0xbc, 0x48, 0x31, 0x8f, 0xb0, 0x40, 0x49, 0x25,
|
||||
0x6e, 0x92, 0x0e, 0x7f, 0xd7, 0x50, 0xeb, 0x7d, 0x3e, 0xf7, 0x6b, 0x8f, 0x4a, 0xa9, 0x7f, 0x46,
|
||||
0x07, 0xe9, 0x24, 0x53, 0xaa, 0xa8, 0xa1, 0xf5, 0xb5, 0x41, 0xf3, 0xf8, 0x08, 0x17, 0x9b, 0xde,
|
||||
0x08, 0xe3, 0xd0, 0x75, 0xd2, 0x84, 0xc4, 0x29, 0x1a, 0xc7, 0x43, 0x7c, 0x39, 0xf9, 0x0a, 0xb6,
|
||||
0xba, 0x00, 0x45, 0x47, 0xfa, 0x62, 0xd9, 0xab, 0x24, 0xcb, 0x1e, 0x2a, 0x72, 0xd6, 0x46, 0x55,
|
||||
0x7f, 0x86, 0x9a, 0xa1, 0xe0, 0x31, 0x93, 0x8c, 0x07, 0x20, 0x8c, 0x6a, 0x5f, 0x1b, 0x34, 0x46,
|
||||
0x0f, 0xd6, 0x94, 0xe6, 0xb8, 0x28, 0x59, 0x65, 0x9c, 0xee, 0x20, 0x14, 0x52, 0x41, 0x7d, 0x50,
|
||||
0x20, 0xa4, 0x51, 0xeb, 0xd7, 0x06, 0xcd, 0xe3, 0x13, 0xbc, 0xf7, 0x08, 0x70, 0x79, 0x22, 0x3c,
|
||||
0xde, 0xb0, 0xce, 0x03, 0x25, 0xe6, 0x45, 0x77, 0x45, 0xc1, 0x2a, 0x49, 0xeb, 0x2e, 0x6a, 0x0b,
|
||||
0xb0, 0x3d, 0xca, 0xfc, 0x31, 0xf7, 0x98, 0x3d, 0x37, 0xee, 0x65, 0x1d, 0x9e, 0x27, 0xcb, 0x5e,
|
||||
0xdb, 0x2a, 0x17, 0xfe, 0x2c, 0x7b, 0x47, 0xa5, 0xf3, 0xb1, 0xb9, 0x48, 0x6f, 0x07, 0x8f, 0x41,
|
||||
0x48, 0x26, 0x15, 0x04, 0xea, 0x03, 0xf7, 0x22, 0x1f, 0xb6, 0x38, 0xd6, 0xb6, 0xb6, 0x7e, 0x8a,
|
||||
0x5a, 0x3e, 0x8f, 0x02, 0x75, 0x19, 0x2a, 0xc6, 0x03, 0x69, 0xd4, 0xfb, 0xb5, 0x41, 0x63, 0xd4,
|
||||
0x49, 0x96, 0xbd, 0xd6, 0x45, 0x29, 0x6f, 0x6d, 0xa1, 0xba, 0x2f, 0xd1, 0xfd, 0x1b, 0x53, 0xe9,
|
||||
0x1d, 0x54, 0x73, 0x61, 0x9e, 0x59, 0xd6, 0xb0, 0xd2, 0xa7, 0xfe, 0x10, 0xd5, 0x63, 0xea, 0x45,
|
||||
0x90, 0x6f, 0xd8, 0xca, 0x83, 0x17, 0xd5, 0x33, 0xed, 0xf0, 0x87, 0x86, 0x3a, 0xe5, 0x15, 0xbd,
|
||||
0x63, 0x52, 0xe9, 0x9f, 0x76, 0x8c, 0xc7, 0x77, 0x33, 0x3e, 0x65, 0x67, 0xb6, 0x77, 0xd6, 0x8b,
|
||||
0x3d, 0xf8, 0x97, 0x29, 0x99, 0xfe, 0x06, 0xd5, 0x99, 0x02, 0x5f, 0x1a, 0xd5, 0xcc, 0xb8, 0xc7,
|
||||
0x77, 0x30, 0x6e, 0xd4, 0x5e, 0xeb, 0xd5, 0xdf, 0xa6, 0x4c, 0x2b, 0x17, 0x18, 0x0d, 0x16, 0x2b,
|
||||
0xb3, 0x72, 0xb5, 0x32, 0x2b, 0xd7, 0x2b, 0xb3, 0xf2, 0x2d, 0x31, 0xb5, 0x45, 0x62, 0x6a, 0x57,
|
||||
0x89, 0xa9, 0x5d, 0x27, 0xa6, 0xf6, 0x33, 0x31, 0xb5, 0xef, 0xbf, 0xcc, 0xca, 0xc7, 0x6a, 0x3c,
|
||||
0xfc, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xbf, 0xb4, 0xf1, 0xb4, 0x62, 0x04, 0x00, 0x00,
|
||||
// 593 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0x6f, 0xd3, 0x3e,
|
||||
0x18, 0xc6, 0x9b, 0x76, 0xfd, 0x6b, 0x73, 0x37, 0xfd, 0xab, 0x30, 0xa4, 0xa8, 0x87, 0xb4, 0x1a,
|
||||
0x97, 0x0a, 0x09, 0x7b, 0xdd, 0x06, 0x9a, 0x90, 0x40, 0xa2, 0x68, 0x12, 0x48, 0x9b, 0x56, 0x05,
|
||||
0x89, 0x03, 0xe2, 0x80, 0x9b, 0xbd, 0x64, 0x26, 0x89, 0x1d, 0xd9, 0x4e, 0xa0, 0x37, 0x3e, 0x02,
|
||||
0x9f, 0x87, 0x13, 0xc7, 0x1d, 0x77, 0xdc, 0x29, 0x62, 0xe1, 0x5b, 0x70, 0x42, 0x49, 0xca, 0x92,
|
||||
0xad, 0x9d, 0xd8, 0xcd, 0x7e, 0xdf, 0xe7, 0xf7, 0xd8, 0x7e, 0xfd, 0xa0, 0xe7, 0xfe, 0xbe, 0xc2,
|
||||
0x4c, 0x10, 0x3f, 0x9e, 0x82, 0xe4, 0xa0, 0x41, 0x91, 0x04, 0xf8, 0x89, 0x90, 0x64, 0xde, 0xa0,
|
||||
0x11, 0x23, 0x4a, 0x0b, 0x49, 0x3d, 0x20, 0xc9, 0x88, 0x78, 0xc0, 0x41, 0x52, 0x0d, 0x27, 0x38,
|
||||
0x92, 0x42, 0x0b, 0xf3, 0x7e, 0x29, 0xc3, 0x34, 0x62, 0x78, 0x2e, 0xc3, 0xc9, 0xa8, 0xf7, 0xc8,
|
||||
0x63, 0xfa, 0x34, 0x9e, 0x62, 0x57, 0x84, 0xc4, 0x13, 0x9e, 0x20, 0x85, 0x7a, 0x1a, 0x7f, 0x2c,
|
||||
0x76, 0xc5, 0xa6, 0x58, 0x95, 0x2e, 0xbd, 0x87, 0x4b, 0x0f, 0x9b, 0x82, 0xa6, 0x0b, 0x27, 0xf6,
|
||||
0xf6, 0x2a, 0x6d, 0x48, 0xdd, 0x53, 0xc6, 0x41, 0xce, 0x48, 0xe4, 0x7b, 0x79, 0x41, 0x91, 0x10,
|
||||
0x34, 0x5d, 0x72, 0xcf, 0x1e, 0xb9, 0x8d, 0x92, 0x31, 0xd7, 0x2c, 0x84, 0x05, 0xe0, 0xc9, 0xbf,
|
||||
0x00, 0xe5, 0x9e, 0x42, 0x48, 0x17, 0xb8, 0xdd, 0xdb, 0xb8, 0x58, 0xb3, 0x80, 0x30, 0xae, 0x95,
|
||||
0x96, 0x37, 0xa1, 0xad, 0x1f, 0x2b, 0x68, 0xfd, 0x4d, 0xf9, 0xee, 0x97, 0x01, 0x55, 0xca, 0xfc,
|
||||
0x80, 0x56, 0xf3, 0x97, 0x9c, 0x50, 0x4d, 0x2d, 0x63, 0x60, 0x0c, 0x3b, 0x3b, 0xdb, 0xb8, 0x9a,
|
||||
0xf4, 0x95, 0x31, 0x8e, 0x7c, 0x2f, 0x2f, 0x28, 0x9c, 0xab, 0x71, 0x32, 0xc2, 0xc7, 0xd3, 0x4f,
|
||||
0xe0, 0xea, 0x23, 0xd0, 0x74, 0x6c, 0x9e, 0xa5, 0xfd, 0x46, 0x96, 0xf6, 0x51, 0x55, 0x73, 0xae,
|
||||
0x5c, 0xcd, 0xc7, 0xa8, 0x13, 0x49, 0x91, 0x30, 0xc5, 0x04, 0x07, 0x69, 0x35, 0x07, 0xc6, 0x70,
|
||||
0x6d, 0x7c, 0x6f, 0x8e, 0x74, 0x26, 0x55, 0xcb, 0xa9, 0xeb, 0x4c, 0x0f, 0xa1, 0x88, 0x4a, 0x1a,
|
||||
0x82, 0x06, 0xa9, 0xac, 0xd6, 0xa0, 0x35, 0xec, 0xec, 0xec, 0xe2, 0xa5, 0x21, 0xc0, 0xf5, 0x17,
|
||||
0xe1, 0xc9, 0x15, 0x75, 0xc0, 0xb5, 0x9c, 0x55, 0xb7, 0xab, 0x1a, 0x4e, 0xcd, 0xda, 0xf4, 0xd1,
|
||||
0x86, 0x04, 0x37, 0xa0, 0x2c, 0x9c, 0x88, 0x80, 0xb9, 0x33, 0x6b, 0xa5, 0xb8, 0xe1, 0x41, 0x96,
|
||||
0xf6, 0x37, 0x9c, 0x7a, 0xe3, 0x77, 0xda, 0xdf, 0xae, 0xc5, 0xc7, 0x15, 0x32, 0xcf, 0x0e, 0x9e,
|
||||
0x80, 0x54, 0x4c, 0x69, 0xe0, 0xfa, 0xad, 0x08, 0xe2, 0x10, 0xae, 0x31, 0xce, 0x75, 0x6f, 0x73,
|
||||
0x0f, 0xad, 0x87, 0x22, 0xe6, 0xfa, 0x38, 0xd2, 0x4c, 0x70, 0x65, 0xb5, 0x07, 0xad, 0xe1, 0xda,
|
||||
0xb8, 0x9b, 0xa5, 0xfd, 0xf5, 0xa3, 0x5a, 0xdd, 0xb9, 0xa6, 0x32, 0x0f, 0xd1, 0x26, 0x0d, 0x02,
|
||||
0xf1, 0xb9, 0x3c, 0xe0, 0xe0, 0x4b, 0x44, 0x79, 0x3e, 0x25, 0xeb, 0xbf, 0x81, 0x31, 0x5c, 0x1d,
|
||||
0x5b, 0x59, 0xda, 0xdf, 0x7c, 0xb1, 0xa4, 0xef, 0x2c, 0xa5, 0x7a, 0xcf, 0xd0, 0xff, 0x37, 0x66,
|
||||
0x64, 0x76, 0x51, 0xcb, 0x87, 0x59, 0x11, 0x80, 0x35, 0x27, 0x5f, 0x9a, 0x9b, 0xa8, 0x9d, 0xd0,
|
||||
0x20, 0x86, 0xf2, 0xbf, 0x9c, 0x72, 0xf3, 0xb4, 0xb9, 0x6f, 0x6c, 0x7d, 0x37, 0x50, 0xb7, 0x3e,
|
||||
0xf0, 0x43, 0xa6, 0xb4, 0xf9, 0x7e, 0x21, 0x46, 0xf8, 0x6e, 0x31, 0xca, 0xe9, 0x22, 0x44, 0xdd,
|
||||
0xf9, 0x37, 0xad, 0xfe, 0xad, 0xd4, 0x22, 0xf4, 0x0a, 0xb5, 0x99, 0x86, 0x50, 0x59, 0xcd, 0x22,
|
||||
0x06, 0x0f, 0xee, 0x10, 0x83, 0xf1, 0xc6, 0xdc, 0xaf, 0xfd, 0x3a, 0x27, 0x9d, 0xd2, 0x60, 0x3c,
|
||||
0x3c, 0xbb, 0xb4, 0x1b, 0xe7, 0x97, 0x76, 0xe3, 0xe2, 0xd2, 0x6e, 0x7c, 0xcd, 0x6c, 0xe3, 0x2c,
|
||||
0xb3, 0x8d, 0xf3, 0xcc, 0x36, 0x2e, 0x32, 0xdb, 0xf8, 0x99, 0xd9, 0xc6, 0xb7, 0x5f, 0x76, 0xe3,
|
||||
0x5d, 0x33, 0x19, 0xfd, 0x09, 0x00, 0x00, 0xff, 0xff, 0xbb, 0x57, 0xe7, 0x15, 0xb0, 0x04, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
|
|
@ -59,6 +59,10 @@ message StorageClass {
|
|||
// mount of the PVs will simply fail if one is invalid.
|
||||
// +optional
|
||||
repeated string mountOptions = 5;
|
||||
|
||||
// AllowVolumeExpansion shows whether the storage class allow volume expand
|
||||
// +optional
|
||||
optional bool allowVolumeExpansion = 6;
|
||||
}
|
||||
|
||||
// StorageClassList is a collection of storage classes.
|
||||
|
|
|
@ -55,6 +55,10 @@ type StorageClass struct {
|
|||
// mount of the PVs will simply fail if one is invalid.
|
||||
// +optional
|
||||
MountOptions []string `json:"mountOptions,omitempty" protobuf:"bytes,5,opt,name=mountOptions"`
|
||||
|
||||
// AllowVolumeExpansion shows whether the storage class allow volume expand
|
||||
// +optional
|
||||
AllowVolumeExpansion *bool `json:"allowVolumeExpansion,omitempty" protobuf:"varint,6,opt,name=allowVolumeExpansion"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
|
|
@ -28,12 +28,13 @@ package v1
|
|||
|
||||
// AUTO-GENERATED FUNCTIONS START HERE
|
||||
var map_StorageClass = map[string]string{
|
||||
"": "StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.\n\nStorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.",
|
||||
"metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
|
||||
"provisioner": "Provisioner indicates the type of the provisioner.",
|
||||
"parameters": "Parameters holds the parameters for the provisioner that should create volumes of this storage class.",
|
||||
"reclaimPolicy": "Dynamically provisioned PersistentVolumes of this storage class are created with this reclaimPolicy. Defaults to Delete.",
|
||||
"mountOptions": "Dynamically provisioned PersistentVolumes of this storage class are created with these mountOptions, e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid.",
|
||||
"": "StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.\n\nStorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.",
|
||||
"metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
|
||||
"provisioner": "Provisioner indicates the type of the provisioner.",
|
||||
"parameters": "Parameters holds the parameters for the provisioner that should create volumes of this storage class.",
|
||||
"reclaimPolicy": "Dynamically provisioned PersistentVolumes of this storage class are created with this reclaimPolicy. Defaults to Delete.",
|
||||
"mountOptions": "Dynamically provisioned PersistentVolumes of this storage class are created with these mountOptions, e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid.",
|
||||
"allowVolumeExpansion": "AllowVolumeExpansion shows whether the storage class allow volume expand",
|
||||
}
|
||||
|
||||
func (StorageClass) SwaggerDoc() map[string]string {
|
||||
|
|
|
@ -74,6 +74,15 @@ func (in *StorageClass) DeepCopyInto(out *StorageClass) {
|
|||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AllowVolumeExpansion != nil {
|
||||
in, out := &in.AllowVolumeExpansion, &out.AllowVolumeExpansion
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,16 @@ func (m *StorageClass) MarshalTo(dAtA []byte) (int, error) {
|
|||
i += copy(dAtA[i:], s)
|
||||
}
|
||||
}
|
||||
if m.AllowVolumeExpansion != nil {
|
||||
dAtA[i] = 0x30
|
||||
i++
|
||||
if *m.AllowVolumeExpansion {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
@ -229,6 +239,9 @@ func (m *StorageClass) Size() (n int) {
|
|||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.AllowVolumeExpansion != nil {
|
||||
n += 2
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -279,6 +292,7 @@ func (this *StorageClass) String() string {
|
|||
`Parameters:` + mapStringForParameters + `,`,
|
||||
`ReclaimPolicy:` + valueToStringGenerated(this.ReclaimPolicy) + `,`,
|
||||
`MountOptions:` + fmt.Sprintf("%v", this.MountOptions) + `,`,
|
||||
`AllowVolumeExpansion:` + valueToStringGenerated(this.AllowVolumeExpansion) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
|
@ -565,6 +579,27 @@ func (m *StorageClass) Unmarshal(dAtA []byte) error {
|
|||
}
|
||||
m.MountOptions = append(m.MountOptions, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
case 6:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AllowVolumeExpansion", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
b := bool(v != 0)
|
||||
m.AllowVolumeExpansion = &b
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
|
@ -807,40 +842,42 @@ func init() {
|
|||
}
|
||||
|
||||
var fileDescriptorGenerated = []byte{
|
||||
// 554 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x3f, 0x8f, 0x12, 0x41,
|
||||
0x18, 0xc6, 0xd9, 0x43, 0xe2, 0xdd, 0x00, 0x91, 0xac, 0x16, 0x1b, 0x8a, 0x85, 0x50, 0xd1, 0xdc,
|
||||
0xcc, 0x71, 0x9e, 0x86, 0x98, 0xd8, 0x70, 0xb9, 0xc2, 0x44, 0x72, 0x64, 0x4d, 0x2c, 0x8c, 0x85,
|
||||
0xc3, 0xf2, 0xba, 0x8c, 0xfb, 0x67, 0x36, 0x33, 0xb3, 0x24, 0x74, 0x7e, 0x04, 0xbf, 0x91, 0x2d,
|
||||
0xe5, 0x95, 0x57, 0x11, 0x59, 0x3f, 0x84, 0x89, 0x95, 0xd9, 0x3f, 0xb2, 0x0b, 0x48, 0xbc, 0x6e,
|
||||
0xe6, 0x7d, 0x9f, 0xdf, 0x33, 0x33, 0xef, 0x33, 0xe8, 0xda, 0x1d, 0x4a, 0xcc, 0x38, 0x71, 0xa3,
|
||||
0x29, 0x88, 0x00, 0x14, 0x48, 0xb2, 0x80, 0x60, 0xc6, 0x05, 0xc9, 0x1b, 0x34, 0x64, 0x44, 0x2a,
|
||||
0x2e, 0xa8, 0x03, 0x64, 0x31, 0x98, 0x82, 0xa2, 0x03, 0xe2, 0x40, 0x00, 0x82, 0x2a, 0x98, 0xe1,
|
||||
0x50, 0x70, 0xc5, 0xf5, 0x76, 0xa6, 0xc5, 0x34, 0x64, 0x38, 0xd7, 0xe2, 0x5c, 0xdb, 0x3e, 0x77,
|
||||
0x98, 0x9a, 0x47, 0x53, 0x6c, 0x73, 0x9f, 0x38, 0xdc, 0xe1, 0x24, 0x45, 0xa6, 0xd1, 0xe7, 0x74,
|
||||
0x97, 0x6e, 0xd2, 0x55, 0x66, 0xd5, 0xee, 0x95, 0x8e, 0xb5, 0xb9, 0x48, 0xce, 0xdc, 0x3f, 0xae,
|
||||
0x7d, 0x55, 0x68, 0x7c, 0x6a, 0xcf, 0x59, 0x00, 0x62, 0x49, 0x42, 0xd7, 0x49, 0x0a, 0x92, 0xf8,
|
||||
0xa0, 0xe8, 0xbf, 0x28, 0x72, 0x8c, 0x12, 0x51, 0xa0, 0x98, 0x0f, 0x07, 0xc0, 0xcb, 0xff, 0x01,
|
||||
0xd2, 0x9e, 0x83, 0x4f, 0x0f, 0xb8, 0xe7, 0xc7, 0xb8, 0x48, 0x31, 0x8f, 0xb0, 0x40, 0x49, 0x25,
|
||||
0xf6, 0xa1, 0xde, 0xaf, 0x2a, 0x6a, 0xbc, 0xcb, 0x46, 0x77, 0xed, 0x51, 0x29, 0xf5, 0x4f, 0xe8,
|
||||
0x34, 0x79, 0xc9, 0x8c, 0x2a, 0x6a, 0x68, 0x5d, 0xad, 0x5f, 0xbf, 0xbc, 0xc0, 0xc5, 0x98, 0xb7,
|
||||
0xc6, 0x38, 0x74, 0x9d, 0xa4, 0x20, 0x71, 0xa2, 0xc6, 0x8b, 0x01, 0xbe, 0x9d, 0x7e, 0x01, 0x5b,
|
||||
0x8d, 0x41, 0xd1, 0x91, 0xbe, 0x5a, 0x77, 0x2a, 0xf1, 0xba, 0x83, 0x8a, 0x9a, 0xb5, 0x75, 0xd5,
|
||||
0x5f, 0xa0, 0x7a, 0x28, 0xf8, 0x82, 0x49, 0xc6, 0x03, 0x10, 0xc6, 0x49, 0x57, 0xeb, 0x9f, 0x8d,
|
||||
0x9e, 0xe6, 0x48, 0x7d, 0x52, 0xb4, 0xac, 0xb2, 0x4e, 0xf7, 0x10, 0x0a, 0xa9, 0xa0, 0x3e, 0x28,
|
||||
0x10, 0xd2, 0xa8, 0x76, 0xab, 0xfd, 0xfa, 0xe5, 0x10, 0x1f, 0xff, 0x01, 0xb8, 0xfc, 0x2c, 0x3c,
|
||||
0xd9, 0xa2, 0x37, 0x81, 0x12, 0xcb, 0xe2, 0x8a, 0x45, 0xc3, 0x2a, 0xf9, 0xeb, 0x2e, 0x6a, 0x0a,
|
||||
0xb0, 0x3d, 0xca, 0xfc, 0x09, 0xf7, 0x98, 0xbd, 0x34, 0x1e, 0xa5, 0xd7, 0xbc, 0x89, 0xd7, 0x9d,
|
||||
0xa6, 0x55, 0x6e, 0xfc, 0x5e, 0x77, 0x2e, 0x0e, 0xff, 0x0e, 0x9e, 0x80, 0x90, 0x4c, 0x2a, 0x08,
|
||||
0xd4, 0x7b, 0xee, 0x45, 0x3e, 0xec, 0x30, 0xd6, 0xae, 0xb7, 0x7e, 0x85, 0x1a, 0x3e, 0x8f, 0x02,
|
||||
0x75, 0x1b, 0x2a, 0xc6, 0x03, 0x69, 0xd4, 0xba, 0xd5, 0xfe, 0xd9, 0xa8, 0x15, 0xaf, 0x3b, 0x8d,
|
||||
0x71, 0xa9, 0x6e, 0xed, 0xa8, 0xda, 0xaf, 0xd1, 0x93, 0xbd, 0x57, 0xe9, 0x2d, 0x54, 0x75, 0x61,
|
||||
0x99, 0xe6, 0x76, 0x66, 0x25, 0x4b, 0xfd, 0x19, 0xaa, 0x2d, 0xa8, 0x17, 0x41, 0x36, 0x66, 0x2b,
|
||||
0xdb, 0xbc, 0x3a, 0x19, 0x6a, 0xbd, 0xef, 0x1a, 0x6a, 0x95, 0x47, 0xf4, 0x96, 0x49, 0xa5, 0x7f,
|
||||
0x3c, 0x48, 0x1f, 0x3f, 0x2c, 0xfd, 0x84, 0x4e, 0xb3, 0x6f, 0xe5, 0x83, 0x3d, 0xfd, 0x5b, 0x29,
|
||||
0x25, 0x3f, 0x46, 0x35, 0xa6, 0xc0, 0x97, 0xc6, 0x49, 0x9a, 0x5e, 0xff, 0xa1, 0xe9, 0x8d, 0x9a,
|
||||
0xb9, 0x69, 0xed, 0x4d, 0x82, 0x5b, 0x99, 0xcb, 0xe8, 0x7c, 0xb5, 0x31, 0x2b, 0x77, 0x1b, 0xb3,
|
||||
0x72, 0xbf, 0x31, 0x2b, 0x5f, 0x63, 0x53, 0x5b, 0xc5, 0xa6, 0x76, 0x17, 0x9b, 0xda, 0x7d, 0x6c,
|
||||
0x6a, 0x3f, 0x62, 0x53, 0xfb, 0xf6, 0xd3, 0xac, 0x7c, 0x78, 0x9c, 0x3b, 0xfe, 0x09, 0x00, 0x00,
|
||||
0xff, 0xff, 0x2a, 0xfd, 0x7a, 0x0a, 0x73, 0x04, 0x00, 0x00,
|
||||
// 589 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x4f, 0xd4, 0x40,
|
||||
0x18, 0xc6, 0xb7, 0x2c, 0xab, 0x30, 0x0b, 0x71, 0x53, 0x39, 0x34, 0x7b, 0xe8, 0x6e, 0x38, 0xf5,
|
||||
0xc2, 0x0c, 0x20, 0x1a, 0x62, 0xe2, 0xc1, 0x12, 0x0e, 0x26, 0x10, 0x36, 0x35, 0xf1, 0x60, 0x3c,
|
||||
0x38, 0x5b, 0x5e, 0xcb, 0xd8, 0x76, 0xa6, 0x99, 0x99, 0xae, 0xee, 0xcd, 0x8f, 0xe0, 0x37, 0xf2,
|
||||
0x64, 0xc2, 0x91, 0x23, 0xa7, 0x46, 0xea, 0xb7, 0xf0, 0x64, 0xfa, 0x47, 0x5a, 0x58, 0x88, 0xdc,
|
||||
0x3a, 0xef, 0xfb, 0xfc, 0x9e, 0xb7, 0xf3, 0xce, 0x83, 0x0e, 0xc2, 0x7d, 0x85, 0x99, 0x20, 0x61,
|
||||
0x3a, 0x05, 0xc9, 0x41, 0x83, 0x22, 0x33, 0xe0, 0xa7, 0x42, 0x92, 0xba, 0x41, 0x13, 0x46, 0x94,
|
||||
0x16, 0x92, 0x06, 0x40, 0x66, 0x3b, 0x53, 0xd0, 0x74, 0x87, 0x04, 0xc0, 0x41, 0x52, 0x0d, 0xa7,
|
||||
0x38, 0x91, 0x42, 0x0b, 0x73, 0x58, 0x69, 0x31, 0x4d, 0x18, 0xae, 0xb5, 0xb8, 0xd6, 0x0e, 0xb7,
|
||||
0x02, 0xa6, 0xcf, 0xd2, 0x29, 0xf6, 0x45, 0x4c, 0x02, 0x11, 0x08, 0x52, 0x22, 0xd3, 0xf4, 0x53,
|
||||
0x79, 0x2a, 0x0f, 0xe5, 0x57, 0x65, 0x35, 0xdc, 0x6c, 0x8d, 0xf5, 0x85, 0x2c, 0x66, 0xde, 0x1e,
|
||||
0x37, 0xdc, 0x6b, 0x34, 0x31, 0xf5, 0xcf, 0x18, 0x07, 0x39, 0x27, 0x49, 0x18, 0x14, 0x05, 0x45,
|
||||
0x62, 0xd0, 0xf4, 0x2e, 0x8a, 0xdc, 0x47, 0xc9, 0x94, 0x6b, 0x16, 0xc3, 0x02, 0xf0, 0xe2, 0x7f,
|
||||
0x80, 0xf2, 0xcf, 0x20, 0xa6, 0x0b, 0xdc, 0xb3, 0xfb, 0xb8, 0x54, 0xb3, 0x88, 0x30, 0xae, 0x95,
|
||||
0x96, 0xb7, 0xa1, 0xcd, 0x9f, 0xcb, 0x68, 0xed, 0x6d, 0xb5, 0xba, 0x83, 0x88, 0x2a, 0x65, 0x7e,
|
||||
0x44, 0x2b, 0xc5, 0x4d, 0x4e, 0xa9, 0xa6, 0x96, 0x31, 0x36, 0x9c, 0xfe, 0xee, 0x36, 0x6e, 0xd6,
|
||||
0x7c, 0x6d, 0x8c, 0x93, 0x30, 0x28, 0x0a, 0x0a, 0x17, 0x6a, 0x3c, 0xdb, 0xc1, 0x27, 0xd3, 0xcf,
|
||||
0xe0, 0xeb, 0x63, 0xd0, 0xd4, 0x35, 0xcf, 0xb3, 0x51, 0x27, 0xcf, 0x46, 0xa8, 0xa9, 0x79, 0xd7,
|
||||
0xae, 0xe6, 0x73, 0xd4, 0x4f, 0xa4, 0x98, 0x31, 0xc5, 0x04, 0x07, 0x69, 0x2d, 0x8d, 0x0d, 0x67,
|
||||
0xd5, 0x7d, 0x5a, 0x23, 0xfd, 0x49, 0xd3, 0xf2, 0xda, 0x3a, 0x33, 0x42, 0x28, 0xa1, 0x92, 0xc6,
|
||||
0xa0, 0x41, 0x2a, 0xab, 0x3b, 0xee, 0x3a, 0xfd, 0xdd, 0x7d, 0x7c, 0x7f, 0x02, 0x70, 0xfb, 0x5a,
|
||||
0x78, 0x72, 0x8d, 0x1e, 0x72, 0x2d, 0xe7, 0xcd, 0x2f, 0x36, 0x0d, 0xaf, 0xe5, 0x6f, 0x86, 0x68,
|
||||
0x5d, 0x82, 0x1f, 0x51, 0x16, 0x4f, 0x44, 0xc4, 0xfc, 0xb9, 0xb5, 0x5c, 0xfe, 0xe6, 0x61, 0x9e,
|
||||
0x8d, 0xd6, 0xbd, 0x76, 0xe3, 0x4f, 0x36, 0xda, 0x5e, 0xcc, 0x0e, 0x9e, 0x80, 0x54, 0x4c, 0x69,
|
||||
0xe0, 0xfa, 0x9d, 0x88, 0xd2, 0x18, 0x6e, 0x30, 0xde, 0x4d, 0x6f, 0x73, 0x0f, 0xad, 0xc5, 0x22,
|
||||
0xe5, 0xfa, 0x24, 0xd1, 0x4c, 0x70, 0x65, 0xf5, 0xc6, 0x5d, 0x67, 0xd5, 0x1d, 0xe4, 0xd9, 0x68,
|
||||
0xed, 0xb8, 0x55, 0xf7, 0x6e, 0xa8, 0xcc, 0x23, 0xb4, 0x41, 0xa3, 0x48, 0x7c, 0xa9, 0x06, 0x1c,
|
||||
0x7e, 0x4d, 0x28, 0x2f, 0x56, 0x65, 0x3d, 0x1a, 0x1b, 0xce, 0x8a, 0x6b, 0xe5, 0xd9, 0x68, 0xe3,
|
||||
0xf5, 0x1d, 0x7d, 0xef, 0x4e, 0x6a, 0xf8, 0x0a, 0x3d, 0xb9, 0xb5, 0x23, 0x73, 0x80, 0xba, 0x21,
|
||||
0xcc, 0xcb, 0x14, 0xac, 0x7a, 0xc5, 0xa7, 0xb9, 0x81, 0x7a, 0x33, 0x1a, 0xa5, 0x50, 0x3d, 0x9a,
|
||||
0x57, 0x1d, 0x5e, 0x2e, 0xed, 0x1b, 0x9b, 0x3f, 0x0c, 0x34, 0x68, 0x2f, 0xfc, 0x88, 0x29, 0x6d,
|
||||
0x7e, 0x58, 0xc8, 0x12, 0x7e, 0x58, 0x96, 0x0a, 0xba, 0x4c, 0xd2, 0xa0, 0x7e, 0xa6, 0x95, 0x7f,
|
||||
0x95, 0x56, 0x8e, 0x8e, 0x51, 0x8f, 0x69, 0x88, 0x95, 0xb5, 0x54, 0x66, 0xc1, 0x79, 0x68, 0x16,
|
||||
0xdc, 0xf5, 0xda, 0xb4, 0xf7, 0xa6, 0xc0, 0xbd, 0xca, 0xc5, 0xdd, 0x3a, 0xbf, 0xb2, 0x3b, 0x17,
|
||||
0x57, 0x76, 0xe7, 0xf2, 0xca, 0xee, 0x7c, 0xcb, 0x6d, 0xe3, 0x3c, 0xb7, 0x8d, 0x8b, 0xdc, 0x36,
|
||||
0x2e, 0x73, 0xdb, 0xf8, 0x95, 0xdb, 0xc6, 0xf7, 0xdf, 0x76, 0xe7, 0xfd, 0xe3, 0xda, 0xf1, 0x6f,
|
||||
0x00, 0x00, 0x00, 0xff, 0xff, 0x1b, 0xae, 0x44, 0x72, 0xc1, 0x04, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -59,6 +59,10 @@ message StorageClass {
|
|||
// mount of the PVs will simply fail if one is invalid.
|
||||
// +optional
|
||||
repeated string mountOptions = 5;
|
||||
|
||||
// AllowVolumeExpansion shows whether the storage class allow volume expand
|
||||
// +optional
|
||||
optional bool allowVolumeExpansion = 6;
|
||||
}
|
||||
|
||||
// StorageClassList is a collection of storage classes.
|
||||
|
|
|
@ -55,6 +55,10 @@ type StorageClass struct {
|
|||
// mount of the PVs will simply fail if one is invalid.
|
||||
// +optional
|
||||
MountOptions []string `json:"mountOptions,omitempty" protobuf:"bytes,5,opt,name=mountOptions"`
|
||||
|
||||
// AllowVolumeExpansion shows whether the storage class allow volume expand
|
||||
// +optional
|
||||
AllowVolumeExpansion *bool `json:"allowVolumeExpansion,omitempty" protobuf:"varint,6,opt,name=allowVolumeExpansion"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
|
|
@ -28,12 +28,13 @@ package v1beta1
|
|||
|
||||
// AUTO-GENERATED FUNCTIONS START HERE
|
||||
var map_StorageClass = map[string]string{
|
||||
"": "StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.\n\nStorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.",
|
||||
"metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
|
||||
"provisioner": "Provisioner indicates the type of the provisioner.",
|
||||
"parameters": "Parameters holds the parameters for the provisioner that should create volumes of this storage class.",
|
||||
"reclaimPolicy": "Dynamically provisioned PersistentVolumes of this storage class are created with this reclaimPolicy. Defaults to Delete.",
|
||||
"mountOptions": "Dynamically provisioned PersistentVolumes of this storage class are created with these mountOptions, e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid.",
|
||||
"": "StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.\n\nStorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.",
|
||||
"metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
|
||||
"provisioner": "Provisioner indicates the type of the provisioner.",
|
||||
"parameters": "Parameters holds the parameters for the provisioner that should create volumes of this storage class.",
|
||||
"reclaimPolicy": "Dynamically provisioned PersistentVolumes of this storage class are created with this reclaimPolicy. Defaults to Delete.",
|
||||
"mountOptions": "Dynamically provisioned PersistentVolumes of this storage class are created with these mountOptions, e.g. [\"ro\", \"soft\"]. Not validated - mount of the PVs will simply fail if one is invalid.",
|
||||
"allowVolumeExpansion": "AllowVolumeExpansion shows whether the storage class allow volume expand",
|
||||
}
|
||||
|
||||
func (StorageClass) SwaggerDoc() map[string]string {
|
||||
|
|
|
@ -74,6 +74,15 @@ func (in *StorageClass) DeepCopyInto(out *StorageClass) {
|
|||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AllowVolumeExpansion != nil {
|
||||
in, out := &in.AllowVolumeExpansion, &out.AllowVolumeExpansion
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue