Merge pull request #69929 from jsafrane/csi-ga

Promote CSIPersistentVolume feature to GA
This commit is contained in:
k8s-ci-robot
2018-11-14 20:34:58 -08:00
committed by GitHub
46 changed files with 7578 additions and 521 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -375,6 +375,12 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<li>
<p><a href="#_v1_storageclasslist">v1.StorageClassList</a></p>
</li>
<li>
<p><a href="#_v1_volumeattachment">v1.VolumeAttachment</a></p>
</li>
<li>
<p><a href="#_v1_volumeattachmentlist">v1.VolumeAttachmentList</a></p>
</li>
</ul>
</div>
</div>
@@ -438,15 +444,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div class="sect2">
<h3 id="_v1_patch">v1.Patch</h3>
<h3 id="_v1_volumeattachmentlist">v1.VolumeAttachmentList</h3>
<div class="paragraph">
<p>Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.</p>
</div>
</div>
<div class="sect2">
<h3 id="_v1_deleteoptions">v1.DeleteOptions</h3>
<div class="paragraph">
<p>DeleteOptions may be provided when deleting an API object.</p>
<p>VolumeAttachmentList is a collection of VolumeAttachment objects.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
@@ -481,38 +481,17 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">gracePeriodSeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">metadata</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Standard list metadata More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata">https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata</a></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">integer (int64)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_listmeta">v1.ListMeta</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">preconditions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.</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_preconditions">v1.Preconditions</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">orphanDependents</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object&#8217;s finalizers list. Either this field or PropagationPolicy may be set, but not both.</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>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">propagationPolicy</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: <em>Orphan</em> - orphan the dependents; <em>Background</em> - allow the garbage collector to delete the dependents in the background; <em>Foreground</em> - a cascading policy that deletes all dependents in the foreground.</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_deletionpropagation">v1.DeletionPropagation</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">dryRun</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed</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 array</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">items</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Items is the list of VolumeAttachments</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"><a href="#_v1_volumeattachment">v1.VolumeAttachment</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
@@ -568,9 +547,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div class="sect2">
<h3 id="_v1_topologyselectorlabelrequirement">v1.TopologySelectorLabelRequirement</h3>
<h3 id="_v1_volumeattachmentsource">v1.VolumeAttachmentSource</h3>
<div class="paragraph">
<p>A topology selector requirement is a selector that matches given label. This is an alpha feature and may change in the future.</p>
<p>VolumeAttachmentSource represents a volume that should be attached. Right now only PersistenVolumes can be attached via external attacher, in future we may allow also inline volumes in pods. Exactly one member can be set.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
@@ -591,122 +570,12 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">key</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The label key that the selector applies to.</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">values</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">An array of string values. One value must match the label to be selected. Each entry in Values is ORed.</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 array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_statusdetails">v1.StatusDetails</h3>
<div class="paragraph">
<p>StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.</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">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">persistentVolumeName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the persistent volume to attach.</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">group</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The group attribute of the resource associated with the status StatusReason.</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">kind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></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">uid</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">UID of the resource. (when there is a single resource which can be described). More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#uids">http://kubernetes.io/docs/user-guide/identifiers#uids</a></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">causes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.</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_statuscause">v1.StatusCause</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">retryAfterSeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.</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">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_preconditions">v1.Preconditions</h3>
<div class="paragraph">
<p>Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.</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">uid</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Specifies the target UID.</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="#_types_uid">types.UID</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@@ -753,9 +622,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div class="sect2">
<h3 id="_v1_initializer">v1.Initializer</h3>
<h3 id="_v1_preconditions">v1.Preconditions</h3>
<div class="paragraph">
<p>Initializer is information about an initializer that has not yet completed.</p>
<p>Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
@@ -776,10 +645,10 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">name of the process that is responsible for initializing this object.</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"><p class="tableblock">uid</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Specifies the target UID.</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="#_types_uid">types.UID</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
@@ -868,6 +737,47 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_volumeerror">v1.VolumeError</h3>
<div class="paragraph">
<p>VolumeError captures an error encountered during a volume operation.</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">time</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Time the error was encountered.</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">String detailing the error encountered during Attach or Detach operation. This string maybe logged, so it should not contain sensitive information.</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_watchevent">v1.WatchEvent</h3>
@@ -906,6 +816,387 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_volumeattachment">v1.VolumeAttachment</h3>
<div class="paragraph">
<p>VolumeAttachment captures the intent to attach or detach the specified volume to/from the specified node.</p>
</div>
<div class="paragraph">
<p>VolumeAttachment objects are non-namespaced.</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">kind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></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">apiVersion</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#resources">https://git.k8s.io/community/contributors/devel/api-conventions.md#resources</a></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">metadata</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Standard object metadata. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata">https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata</a></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_objectmeta">v1.ObjectMeta</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">spec</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Specification of the desired attach/detach volume behavior. Populated by the Kubernetes system.</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"><a href="#_v1_volumeattachmentspec">v1.VolumeAttachmentSpec</a></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"><p class="tableblock">Status of the VolumeAttachment request. Populated by the entity completing the attach or detach operation, i.e. the external-attacher.</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_volumeattachmentstatus">v1.VolumeAttachmentStatus</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_persistentvolumereclaimpolicy">v1.PersistentVolumeReclaimPolicy</h3>
</div>
<div class="sect2">
<h3 id="_v1_topologyselectorterm">v1.TopologySelectorTerm</h3>
<div class="paragraph">
<p>A topology selector term represents the result of label queries. A null or empty topology selector term matches no objects. The requirements of them are ANDed. It provides a subset of functionality as NodeSelectorTerm. This is an alpha feature and may change in the future.</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">matchLabelExpressions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">A list of topology selector requirements by labels.</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_topologyselectorlabelrequirement">v1.TopologySelectorLabelRequirement</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_volumeattachmentspec">v1.VolumeAttachmentSpec</h3>
<div class="paragraph">
<p>VolumeAttachmentSpec is the specification of a VolumeAttachment request.</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">attacher</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Attacher indicates the name of the volume driver that MUST handle this request. This is the name returned by GetPluginName().</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">source</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Source represents the volume that should be attached.</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"><a href="#_v1_volumeattachmentsource">v1.VolumeAttachmentSource</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">nodeName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The node that the volume should be attached to.</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>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_deletionpropagation">v1.DeletionPropagation</h3>
</div>
<div class="sect2">
<h3 id="_v1_patch">v1.Patch</h3>
<div class="paragraph">
<p>Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.</p>
</div>
</div>
<div class="sect2">
<h3 id="_v1_deleteoptions">v1.DeleteOptions</h3>
<div class="paragraph">
<p>DeleteOptions may be provided when deleting an API object.</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">kind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></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">apiVersion</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#resources">https://git.k8s.io/community/contributors/devel/api-conventions.md#resources</a></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">gracePeriodSeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.</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">integer (int64)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">preconditions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.</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_preconditions">v1.Preconditions</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">orphanDependents</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object&#8217;s finalizers list. Either this field or PropagationPolicy may be set, but not both.</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>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">propagationPolicy</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: <em>Orphan</em> - orphan the dependents; <em>Background</em> - allow the garbage collector to delete the dependents in the background; <em>Foreground</em> - a cascading policy that deletes all dependents in the foreground.</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_deletionpropagation">v1.DeletionPropagation</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">dryRun</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed</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 array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_statusdetails">v1.StatusDetails</h3>
<div class="paragraph">
<p>StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.</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">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).</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">group</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The group attribute of the resource associated with the status StatusReason.</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">kind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></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">uid</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">UID of the resource. (when there is a single resource which can be described). More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#uids">http://kubernetes.io/docs/user-guide/identifiers#uids</a></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">causes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.</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_statuscause">v1.StatusCause</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">retryAfterSeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.</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">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_topologyselectorlabelrequirement">v1.TopologySelectorLabelRequirement</h3>
<div class="paragraph">
<p>A topology selector requirement is a selector that matches given label. This is an alpha feature and may change in the future.</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">key</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The label key that the selector applies to.</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">values</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">An array of string values. One value must match the label to be selected. Each entry in Values is ORed.</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 array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_initializer">v1.Initializer</h3>
<div class="paragraph">
<p>Initializer is information about an initializer that has not yet completed.</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">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">name of the process that is responsible for initializing this object.</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>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_storageclasslist">v1.StorageClassList</h3>
@@ -963,12 +1254,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div class="sect2">
<h3 id="_v1_storageclass">v1.StorageClass</h3>
<h3 id="_v1_ownerreference">v1.OwnerReference</h3>
<div class="paragraph">
<p>StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.</p>
</div>
<div class="paragraph">
<p>StorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.</p>
<p>OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
@@ -989,74 +1277,46 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">kind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></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">apiVersion</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#resources">https://git.k8s.io/community/contributors/devel/api-conventions.md#resources</a></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">metadata</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Standard object&#8217;s metadata. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata">https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata</a></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_objectmeta">v1.ObjectMeta</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">provisioner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provisioner indicates the type of the provisioner.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">API version of the referent.</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">parameters</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Parameters holds the parameters for the provisioner that should create volumes of this storage class.</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">object</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">kind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Kind of the referent. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></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">reclaimPolicy</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Dynamically provisioned PersistentVolumes of this storage class are created with this reclaimPolicy. Defaults to Delete.</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_persistentvolumereclaimpolicy">v1.PersistentVolumeReclaimPolicy</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the referent. More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#names">http://kubernetes.io/docs/user-guide/identifiers#names</a></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">mountOptions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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.</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 array</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">uid</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">UID of the referent. More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#uids">http://kubernetes.io/docs/user-guide/identifiers#uids</a></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">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">controller</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If true, this reference points to the managing controller.</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>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeBindingMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeBindingMode indicates how PersistentVolumeClaims should be provisioned and bound. When unset, VolumeBindingImmediate is used. This field is only honored by servers that enable the VolumeScheduling feature.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">blockOwnerDeletion</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If true, AND if the owner has the "foregroundDeletion" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs "delete" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.</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_volumebindingmode">v1.VolumeBindingMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">allowedTopologies</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Restrict the node topologies where volumes can be dynamically provisioned. Each volume plugin defines its own supported topology specifications. An empty TopologySelectorTerm list means there is no topology restriction. This field is only honored by servers that enable the VolumeScheduling feature.</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>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_topologyselectorterm">v1.TopologySelectorTerm</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@@ -1218,9 +1478,12 @@ When an object is created, the system will populate this list with the current s
</div>
<div class="sect2">
<h3 id="_v1_ownerreference">v1.OwnerReference</h3>
<h3 id="_v1_storageclass">v1.StorageClass</h3>
<div class="paragraph">
<p>OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.</p>
<p>StorageClass describes the parameters for a class of storage for which PersistentVolumes can be dynamically provisioned.</p>
</div>
<div class="paragraph">
<p>StorageClasses are non-namespaced; the name of the storage class according to etcd is in ObjectMeta.Name.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
@@ -1241,88 +1504,78 @@ When an object is created, the system will populate this list with the current s
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">apiVersion</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">API version of the referent.</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">kind</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Kind of the referent. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds">https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds</a></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">apiVersion</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#resources">https://git.k8s.io/community/contributors/devel/api-conventions.md#resources</a></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">metadata</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Standard object&#8217;s metadata. More info: <a href="https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata">https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata</a></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_objectmeta">v1.ObjectMeta</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">provisioner</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Provisioner indicates the type of the provisioner.</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">name</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Name of the referent. More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#names">http://kubernetes.io/docs/user-guide/identifiers#names</a></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"><p class="tableblock">parameters</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Parameters holds the parameters for the provisioner that should create volumes of this storage class.</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">object</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">uid</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">UID of the referent. More info: <a href="http://kubernetes.io/docs/user-guide/identifiers#uids">http://kubernetes.io/docs/user-guide/identifiers#uids</a></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"><p class="tableblock">reclaimPolicy</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Dynamically provisioned PersistentVolumes of this storage class are created with this reclaimPolicy. Defaults to Delete.</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_persistentvolumereclaimpolicy">v1.PersistentVolumeReclaimPolicy</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">controller</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If true, this reference points to the managing controller.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">mountOptions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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.</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 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>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">blockOwnerDeletion</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">If true, AND if the owner has the "foregroundDeletion" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs "delete" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.</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">volumeBindingMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">VolumeBindingMode indicates how PersistentVolumeClaims should be provisioned and bound. When unset, VolumeBindingImmediate is used. This field is only honored by servers that enable the VolumeScheduling feature.</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_volumebindingmode">v1.VolumeBindingMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_topologyselectorterm">v1.TopologySelectorTerm</h3>
<div class="paragraph">
<p>A topology selector term represents the result of label queries. A null or empty topology selector term matches no objects. The requirements of them are ANDed. It provides a subset of functionality as NodeSelectorTerm. This is an alpha feature and may change in the future.</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">matchLabelExpressions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">A list of topology selector requirements by labels.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">allowedTopologies</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Restrict the node topologies where volumes can be dynamically provisioned. Each volume plugin defines its own supported topology specifications. An empty TopologySelectorTerm list means there is no topology restriction. This field is only honored by servers that enable the VolumeScheduling feature.</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_topologyselectorlabelrequirement">v1.TopologySelectorLabelRequirement</a> array</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_topologyselectorterm">v1.TopologySelectorTerm</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_persistentvolumereclaimpolicy">v1.PersistentVolumeReclaimPolicy</h3>
</div>
<div class="sect2">
<h3 id="_v1_apiresource">v1.APIResource</h3>
@@ -1413,6 +1666,61 @@ When an object is created, the system will populate this list with the current s
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_volumeattachmentstatus">v1.VolumeAttachmentStatus</h3>
<div class="paragraph">
<p>VolumeAttachmentStatus is the status of a VolumeAttachment request.</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">attached</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Indicates the volume is successfully attached. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.</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">boolean</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">attachmentMetadata</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Upon successful attach, this field is populated with any information returned by the attach operation that must be passed into subsequent WaitForAttach or Mount calls. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.</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">object</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">attachError</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The last error encountered during attach operation, if any. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.</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_volumeerror">v1.VolumeError</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">detachError</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The last error encountered during detach operation, if any. This field must only be set by the entity completing the detach operation, i.e. the external-attacher.</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_volumeerror">v1.VolumeError</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_types_uid">types.UID</h3>
@@ -1469,10 +1777,6 @@ Examples:<br>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_deletionpropagation">v1.DeletionPropagation</h3>
</div>
<div class="sect2">
<h3 id="_v1_volumebindingmode">v1.VolumeBindingMode</h3>

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,6 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
@@ -28,3 +25,16 @@ filegroup(
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["util_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
],
)

View File

@@ -22,10 +22,22 @@ import (
"k8s.io/kubernetes/pkg/features"
)
// DropDisabledAlphaFields removes disabled fields from the pv spec.
// DropDisabledFields removes disabled fields from the pv spec.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pv spec.
func DropDisabledAlphaFields(pvSpec *api.PersistentVolumeSpec) {
func DropDisabledFields(pvSpec *api.PersistentVolumeSpec, oldPVSpec *api.PersistentVolumeSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
// TODO(liggitt): change this to only drop pvSpec.VolumeMode if (oldPVSpec == nil || oldPVSpec.VolumeMode == nil)
// Requires more coordinated changes to validation
pvSpec.VolumeMode = nil
if oldPVSpec != nil {
oldPVSpec.VolumeMode = nil
}
}
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIPersistentVolume) {
// if this is a new PV, or the old PV didn't already have the CSI field, clear it
if oldPVSpec == nil || oldPVSpec.PersistentVolumeSource.CSI == nil {
pvSpec.PersistentVolumeSource.CSI = nil
}
}
}

View File

@@ -0,0 +1,155 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package persistentvolume
import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/util/diff"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
)
func TestDropDisabledFields(t *testing.T) {
specWithCSI := func() *api.PersistentVolumeSpec {
return &api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{CSI: &api.CSIPersistentVolumeSource{}}}
}
specWithoutCSI := func() *api.PersistentVolumeSpec {
return &api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{CSI: nil}}
}
specWithMode := func(mode *api.PersistentVolumeMode) *api.PersistentVolumeSpec {
return &api.PersistentVolumeSpec{VolumeMode: mode}
}
modeBlock := api.PersistentVolumeBlock
tests := map[string]struct {
oldSpec *api.PersistentVolumeSpec
newSpec *api.PersistentVolumeSpec
expectOldSpec *api.PersistentVolumeSpec
expectNewSpec *api.PersistentVolumeSpec
csiEnabled bool
blockEnabled bool
}{
"disabled csi clears new": {
csiEnabled: false,
newSpec: specWithCSI(),
expectNewSpec: specWithoutCSI(),
oldSpec: nil,
expectOldSpec: nil,
},
"disabled csi clears update when old pv did not use csi": {
csiEnabled: false,
newSpec: specWithCSI(),
expectNewSpec: specWithoutCSI(),
oldSpec: specWithoutCSI(),
expectOldSpec: specWithoutCSI(),
},
"disabled csi preserves update when old pv did use csi": {
csiEnabled: false,
newSpec: specWithCSI(),
expectNewSpec: specWithCSI(),
oldSpec: specWithCSI(),
expectOldSpec: specWithCSI(),
},
"enabled csi preserves new": {
csiEnabled: true,
newSpec: specWithCSI(),
expectNewSpec: specWithCSI(),
oldSpec: nil,
expectOldSpec: nil,
},
"enabled csi preserves update when old pv did not use csi": {
csiEnabled: true,
newSpec: specWithCSI(),
expectNewSpec: specWithCSI(),
oldSpec: specWithoutCSI(),
expectOldSpec: specWithoutCSI(),
},
"enabled csi preserves update when old pv did use csi": {
csiEnabled: true,
newSpec: specWithCSI(),
expectNewSpec: specWithCSI(),
oldSpec: specWithCSI(),
expectOldSpec: specWithCSI(),
},
"disabled block clears new": {
blockEnabled: false,
newSpec: specWithMode(&modeBlock),
expectNewSpec: specWithMode(nil),
oldSpec: nil,
expectOldSpec: nil,
},
"disabled block clears update when old pv did not use block": {
blockEnabled: false,
newSpec: specWithMode(&modeBlock),
expectNewSpec: specWithMode(nil),
oldSpec: specWithMode(nil),
expectOldSpec: specWithMode(nil),
},
// TODO: consider changing this case to preserve
"disabled block clears old and new on update when old pv did use block": {
blockEnabled: false,
newSpec: specWithMode(&modeBlock),
expectNewSpec: specWithMode(nil),
oldSpec: specWithMode(&modeBlock),
expectOldSpec: specWithMode(nil),
},
"enabled block preserves new": {
blockEnabled: true,
newSpec: specWithMode(&modeBlock),
expectNewSpec: specWithMode(&modeBlock),
oldSpec: nil,
expectOldSpec: nil,
},
"enabled block preserves update when old pv did not use block": {
blockEnabled: true,
newSpec: specWithMode(&modeBlock),
expectNewSpec: specWithMode(&modeBlock),
oldSpec: specWithMode(nil),
expectOldSpec: specWithMode(nil),
},
"enabled block preserves update when old pv did use block": {
blockEnabled: true,
newSpec: specWithMode(&modeBlock),
expectNewSpec: specWithMode(&modeBlock),
oldSpec: specWithMode(&modeBlock),
expectOldSpec: specWithMode(&modeBlock),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIPersistentVolume, tc.csiEnabled)()
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, tc.blockEnabled)()
DropDisabledFields(tc.newSpec, tc.oldSpec)
if !reflect.DeepEqual(tc.newSpec, tc.expectNewSpec) {
t.Error(diff.ObjectReflectDiff(tc.newSpec, tc.expectNewSpec))
}
if !reflect.DeepEqual(tc.oldSpec, tc.expectOldSpec) {
t.Error(diff.ObjectReflectDiff(tc.oldSpec, tc.expectOldSpec))
}
})
}
}

View File

@@ -5,11 +5,7 @@ go_library(
srcs = ["util.go"],
importpath = "k8s.io/kubernetes/pkg/api/v1/persistentvolume",
visibility = ["//visibility:public"],
deps = [
"//pkg/features:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
deps = ["//staging/src/k8s.io/api/core/v1:go_default_library"],
)
go_test(
@@ -18,11 +14,9 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)

View File

@@ -18,8 +18,6 @@ package persistentvolume
import (
corev1 "k8s.io/api/core/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
)
func getClaimRefNamespace(pv *corev1.PersistentVolume) string {
@@ -134,11 +132,3 @@ func VisitPVSecretNames(pv *corev1.PersistentVolume, visitor Visitor) bool {
}
return true
}
// DropDisabledAlphaFields removes disabled fields from the pv spec.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pv spec.
func DropDisabledAlphaFields(pvSpec *corev1.PersistentVolumeSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
pvSpec.VolumeMode = nil
}
}

View File

@@ -25,9 +25,7 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
)
func TestPVSecrets(t *testing.T) {
@@ -272,56 +270,3 @@ func newHostPathType(pathType string) *corev1.HostPathType {
*hostPathType = corev1.HostPathType(pathType)
return hostPathType
}
func TestDropAlphaPVVolumeMode(t *testing.T) {
vmode := corev1.PersistentVolumeFilesystem
// PersistentVolume with VolumeMode set
pv := corev1.PersistentVolume{
Spec: corev1.PersistentVolumeSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
PersistentVolumeSource: corev1.PersistentVolumeSource{
HostPath: &corev1.HostPathVolumeSource{
Path: "/foo",
Type: newHostPathType(string(corev1.HostPathDirectory)),
},
},
StorageClassName: "test-storage-class",
VolumeMode: &vmode,
},
}
// Enable alpha feature BlockVolume
err1 := utilfeature.DefaultFeatureGate.Set("BlockVolume=true")
if err1 != nil {
t.Fatalf("Failed to enable feature gate for BlockVolume: %v", err1)
}
// now test dropping the fields - should not be dropped
DropDisabledAlphaFields(&pv.Spec)
// check to make sure VolumeDevices is still present
// if featureset is set to true
if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
if pv.Spec.VolumeMode == nil {
t.Error("VolumeMode in pv.Spec should not have been dropped based on feature-gate")
}
}
// Disable alpha feature BlockVolume
err := utilfeature.DefaultFeatureGate.Set("BlockVolume=false")
if err != nil {
t.Fatalf("Failed to disable feature gate for BlockVolume: %v", err)
}
// now test dropping the fields
DropDisabledAlphaFields(&pv.Spec)
// check to make sure VolumeDevices is nil
// if featureset is set to false
if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
if pv.Spec.VolumeMode != nil {
t.Error("DropDisabledAlphaFields VolumeMode for pv.Spec failed")
}
}
}

View File

@@ -229,7 +229,7 @@ type PersistentVolumeSource struct {
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
// +optional
StorageOS *StorageOSPersistentVolumeSource
// CSI (Container Storage Interface) represents storage that handled by an external CSI driver (Beta feature).
// CSI (Container Storage Interface) represents storage that handled by an external CSI driver.
// +optional
CSI *CSIPersistentVolumeSource
}
@@ -1547,7 +1547,7 @@ type LocalVolumeSource struct {
FSType *string
}
// Represents storage that is managed by an external CSI volume driver (Beta feature)
// Represents storage that is managed by an external CSI volume driver.
type CSIPersistentVolumeSource struct {
// Driver is the name of the driver to use for this volume.
// Required.

View File

@@ -1443,24 +1443,27 @@ func validateStorageOSPersistentVolumeSource(storageos *core.StorageOSPersistent
return allErrs
}
func ValidateCSIDriverName(driverName string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(driverName) == 0 {
allErrs = append(allErrs, field.Required(fldPath, ""))
}
if len(driverName) > 63 {
allErrs = append(allErrs, field.TooLong(fldPath, driverName, 63))
}
if !csiDriverNameRexp.MatchString(driverName) {
allErrs = append(allErrs, field.Invalid(fldPath, driverName, validation.RegexError(csiDriverNameRexpErrMsg, csiDriverNameRexpFmt, "csi-hostpath")))
}
return allErrs
}
func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIPersistentVolume) {
allErrs = append(allErrs, field.Forbidden(fldPath, "CSIPersistentVolume disabled by feature-gate"))
}
if len(csi.Driver) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("driver"), ""))
}
if len(csi.Driver) > 63 {
allErrs = append(allErrs, field.TooLong(fldPath.Child("driver"), csi.Driver, 63))
}
if !csiDriverNameRexp.MatchString(csi.Driver) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("driver"), csi.Driver, validation.RegexError(csiDriverNameRexpErrMsg, csiDriverNameRexpFmt, "csi-hostpath")))
}
allErrs = append(allErrs, ValidateCSIDriverName(csi.Driver, fldPath.Child("driver"))...)
if len(csi.VolumeHandle) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("volumeHandle"), ""))

View File

@@ -58,6 +58,66 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.VolumeAttachment)(nil), (*storage.VolumeAttachment)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_VolumeAttachment_To_storage_VolumeAttachment(a.(*v1.VolumeAttachment), b.(*storage.VolumeAttachment), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*storage.VolumeAttachment)(nil), (*v1.VolumeAttachment)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_storage_VolumeAttachment_To_v1_VolumeAttachment(a.(*storage.VolumeAttachment), b.(*v1.VolumeAttachment), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.VolumeAttachmentList)(nil), (*storage.VolumeAttachmentList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_VolumeAttachmentList_To_storage_VolumeAttachmentList(a.(*v1.VolumeAttachmentList), b.(*storage.VolumeAttachmentList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*storage.VolumeAttachmentList)(nil), (*v1.VolumeAttachmentList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_storage_VolumeAttachmentList_To_v1_VolumeAttachmentList(a.(*storage.VolumeAttachmentList), b.(*v1.VolumeAttachmentList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.VolumeAttachmentSource)(nil), (*storage.VolumeAttachmentSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_VolumeAttachmentSource_To_storage_VolumeAttachmentSource(a.(*v1.VolumeAttachmentSource), b.(*storage.VolumeAttachmentSource), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*storage.VolumeAttachmentSource)(nil), (*v1.VolumeAttachmentSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_storage_VolumeAttachmentSource_To_v1_VolumeAttachmentSource(a.(*storage.VolumeAttachmentSource), b.(*v1.VolumeAttachmentSource), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.VolumeAttachmentSpec)(nil), (*storage.VolumeAttachmentSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_VolumeAttachmentSpec_To_storage_VolumeAttachmentSpec(a.(*v1.VolumeAttachmentSpec), b.(*storage.VolumeAttachmentSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*storage.VolumeAttachmentSpec)(nil), (*v1.VolumeAttachmentSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_storage_VolumeAttachmentSpec_To_v1_VolumeAttachmentSpec(a.(*storage.VolumeAttachmentSpec), b.(*v1.VolumeAttachmentSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.VolumeAttachmentStatus)(nil), (*storage.VolumeAttachmentStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_VolumeAttachmentStatus_To_storage_VolumeAttachmentStatus(a.(*v1.VolumeAttachmentStatus), b.(*storage.VolumeAttachmentStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*storage.VolumeAttachmentStatus)(nil), (*v1.VolumeAttachmentStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_storage_VolumeAttachmentStatus_To_v1_VolumeAttachmentStatus(a.(*storage.VolumeAttachmentStatus), b.(*v1.VolumeAttachmentStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.VolumeError)(nil), (*storage.VolumeError)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_VolumeError_To_storage_VolumeError(a.(*v1.VolumeError), b.(*storage.VolumeError), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*storage.VolumeError)(nil), (*v1.VolumeError)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_storage_VolumeError_To_v1_VolumeError(a.(*storage.VolumeError), b.(*v1.VolumeError), scope)
}); err != nil {
return err
}
return nil
}
@@ -116,3 +176,153 @@ func autoConvert_storage_StorageClassList_To_v1_StorageClassList(in *storage.Sto
func Convert_storage_StorageClassList_To_v1_StorageClassList(in *storage.StorageClassList, out *v1.StorageClassList, s conversion.Scope) error {
return autoConvert_storage_StorageClassList_To_v1_StorageClassList(in, out, s)
}
func autoConvert_v1_VolumeAttachment_To_storage_VolumeAttachment(in *v1.VolumeAttachment, out *storage.VolumeAttachment, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1_VolumeAttachmentSpec_To_storage_VolumeAttachmentSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_v1_VolumeAttachmentStatus_To_storage_VolumeAttachmentStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_v1_VolumeAttachment_To_storage_VolumeAttachment is an autogenerated conversion function.
func Convert_v1_VolumeAttachment_To_storage_VolumeAttachment(in *v1.VolumeAttachment, out *storage.VolumeAttachment, s conversion.Scope) error {
return autoConvert_v1_VolumeAttachment_To_storage_VolumeAttachment(in, out, s)
}
func autoConvert_storage_VolumeAttachment_To_v1_VolumeAttachment(in *storage.VolumeAttachment, out *v1.VolumeAttachment, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_storage_VolumeAttachmentSpec_To_v1_VolumeAttachmentSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_storage_VolumeAttachmentStatus_To_v1_VolumeAttachmentStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_storage_VolumeAttachment_To_v1_VolumeAttachment is an autogenerated conversion function.
func Convert_storage_VolumeAttachment_To_v1_VolumeAttachment(in *storage.VolumeAttachment, out *v1.VolumeAttachment, s conversion.Scope) error {
return autoConvert_storage_VolumeAttachment_To_v1_VolumeAttachment(in, out, s)
}
func autoConvert_v1_VolumeAttachmentList_To_storage_VolumeAttachmentList(in *v1.VolumeAttachmentList, out *storage.VolumeAttachmentList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]storage.VolumeAttachment)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_v1_VolumeAttachmentList_To_storage_VolumeAttachmentList is an autogenerated conversion function.
func Convert_v1_VolumeAttachmentList_To_storage_VolumeAttachmentList(in *v1.VolumeAttachmentList, out *storage.VolumeAttachmentList, s conversion.Scope) error {
return autoConvert_v1_VolumeAttachmentList_To_storage_VolumeAttachmentList(in, out, s)
}
func autoConvert_storage_VolumeAttachmentList_To_v1_VolumeAttachmentList(in *storage.VolumeAttachmentList, out *v1.VolumeAttachmentList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]v1.VolumeAttachment)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_storage_VolumeAttachmentList_To_v1_VolumeAttachmentList is an autogenerated conversion function.
func Convert_storage_VolumeAttachmentList_To_v1_VolumeAttachmentList(in *storage.VolumeAttachmentList, out *v1.VolumeAttachmentList, s conversion.Scope) error {
return autoConvert_storage_VolumeAttachmentList_To_v1_VolumeAttachmentList(in, out, s)
}
func autoConvert_v1_VolumeAttachmentSource_To_storage_VolumeAttachmentSource(in *v1.VolumeAttachmentSource, out *storage.VolumeAttachmentSource, s conversion.Scope) error {
out.PersistentVolumeName = (*string)(unsafe.Pointer(in.PersistentVolumeName))
return nil
}
// Convert_v1_VolumeAttachmentSource_To_storage_VolumeAttachmentSource is an autogenerated conversion function.
func Convert_v1_VolumeAttachmentSource_To_storage_VolumeAttachmentSource(in *v1.VolumeAttachmentSource, out *storage.VolumeAttachmentSource, s conversion.Scope) error {
return autoConvert_v1_VolumeAttachmentSource_To_storage_VolumeAttachmentSource(in, out, s)
}
func autoConvert_storage_VolumeAttachmentSource_To_v1_VolumeAttachmentSource(in *storage.VolumeAttachmentSource, out *v1.VolumeAttachmentSource, s conversion.Scope) error {
out.PersistentVolumeName = (*string)(unsafe.Pointer(in.PersistentVolumeName))
return nil
}
// Convert_storage_VolumeAttachmentSource_To_v1_VolumeAttachmentSource is an autogenerated conversion function.
func Convert_storage_VolumeAttachmentSource_To_v1_VolumeAttachmentSource(in *storage.VolumeAttachmentSource, out *v1.VolumeAttachmentSource, s conversion.Scope) error {
return autoConvert_storage_VolumeAttachmentSource_To_v1_VolumeAttachmentSource(in, out, s)
}
func autoConvert_v1_VolumeAttachmentSpec_To_storage_VolumeAttachmentSpec(in *v1.VolumeAttachmentSpec, out *storage.VolumeAttachmentSpec, s conversion.Scope) error {
out.Attacher = in.Attacher
if err := Convert_v1_VolumeAttachmentSource_To_storage_VolumeAttachmentSource(&in.Source, &out.Source, s); err != nil {
return err
}
out.NodeName = in.NodeName
return nil
}
// Convert_v1_VolumeAttachmentSpec_To_storage_VolumeAttachmentSpec is an autogenerated conversion function.
func Convert_v1_VolumeAttachmentSpec_To_storage_VolumeAttachmentSpec(in *v1.VolumeAttachmentSpec, out *storage.VolumeAttachmentSpec, s conversion.Scope) error {
return autoConvert_v1_VolumeAttachmentSpec_To_storage_VolumeAttachmentSpec(in, out, s)
}
func autoConvert_storage_VolumeAttachmentSpec_To_v1_VolumeAttachmentSpec(in *storage.VolumeAttachmentSpec, out *v1.VolumeAttachmentSpec, s conversion.Scope) error {
out.Attacher = in.Attacher
if err := Convert_storage_VolumeAttachmentSource_To_v1_VolumeAttachmentSource(&in.Source, &out.Source, s); err != nil {
return err
}
out.NodeName = in.NodeName
return nil
}
// Convert_storage_VolumeAttachmentSpec_To_v1_VolumeAttachmentSpec is an autogenerated conversion function.
func Convert_storage_VolumeAttachmentSpec_To_v1_VolumeAttachmentSpec(in *storage.VolumeAttachmentSpec, out *v1.VolumeAttachmentSpec, s conversion.Scope) error {
return autoConvert_storage_VolumeAttachmentSpec_To_v1_VolumeAttachmentSpec(in, out, s)
}
func autoConvert_v1_VolumeAttachmentStatus_To_storage_VolumeAttachmentStatus(in *v1.VolumeAttachmentStatus, out *storage.VolumeAttachmentStatus, s conversion.Scope) error {
out.Attached = in.Attached
out.AttachmentMetadata = *(*map[string]string)(unsafe.Pointer(&in.AttachmentMetadata))
out.AttachError = (*storage.VolumeError)(unsafe.Pointer(in.AttachError))
out.DetachError = (*storage.VolumeError)(unsafe.Pointer(in.DetachError))
return nil
}
// Convert_v1_VolumeAttachmentStatus_To_storage_VolumeAttachmentStatus is an autogenerated conversion function.
func Convert_v1_VolumeAttachmentStatus_To_storage_VolumeAttachmentStatus(in *v1.VolumeAttachmentStatus, out *storage.VolumeAttachmentStatus, s conversion.Scope) error {
return autoConvert_v1_VolumeAttachmentStatus_To_storage_VolumeAttachmentStatus(in, out, s)
}
func autoConvert_storage_VolumeAttachmentStatus_To_v1_VolumeAttachmentStatus(in *storage.VolumeAttachmentStatus, out *v1.VolumeAttachmentStatus, s conversion.Scope) error {
out.Attached = in.Attached
out.AttachmentMetadata = *(*map[string]string)(unsafe.Pointer(&in.AttachmentMetadata))
out.AttachError = (*v1.VolumeError)(unsafe.Pointer(in.AttachError))
out.DetachError = (*v1.VolumeError)(unsafe.Pointer(in.DetachError))
return nil
}
// Convert_storage_VolumeAttachmentStatus_To_v1_VolumeAttachmentStatus is an autogenerated conversion function.
func Convert_storage_VolumeAttachmentStatus_To_v1_VolumeAttachmentStatus(in *storage.VolumeAttachmentStatus, out *v1.VolumeAttachmentStatus, s conversion.Scope) error {
return autoConvert_storage_VolumeAttachmentStatus_To_v1_VolumeAttachmentStatus(in, out, s)
}
func autoConvert_v1_VolumeError_To_storage_VolumeError(in *v1.VolumeError, out *storage.VolumeError, s conversion.Scope) error {
out.Time = in.Time
out.Message = in.Message
return nil
}
// Convert_v1_VolumeError_To_storage_VolumeError is an autogenerated conversion function.
func Convert_v1_VolumeError_To_storage_VolumeError(in *v1.VolumeError, out *storage.VolumeError, s conversion.Scope) error {
return autoConvert_v1_VolumeError_To_storage_VolumeError(in, out, s)
}
func autoConvert_storage_VolumeError_To_v1_VolumeError(in *storage.VolumeError, out *v1.VolumeError, s conversion.Scope) error {
out.Time = in.Time
out.Message = in.Message
return nil
}
// Convert_storage_VolumeError_To_v1_VolumeError is an autogenerated conversion function.
func Convert_storage_VolumeError_To_v1_VolumeError(in *storage.VolumeError, out *v1.VolumeError, s conversion.Scope) error {
return autoConvert_storage_VolumeError_To_v1_VolumeError(in, out, s)
}

View File

@@ -133,7 +133,7 @@ func validateAllowVolumeExpansion(allowExpand *bool, fldPath *field.Path) field.
return allErrs
}
// ValidateVolumeAttachment validates a VolumeAttachment.
// ValidateVolumeAttachment validates a VolumeAttachment. This function is common for v1 and v1beta1 objects,
func ValidateVolumeAttachment(volumeAttachment *storage.VolumeAttachment) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&volumeAttachment.ObjectMeta, false, apivalidation.ValidateClassName, field.NewPath("metadata"))
allErrs = append(allErrs, validateVolumeAttachmentSpec(&volumeAttachment.Spec, field.NewPath("spec"))...)
@@ -141,6 +141,20 @@ func ValidateVolumeAttachment(volumeAttachment *storage.VolumeAttachment) field.
return allErrs
}
// ValidateVolumeAttachmentV1 validates a v1/VolumeAttachment. It contains only extra checks missing in
// ValidateVolumeAttachment.
func ValidateVolumeAttachmentV1(volumeAttachment *storage.VolumeAttachment) field.ErrorList {
allErrs := apivalidation.ValidateCSIDriverName(volumeAttachment.Spec.Attacher, field.NewPath("spec.attacher"))
if volumeAttachment.Spec.Source.PersistentVolumeName != nil {
pvName := *volumeAttachment.Spec.Source.PersistentVolumeName
for _, msg := range apivalidation.ValidatePersistentVolumeName(pvName, false) {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec.source.persistentVolumeName"), pvName, msg))
}
}
return allErrs
}
// ValidateVolumeAttachmentSpec tests that the specified VolumeAttachmentSpec
// has valid data.
func validateVolumeAttachmentSpec(
@@ -218,6 +232,7 @@ func ValidateVolumeAttachmentUpdate(new, old *storage.VolumeAttachment) field.Er
allErrs := ValidateVolumeAttachment(new)
// Spec is read-only
// If this ever relaxes in the future, make sure to increment the Generation number in PrepareForUpdate
if !apiequality.Semantic.DeepEqual(old.Spec, new.Spec) {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec"), new.Spec, "field is immutable"))
}

View File

@@ -224,14 +224,9 @@ func TestVolumeAttachmentValidation(t *testing.T) {
Spec: storage.VolumeAttachmentSpec{
Attacher: "",
NodeName: "mynode",
},
},
{
// Invalid attacher name
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: storage.VolumeAttachmentSpec{
Attacher: "invalid!@#$%^&*()",
NodeName: "mynode",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &volumeName,
},
},
},
{
@@ -240,6 +235,9 @@ func TestVolumeAttachmentValidation(t *testing.T) {
Spec: storage.VolumeAttachmentSpec{
Attacher: "myattacher",
NodeName: "",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &volumeName,
},
},
},
{
@@ -378,7 +376,7 @@ func TestVolumeAttachmentUpdateValidation(t *testing.T) {
for _, volumeAttachment := range successCases {
if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
t.Errorf("expected success: %+v", errs)
}
}
@@ -445,7 +443,61 @@ func TestVolumeAttachmentUpdateValidation(t *testing.T) {
for _, volumeAttachment := range errorCases {
if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) == 0 {
t.Errorf("Expected failure for test: %v", volumeAttachment)
t.Errorf("Expected failure for test: %+v", volumeAttachment)
}
}
}
func TestVolumeAttachmentValidationV1(t *testing.T) {
volumeName := "pv-name"
invalidVolumeName := "-invalid-@#$%^&*()-"
successCases := []storage.VolumeAttachment{
{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: storage.VolumeAttachmentSpec{
Attacher: "myattacher",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &volumeName,
},
NodeName: "mynode",
},
},
}
for _, volumeAttachment := range successCases {
if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) != 0 {
t.Errorf("expected success: %+v", errs)
}
}
errorCases := []storage.VolumeAttachment{
{
// Invalid attacher name
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: storage.VolumeAttachmentSpec{
Attacher: "invalid-@#$%^&*()",
NodeName: "mynode",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &volumeName,
},
},
},
{
// Invalid PV name
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: storage.VolumeAttachmentSpec{
Attacher: "myattacher",
NodeName: "mynode",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &invalidVolumeName,
},
},
},
}
for _, volumeAttachment := range errorCases {
if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) == 0 {
t.Errorf("Expected failure for test: %+v", volumeAttachment)
}
}
}

View File

@@ -194,7 +194,7 @@ const (
VolumeScheduling utilfeature.Feature = "VolumeScheduling"
// owner: @vladimirvivien
// beta: v1.10
// GA: v1.13
//
// Enable mount/attachment of Container Storage Interface (CSI) backed PVs
CSIPersistentVolume utilfeature.Feature = "CSIPersistentVolume"
@@ -423,7 +423,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
ServiceNodeExclusion: {Default: false, PreRelease: utilfeature.Alpha},
MountContainers: {Default: false, PreRelease: utilfeature.Alpha},
VolumeScheduling: {Default: true, PreRelease: utilfeature.GA},
CSIPersistentVolume: {Default: true, PreRelease: utilfeature.Beta},
CSIPersistentVolume: {Default: true, PreRelease: utilfeature.GA},
CSIDriverRegistry: {Default: false, PreRelease: utilfeature.Alpha},
CSINodeInfo: {Default: false, PreRelease: utilfeature.Alpha},
CustomPodDNS: {Default: true, PreRelease: utilfeature.Beta},

View File

@@ -53,7 +53,7 @@ func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtim
pv := obj.(*api.PersistentVolume)
pv.Status = api.PersistentVolumeStatus{}
pvutil.DropDisabledAlphaFields(&pv.Spec)
pvutil.DropDisabledFields(&pv.Spec, nil)
}
func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
@@ -76,8 +76,7 @@ func (persistentvolumeStrategy) PrepareForUpdate(ctx context.Context, obj, old r
oldPv := old.(*api.PersistentVolume)
newPv.Status = oldPv.Status
pvutil.DropDisabledAlphaFields(&newPv.Spec)
pvutil.DropDisabledAlphaFields(&oldPv.Spec)
pvutil.DropDisabledFields(&newPv.Spec, &oldPv.Spec)
}
func (persistentvolumeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {

View File

@@ -54,8 +54,8 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
storage := map[string]rest.Storage{}
// volumeattachments
volumeAttachmentStorage := volumeattachmentstore.NewREST(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage
volumeAttachmentStorage := volumeattachmentstore.NewStorage(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage.VolumeAttachment
return storage
}
@@ -67,17 +67,24 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag
storage["storageclasses"] = storageClassStorage
// volumeattachments
volumeAttachmentStorage := volumeattachmentstore.NewREST(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage
volumeAttachmentStorage := volumeattachmentstore.NewStorage(restOptionsGetter)
storage["volumeattachments"] = volumeAttachmentStorage.VolumeAttachment
return storage
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
storage := map[string]rest.Storage{}
// storageclasses
storageClassStorage := storageclassstore.NewREST(restOptionsGetter)
storage["storageclasses"] = storageClassStorage
volumeAttachmentStorage := volumeattachmentstore.NewStorage(restOptionsGetter)
storage := map[string]rest.Storage{
// storageclasses
"storageclasses": storageClassStorage,
// volumeattachments
"volumeattachments": volumeAttachmentStorage.VolumeAttachment,
"volumeattachments/status": volumeAttachmentStorage.Status,
}
return storage
}

View File

@@ -12,8 +12,11 @@ go_library(
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/apis/storage/validation:go_default_library",
"//staging/src/k8s.io/api/storage/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)
@@ -24,7 +27,10 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/apis/storage:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
],
)

View File

@@ -8,9 +8,11 @@ go_library(
deps = [
"//pkg/apis/storage:go_default_library",
"//pkg/registry/storage/volumeattachment:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
@@ -19,17 +21,18 @@ go_test(
srcs = ["storage_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/api/testapi:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/registry/registrytest:go_default_library",
"//staging/src/k8s.io/api/storage/v1alpha1:go_default_library",
"//staging/src/k8s.io/api/storage/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
],
)

View File

@@ -17,20 +17,30 @@ limitations under the License.
package storage
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
storageapi "k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/registry/storage/volumeattachment"
)
// REST object that will work against persistent volumes.
// VolumeAttachmentStorage includes storage for VolumeAttachments and all subresources
type VolumeAttachmentStorage struct {
VolumeAttachment *REST
Status *StatusREST
}
// REST object that will work for VolumeAttachments
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against persistent volumes.
func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
// NewStorage returns a RESTStorage object that will work against VolumeAttachments
func NewStorage(optsGetter generic.RESTOptionsGetter) *VolumeAttachmentStorage {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &storageapi.VolumeAttachment{} },
NewListFunc: func() runtime.Object { return &storageapi.VolumeAttachmentList{} },
@@ -46,5 +56,33 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
panic(err) // TODO: Propagate error up
}
return &REST{store}
statusStore := *store
statusStore.UpdateStrategy = volumeattachment.StatusStrategy
return &VolumeAttachmentStorage{
VolumeAttachment: &REST{store},
Status: &StatusREST{store: &statusStore},
}
}
// StatusREST implements the REST endpoint for changing the status of a VolumeAttachment
type StatusREST struct {
store *genericregistry.Store
}
// New creates a new VolumeAttachment resource
func (r *StatusREST) New() runtime.Object {
return &storageapi.VolumeAttachment{}
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
// subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
}

View File

@@ -19,21 +19,22 @@ package storage
import (
"testing"
storageapiv1alpha1 "k8s.io/api/storage/v1alpha1"
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
"k8s.io/apiserver/pkg/registry/rest"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
"k8s.io/kubernetes/pkg/api/testapi"
storageapi "k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, storageapi.GroupName)
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
@@ -41,8 +42,8 @@ func newStorage(t *testing.T) (*REST, *etcdtesting.EtcdTestServer) {
DeleteCollectionWorkers: 1,
ResourcePrefix: "volumeattachments",
}
volumeAttachmentStorage := NewREST(restOptions)
return volumeAttachmentStorage, server
volumeAttachmentStorage := NewStorage(restOptions)
return volumeAttachmentStorage.VolumeAttachment, volumeAttachmentStorage.Status, server
}
func validNewVolumeAttachment(name string) *storageapi.VolumeAttachment {
@@ -62,13 +63,7 @@ func validNewVolumeAttachment(name string) *storageapi.VolumeAttachment {
}
func TestCreate(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@@ -93,20 +88,16 @@ func TestCreate(t *testing.T) {
}
func TestUpdate(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestUpdate(
// valid
validNewVolumeAttachment("foo"),
// updateFunc
// we still allow status field to be set in both v1 and v1beta1
// it is just that in v1 the new value does not take effect.
func(obj runtime.Object) runtime.Object {
object := obj.(*storageapi.VolumeAttachment)
object.Status.Attached = true
@@ -122,13 +113,7 @@ func TestUpdate(t *testing.T) {
}
func TestDelete(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope().ReturnDeletedObject()
@@ -136,13 +121,7 @@ func TestDelete(t *testing.T) {
}
func TestGet(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@@ -150,13 +129,7 @@ func TestGet(t *testing.T) {
}
func TestList(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@@ -164,13 +137,7 @@ func TestList(t *testing.T) {
}
func TestWatch(t *testing.T) {
if *testapi.Storage.GroupVersion() != storageapiv1alpha1.SchemeGroupVersion &&
*testapi.Storage.GroupVersion() != storageapiv1beta1.SchemeGroupVersion {
// skip the test for all versions exception v1alpha1 and v1beta1
return
}
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@@ -192,3 +159,41 @@ func TestWatch(t *testing.T) {
},
)
}
func TestEtcdStatusUpdate(t *testing.T) {
storage, statusStorage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
ctx := genericapirequest.NewDefaultContext()
attachment := validNewVolumeAttachment("foo")
if _, err := storage.Create(ctx, attachment, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
t.Fatalf("unexpected error: %v", err)
}
obj, err := storage.Get(ctx, attachment.ObjectMeta.Name, &metav1.GetOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
// update status
attachmentIn := obj.(*storageapi.VolumeAttachment).DeepCopy()
attachmentIn.Status.Attached = true
_, _, err = statusStorage.Update(ctx, attachmentIn.Name, rest.DefaultUpdatedObjectInfo(attachmentIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
if err != nil {
t.Fatalf("Failed to update status: %v", err)
}
// validate object got updated
obj, err = storage.Get(ctx, attachmentIn.ObjectMeta.Name, &metav1.GetOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
attachmentOut := obj.(*storageapi.VolumeAttachment)
if !apiequality.Semantic.DeepEqual(attachmentIn.Spec, attachmentOut.Spec) {
t.Errorf("objects differ: %v", diff.ObjectDiff(attachmentOut.Spec, attachmentIn.Spec))
}
if !apiequality.Semantic.DeepEqual(attachmentIn.Status, attachmentOut.Status) {
t.Errorf("objects differ: %v", diff.ObjectDiff(attachmentOut.Status, attachmentIn.Status))
}
}

View File

@@ -19,8 +19,11 @@ package volumeattachment
import (
"context"
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/storage"
@@ -43,11 +46,40 @@ func (volumeAttachmentStrategy) NamespaceScoped() bool {
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
func (volumeAttachmentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
var groupVersion schema.GroupVersion
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
switch groupVersion {
case storageapiv1beta1.SchemeGroupVersion:
// allow modification of status for v1beta1
default:
volumeAttachment := obj.(*storage.VolumeAttachment)
volumeAttachment.Status = storage.VolumeAttachmentStatus{}
}
}
func (volumeAttachmentStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
volumeAttachment := obj.(*storage.VolumeAttachment)
return validation.ValidateVolumeAttachment(volumeAttachment)
errs := validation.ValidateVolumeAttachment(volumeAttachment)
var groupVersion schema.GroupVersion
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
switch groupVersion {
case storageapiv1beta1.SchemeGroupVersion:
// no extra validation
default:
// tighten up validation of newly created v1 attachments
errs = append(errs, validation.ValidateVolumeAttachmentV1(volumeAttachment)...)
}
return errs
}
// Canonicalize normalizes the object after validation.
@@ -58,8 +90,22 @@ func (volumeAttachmentStrategy) AllowCreateOnUpdate() bool {
return false
}
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a PV
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a VolumeAttachment
func (volumeAttachmentStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
var groupVersion schema.GroupVersion
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
switch groupVersion {
case storageapiv1beta1.SchemeGroupVersion:
// allow modification of Status via main resource for v1beta1
default:
newVolumeAttachment := obj.(*storage.VolumeAttachment)
oldVolumeAttachment := old.(*storage.VolumeAttachment)
newVolumeAttachment.Status = oldVolumeAttachment.Status
// No need to increment Generation because we don't allow updates to spec
}
}
func (volumeAttachmentStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
@@ -72,3 +118,30 @@ func (volumeAttachmentStrategy) ValidateUpdate(ctx context.Context, obj, old run
func (volumeAttachmentStrategy) AllowUnconditionalUpdate() bool {
return false
}
// volumeAttachmentStatusStrategy implements behavior for VolumeAttachmentStatus subresource
type volumeAttachmentStatusStrategy struct {
volumeAttachmentStrategy
}
// StatusStrategy is the default logic that applies when creating and updating
// VolumeAttachmentStatus subresource via the REST API.
var StatusStrategy = volumeAttachmentStatusStrategy{Strategy}
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a VolumeAttachment
func (volumeAttachmentStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newVolumeAttachment := obj.(*storage.VolumeAttachment)
oldVolumeAttachment := old.(*storage.VolumeAttachment)
newVolumeAttachment.Spec = oldVolumeAttachment.Spec
oldMeta := oldVolumeAttachment.ObjectMeta
newMeta := &newVolumeAttachment.ObjectMeta
newMeta.SetDeletionTimestamp(oldMeta.GetDeletionTimestamp())
newMeta.SetGeneration(oldMeta.GetGeneration())
newMeta.SetSelfLink(oldMeta.GetSelfLink())
newMeta.SetLabels(oldMeta.GetLabels())
newMeta.SetAnnotations(oldMeta.GetAnnotations())
newMeta.SetFinalizers(oldMeta.GetFinalizers())
newMeta.SetOwnerReferences(oldMeta.GetOwnerReferences())
}

View File

@@ -19,13 +19,35 @@ package volumeattachment
import (
"testing"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/apis/storage"
)
func getValidVolumeAttachment(name string) *storage.VolumeAttachment {
return &storage.VolumeAttachment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: storage.VolumeAttachmentSpec{
Attacher: "valid-attacher",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &name,
},
NodeName: "valid-node",
},
}
}
func TestVolumeAttachmentStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1",
Resource: "volumeattachments",
})
if Strategy.NamespaceScoped() {
t.Errorf("VolumeAttachment must not be namespace scoped")
}
@@ -33,19 +55,7 @@ func TestVolumeAttachmentStrategy(t *testing.T) {
t.Errorf("VolumeAttachment should not allow create on update")
}
pvName := "name"
volumeAttachment := &storage.VolumeAttachment{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-attachment",
},
Spec: storage.VolumeAttachmentSpec{
Attacher: "valid-attacher",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &pvName,
},
NodeName: "valid-node",
},
}
volumeAttachment := getValidVolumeAttachment("valid-attachment")
Strategy.PrepareForCreate(ctx, volumeAttachment)
@@ -54,19 +64,18 @@ func TestVolumeAttachmentStrategy(t *testing.T) {
t.Errorf("unexpected error validating %v", errs)
}
newVolumeAttachment := &storage.VolumeAttachment{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-attachment-2",
},
Spec: storage.VolumeAttachmentSpec{
Attacher: "valid-attacher-2",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &pvName,
},
NodeName: "valid-node-2",
},
// Create with status should drop status
statusVolumeAttachment := volumeAttachment.DeepCopy()
statusVolumeAttachment.Status = storage.VolumeAttachmentStatus{Attached: true}
Strategy.PrepareForCreate(ctx, statusVolumeAttachment)
if !apiequality.Semantic.DeepEqual(statusVolumeAttachment, volumeAttachment) {
t.Errorf("unexpected objects difference after creating with status: %v", diff.ObjectDiff(statusVolumeAttachment, volumeAttachment))
}
// Update of spec is disallowed
newVolumeAttachment := volumeAttachment.DeepCopy()
newVolumeAttachment.Spec.NodeName = "valid-node-2"
Strategy.PrepareForUpdate(ctx, newVolumeAttachment, volumeAttachment)
errs = Strategy.ValidateUpdate(ctx, newVolumeAttachment, volumeAttachment)
@@ -74,4 +83,215 @@ func TestVolumeAttachmentStrategy(t *testing.T) {
t.Errorf("Expected a validation error")
}
// modifying status should be dropped
statusVolumeAttachment = volumeAttachment.DeepCopy()
statusVolumeAttachment.Status = storage.VolumeAttachmentStatus{Attached: true}
Strategy.PrepareForUpdate(ctx, statusVolumeAttachment, volumeAttachment)
if !apiequality.Semantic.DeepEqual(statusVolumeAttachment, volumeAttachment) {
t.Errorf("unexpected objects difference after modfying status: %v", diff.ObjectDiff(statusVolumeAttachment, volumeAttachment))
}
}
func TestVolumeAttachmentStatusStrategy(t *testing.T) {
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1",
Resource: "volumeattachments",
})
volumeAttachment := getValidVolumeAttachment("valid-attachment")
// modifying status should be allowed
statusVolumeAttachment := volumeAttachment.DeepCopy()
statusVolumeAttachment.Status = storage.VolumeAttachmentStatus{Attached: true}
expectedVolumeAttachment := statusVolumeAttachment.DeepCopy()
StatusStrategy.PrepareForUpdate(ctx, statusVolumeAttachment, volumeAttachment)
if !apiequality.Semantic.DeepEqual(statusVolumeAttachment, expectedVolumeAttachment) {
t.Errorf("unexpected objects differerence after modifying status: %v", diff.ObjectDiff(statusVolumeAttachment, expectedVolumeAttachment))
}
// modifying spec should be dropped
newVolumeAttachment := volumeAttachment.DeepCopy()
newVolumeAttachment.Spec.NodeName = "valid-node-2"
StatusStrategy.PrepareForUpdate(ctx, newVolumeAttachment, volumeAttachment)
if !apiequality.Semantic.DeepEqual(newVolumeAttachment, volumeAttachment) {
t.Errorf("unexpected objects differerence after modifying spec: %v", diff.ObjectDiff(newVolumeAttachment, volumeAttachment))
}
}
func TestBetaAndV1StatusUpdate(t *testing.T) {
tests := []struct {
requestInfo genericapirequest.RequestInfo
newStatus bool
expectedStatus bool
}{
{
genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1",
Resource: "volumeattachments",
},
true,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1beta1",
Resource: "volumeattachments",
},
true,
true,
},
}
for _, test := range tests {
va := getValidVolumeAttachment("valid-attachment")
newAttachment := va.DeepCopy()
newAttachment.Status.Attached = test.newStatus
context := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &test.requestInfo)
Strategy.PrepareForUpdate(context, newAttachment, va)
if newAttachment.Status.Attached != test.expectedStatus {
t.Errorf("expected status to be %v got %v", test.expectedStatus, newAttachment.Status.Attached)
}
}
}
func TestBetaAndV1StatusCreate(t *testing.T) {
tests := []struct {
requestInfo genericapirequest.RequestInfo
newStatus bool
expectedStatus bool
}{
{
genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1",
Resource: "volumeattachments",
},
true,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1beta1",
Resource: "volumeattachments",
},
true,
true,
},
}
for _, test := range tests {
va := getValidVolumeAttachment("valid-attachment")
va.Status.Attached = test.newStatus
context := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &test.requestInfo)
Strategy.PrepareForCreate(context, va)
if va.Status.Attached != test.expectedStatus {
t.Errorf("expected status to be %v got %v", test.expectedStatus, va.Status.Attached)
}
}
}
func TestVolumeAttachmentValidation(t *testing.T) {
invalidPVName := "invalid-!@#$%^&*()"
validPVName := "valid-volume-name"
tests := []struct {
name string
volumeAttachment *storage.VolumeAttachment
expectBetaError bool
expectV1Error bool
}{
{
"valid attachment",
getValidVolumeAttachment("foo"),
false,
false,
},
{
"invalid PV name",
&storage.VolumeAttachment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.VolumeAttachmentSpec{
Attacher: "valid-attacher",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &invalidPVName,
},
NodeName: "valid-node",
},
},
false,
true,
},
{
"invalid attacher name",
&storage.VolumeAttachment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.VolumeAttachmentSpec{
Attacher: "invalid!@#$%^&*()",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &validPVName,
},
NodeName: "valid-node",
},
},
false,
true,
},
{
"invalid volume attachment",
&storage.VolumeAttachment{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.VolumeAttachmentSpec{
Attacher: "invalid!@#$%^&*()",
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: nil,
},
NodeName: "valid-node",
},
},
true,
true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
testValidation := func(va *storage.VolumeAttachment, apiVersion string) field.ErrorList {
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: apiVersion,
Resource: "volumeattachments",
})
return Strategy.Validate(ctx, va)
}
v1Err := testValidation(test.volumeAttachment, "v1")
if len(v1Err) > 0 && !test.expectV1Error {
t.Errorf("Validation of v1 object failed: %+v", v1Err)
}
if len(v1Err) == 0 && test.expectV1Error {
t.Errorf("Validation of v1 object unexpectedly succeeded")
}
betaErr := testValidation(test.volumeAttachment, "v1beta1")
if len(betaErr) > 0 && !test.expectBetaError {
t.Errorf("Validation of v1beta1 object failed: %+v", betaErr)
}
if len(betaErr) == 0 && test.expectBetaError {
t.Errorf("Validation of v1beta1 object unexpectedly succeeded")
}
})
}
}

View File

@@ -95,7 +95,6 @@ func (c *csiAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string
PersistentVolumeName: &pvName,
},
},
Status: storage.VolumeAttachmentStatus{Attached: false},
}
_, err = c.k8s.StorageV1beta1().VolumeAttachments().Create(attachment)

File diff suppressed because it is too large Load Diff

View File

@@ -88,3 +88,99 @@ message StorageClassList {
repeated StorageClass items = 2;
}
// VolumeAttachment captures the intent to attach or detach the specified volume
// to/from the specified node.
//
// VolumeAttachment objects are non-namespaced.
message VolumeAttachment {
// Standard object metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// Specification of the desired attach/detach volume behavior.
// Populated by the Kubernetes system.
optional VolumeAttachmentSpec spec = 2;
// Status of the VolumeAttachment request.
// Populated by the entity completing the attach or detach
// operation, i.e. the external-attacher.
// +optional
optional VolumeAttachmentStatus status = 3;
}
// VolumeAttachmentList is a collection of VolumeAttachment objects.
message VolumeAttachmentList {
// Standard list metadata
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1;
// Items is the list of VolumeAttachments
repeated VolumeAttachment items = 2;
}
// VolumeAttachmentSource represents a volume that should be attached.
// Right now only PersistenVolumes can be attached via external attacher,
// in future we may allow also inline volumes in pods.
// Exactly one member can be set.
message VolumeAttachmentSource {
// Name of the persistent volume to attach.
// +optional
optional string persistentVolumeName = 1;
}
// VolumeAttachmentSpec is the specification of a VolumeAttachment request.
message VolumeAttachmentSpec {
// Attacher indicates the name of the volume driver that MUST handle this
// request. This is the name returned by GetPluginName().
optional string attacher = 1;
// Source represents the volume that should be attached.
optional VolumeAttachmentSource source = 2;
// The node that the volume should be attached to.
optional string nodeName = 3;
}
// VolumeAttachmentStatus is the status of a VolumeAttachment request.
message VolumeAttachmentStatus {
// Indicates the volume is successfully attached.
// This field must only be set by the entity completing the attach
// operation, i.e. the external-attacher.
optional bool attached = 1;
// Upon successful attach, this field is populated with any
// information returned by the attach operation that must be passed
// into subsequent WaitForAttach or Mount calls.
// This field must only be set by the entity completing the attach
// operation, i.e. the external-attacher.
// +optional
map<string, string> attachmentMetadata = 2;
// The last error encountered during attach operation, if any.
// This field must only be set by the entity completing the attach
// operation, i.e. the external-attacher.
// +optional
optional VolumeError attachError = 3;
// The last error encountered during detach operation, if any.
// This field must only be set by the entity completing the detach
// operation, i.e. the external-attacher.
// +optional
optional VolumeError detachError = 4;
}
// VolumeError captures an error encountered during a volume operation.
message VolumeError {
// Time the error was encountered.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time time = 1;
// String detailing the error encountered during Attach or Detach operation.
// This string maybe logged, so it should not contain sensitive
// information.
// +optional
optional string message = 2;
}

View File

@@ -46,6 +46,9 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&StorageClass{},
&StorageClassList{},
&VolumeAttachment{},
&VolumeAttachmentList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)

View File

@@ -102,3 +102,110 @@ const (
// binding will occur during Pod scheduing.
VolumeBindingWaitForFirstConsumer VolumeBindingMode = "WaitForFirstConsumer"
)
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// VolumeAttachment captures the intent to attach or detach the specified volume
// to/from the specified node.
//
// VolumeAttachment objects are non-namespaced.
type VolumeAttachment struct {
metav1.TypeMeta `json:",inline"`
// Standard object metadata.
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Specification of the desired attach/detach volume behavior.
// Populated by the Kubernetes system.
Spec VolumeAttachmentSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
// Status of the VolumeAttachment request.
// Populated by the entity completing the attach or detach
// operation, i.e. the external-attacher.
// +optional
Status VolumeAttachmentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// VolumeAttachmentList is a collection of VolumeAttachment objects.
type VolumeAttachmentList struct {
metav1.TypeMeta `json:",inline"`
// Standard list metadata
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
// +optional
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Items is the list of VolumeAttachments
Items []VolumeAttachment `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// VolumeAttachmentSpec is the specification of a VolumeAttachment request.
type VolumeAttachmentSpec struct {
// Attacher indicates the name of the volume driver that MUST handle this
// request. This is the name returned by GetPluginName().
Attacher string `json:"attacher" protobuf:"bytes,1,opt,name=attacher"`
// Source represents the volume that should be attached.
Source VolumeAttachmentSource `json:"source" protobuf:"bytes,2,opt,name=source"`
// The node that the volume should be attached to.
NodeName string `json:"nodeName" protobuf:"bytes,3,opt,name=nodeName"`
}
// VolumeAttachmentSource represents a volume that should be attached.
// Right now only PersistenVolumes can be attached via external attacher,
// in future we may allow also inline volumes in pods.
// Exactly one member can be set.
type VolumeAttachmentSource struct {
// Name of the persistent volume to attach.
// +optional
PersistentVolumeName *string `json:"persistentVolumeName,omitempty" protobuf:"bytes,1,opt,name=persistentVolumeName"`
// Placeholder for *VolumeSource to accommodate inline volumes in pods.
}
// VolumeAttachmentStatus is the status of a VolumeAttachment request.
type VolumeAttachmentStatus struct {
// Indicates the volume is successfully attached.
// This field must only be set by the entity completing the attach
// operation, i.e. the external-attacher.
Attached bool `json:"attached" protobuf:"varint,1,opt,name=attached"`
// Upon successful attach, this field is populated with any
// information returned by the attach operation that must be passed
// into subsequent WaitForAttach or Mount calls.
// This field must only be set by the entity completing the attach
// operation, i.e. the external-attacher.
// +optional
AttachmentMetadata map[string]string `json:"attachmentMetadata,omitempty" protobuf:"bytes,2,rep,name=attachmentMetadata"`
// The last error encountered during attach operation, if any.
// This field must only be set by the entity completing the attach
// operation, i.e. the external-attacher.
// +optional
AttachError *VolumeError `json:"attachError,omitempty" protobuf:"bytes,3,opt,name=attachError,casttype=VolumeError"`
// The last error encountered during detach operation, if any.
// This field must only be set by the entity completing the detach
// operation, i.e. the external-attacher.
// +optional
DetachError *VolumeError `json:"detachError,omitempty" protobuf:"bytes,4,opt,name=detachError,casttype=VolumeError"`
}
// VolumeError captures an error encountered during a volume operation.
type VolumeError struct {
// Time the error was encountered.
// +optional
Time metav1.Time `json:"time,omitempty" protobuf:"bytes,1,opt,name=time"`
// String detailing the error encountered during Attach or Detach operation.
// This string maybe logged, so it should not contain sensitive
// information.
// +optional
Message string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"`
}

View File

@@ -53,4 +53,67 @@ func (StorageClassList) SwaggerDoc() map[string]string {
return map_StorageClassList
}
var map_VolumeAttachment = map[string]string{
"": "VolumeAttachment captures the intent to attach or detach the specified volume to/from the specified node.\n\nVolumeAttachment objects are non-namespaced.",
"metadata": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
"spec": "Specification of the desired attach/detach volume behavior. Populated by the Kubernetes system.",
"status": "Status of the VolumeAttachment request. Populated by the entity completing the attach or detach operation, i.e. the external-attacher.",
}
func (VolumeAttachment) SwaggerDoc() map[string]string {
return map_VolumeAttachment
}
var map_VolumeAttachmentList = map[string]string{
"": "VolumeAttachmentList is a collection of VolumeAttachment objects.",
"metadata": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata",
"items": "Items is the list of VolumeAttachments",
}
func (VolumeAttachmentList) SwaggerDoc() map[string]string {
return map_VolumeAttachmentList
}
var map_VolumeAttachmentSource = map[string]string{
"": "VolumeAttachmentSource represents a volume that should be attached. Right now only PersistenVolumes can be attached via external attacher, in future we may allow also inline volumes in pods. Exactly one member can be set.",
"persistentVolumeName": "Name of the persistent volume to attach.",
}
func (VolumeAttachmentSource) SwaggerDoc() map[string]string {
return map_VolumeAttachmentSource
}
var map_VolumeAttachmentSpec = map[string]string{
"": "VolumeAttachmentSpec is the specification of a VolumeAttachment request.",
"attacher": "Attacher indicates the name of the volume driver that MUST handle this request. This is the name returned by GetPluginName().",
"source": "Source represents the volume that should be attached.",
"nodeName": "The node that the volume should be attached to.",
}
func (VolumeAttachmentSpec) SwaggerDoc() map[string]string {
return map_VolumeAttachmentSpec
}
var map_VolumeAttachmentStatus = map[string]string{
"": "VolumeAttachmentStatus is the status of a VolumeAttachment request.",
"attached": "Indicates the volume is successfully attached. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.",
"attachmentMetadata": "Upon successful attach, this field is populated with any information returned by the attach operation that must be passed into subsequent WaitForAttach or Mount calls. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.",
"attachError": "The last error encountered during attach operation, if any. This field must only be set by the entity completing the attach operation, i.e. the external-attacher.",
"detachError": "The last error encountered during detach operation, if any. This field must only be set by the entity completing the detach operation, i.e. the external-attacher.",
}
func (VolumeAttachmentStatus) SwaggerDoc() map[string]string {
return map_VolumeAttachmentStatus
}
var map_VolumeError = map[string]string{
"": "VolumeError captures an error encountered during a volume operation.",
"time": "Time the error was encountered.",
"message": "String detailing the error encountered during Attach or Detach operation. This string maybe logged, so it should not contain sensitive information.",
}
func (VolumeError) SwaggerDoc() map[string]string {
return map_VolumeError
}
// AUTO-GENERATED FUNCTIONS END HERE

View File

@@ -117,3 +117,152 @@ func (in *StorageClassList) DeepCopyObject() runtime.Object {
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeAttachment) DeepCopyInto(out *VolumeAttachment) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeAttachment.
func (in *VolumeAttachment) DeepCopy() *VolumeAttachment {
if in == nil {
return nil
}
out := new(VolumeAttachment)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *VolumeAttachment) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeAttachmentList) DeepCopyInto(out *VolumeAttachmentList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]VolumeAttachment, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeAttachmentList.
func (in *VolumeAttachmentList) DeepCopy() *VolumeAttachmentList {
if in == nil {
return nil
}
out := new(VolumeAttachmentList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *VolumeAttachmentList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeAttachmentSource) DeepCopyInto(out *VolumeAttachmentSource) {
*out = *in
if in.PersistentVolumeName != nil {
in, out := &in.PersistentVolumeName, &out.PersistentVolumeName
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeAttachmentSource.
func (in *VolumeAttachmentSource) DeepCopy() *VolumeAttachmentSource {
if in == nil {
return nil
}
out := new(VolumeAttachmentSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeAttachmentSpec) DeepCopyInto(out *VolumeAttachmentSpec) {
*out = *in
in.Source.DeepCopyInto(&out.Source)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeAttachmentSpec.
func (in *VolumeAttachmentSpec) DeepCopy() *VolumeAttachmentSpec {
if in == nil {
return nil
}
out := new(VolumeAttachmentSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeAttachmentStatus) DeepCopyInto(out *VolumeAttachmentStatus) {
*out = *in
if in.AttachmentMetadata != nil {
in, out := &in.AttachmentMetadata, &out.AttachmentMetadata
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.AttachError != nil {
in, out := &in.AttachError, &out.AttachError
*out = new(VolumeError)
(*in).DeepCopyInto(*out)
}
if in.DetachError != nil {
in, out := &in.DetachError, &out.DetachError
*out = new(VolumeError)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeAttachmentStatus.
func (in *VolumeAttachmentStatus) DeepCopy() *VolumeAttachmentStatus {
if in == nil {
return nil
}
out := new(VolumeAttachmentStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VolumeError) DeepCopyInto(out *VolumeError) {
*out = *in
in.Time.DeepCopyInto(&out.Time)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeError.
func (in *VolumeError) DeepCopy() *VolumeError {
if in == nil {
return nil
}
out := new(VolumeError)
in.DeepCopyInto(out)
return out
}

View File

@@ -262,6 +262,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
// Group=storage.k8s.io, Version=v1
case storagev1.SchemeGroupVersion.WithResource("storageclasses"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Storage().V1().StorageClasses().Informer()}, nil
case storagev1.SchemeGroupVersion.WithResource("volumeattachments"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Storage().V1().VolumeAttachments().Informer()}, nil
// Group=storage.k8s.io, Version=v1alpha1
case storagev1alpha1.SchemeGroupVersion.WithResource("volumeattachments"):

View File

@@ -10,6 +10,7 @@ go_library(
srcs = [
"interface.go",
"storageclass.go",
"volumeattachment.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/client-go/informers/storage/v1",
importpath = "k8s.io/client-go/informers/storage/v1",

View File

@@ -26,6 +26,8 @@ import (
type Interface interface {
// StorageClasses returns a StorageClassInformer.
StorageClasses() StorageClassInformer
// VolumeAttachments returns a VolumeAttachmentInformer.
VolumeAttachments() VolumeAttachmentInformer
}
type version struct {
@@ -43,3 +45,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
func (v *version) StorageClasses() StorageClassInformer {
return &storageClassInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
}
// VolumeAttachments returns a VolumeAttachmentInformer.
func (v *version) VolumeAttachments() VolumeAttachmentInformer {
return &volumeAttachmentInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}
}

View File

@@ -0,0 +1,88 @@
/*
Copyright 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1
import (
time "time"
storagev1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
internalinterfaces "k8s.io/client-go/informers/internalinterfaces"
kubernetes "k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/listers/storage/v1"
cache "k8s.io/client-go/tools/cache"
)
// VolumeAttachmentInformer provides access to a shared informer and lister for
// VolumeAttachments.
type VolumeAttachmentInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1.VolumeAttachmentLister
}
type volumeAttachmentInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
}
// NewVolumeAttachmentInformer constructs a new informer for VolumeAttachment type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewVolumeAttachmentInformer(client kubernetes.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredVolumeAttachmentInformer(client, resyncPeriod, indexers, nil)
}
// NewFilteredVolumeAttachmentInformer constructs a new informer for VolumeAttachment type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredVolumeAttachmentInformer(client kubernetes.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.StorageV1().VolumeAttachments().List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.StorageV1().VolumeAttachments().Watch(options)
},
},
&storagev1.VolumeAttachment{},
resyncPeriod,
indexers,
)
}
func (f *volumeAttachmentInformer) defaultInformer(client kubernetes.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredVolumeAttachmentInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *volumeAttachmentInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&storagev1.VolumeAttachment{}, f.defaultInformer)
}
func (f *volumeAttachmentInformer) Lister() v1.VolumeAttachmentLister {
return v1.NewVolumeAttachmentLister(f.Informer().GetIndexer())
}

View File

@@ -12,6 +12,7 @@ go_library(
"generated_expansion.go",
"storage_client.go",
"storageclass.go",
"volumeattachment.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/client-go/kubernetes/typed/storage/v1",
importpath = "k8s.io/client-go/kubernetes/typed/storage/v1",

View File

@@ -11,6 +11,7 @@ go_library(
"doc.go",
"fake_storage_client.go",
"fake_storageclass.go",
"fake_volumeattachment.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/client-go/kubernetes/typed/storage/v1/fake",
importpath = "k8s.io/client-go/kubernetes/typed/storage/v1/fake",

View File

@@ -32,6 +32,10 @@ func (c *FakeStorageV1) StorageClasses() v1.StorageClassInterface {
return &FakeStorageClasses{c}
}
func (c *FakeStorageV1) VolumeAttachments() v1.VolumeAttachmentInterface {
return &FakeVolumeAttachments{c}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeStorageV1) RESTClient() rest.Interface {

View File

@@ -0,0 +1,131 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
storagev1 "k8s.io/api/storage/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeVolumeAttachments implements VolumeAttachmentInterface
type FakeVolumeAttachments struct {
Fake *FakeStorageV1
}
var volumeattachmentsResource = schema.GroupVersionResource{Group: "storage.k8s.io", Version: "v1", Resource: "volumeattachments"}
var volumeattachmentsKind = schema.GroupVersionKind{Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachment"}
// Get takes name of the volumeAttachment, and returns the corresponding volumeAttachment object, and an error if there is any.
func (c *FakeVolumeAttachments) Get(name string, options v1.GetOptions) (result *storagev1.VolumeAttachment, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootGetAction(volumeattachmentsResource, name), &storagev1.VolumeAttachment{})
if obj == nil {
return nil, err
}
return obj.(*storagev1.VolumeAttachment), err
}
// List takes label and field selectors, and returns the list of VolumeAttachments that match those selectors.
func (c *FakeVolumeAttachments) List(opts v1.ListOptions) (result *storagev1.VolumeAttachmentList, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootListAction(volumeattachmentsResource, volumeattachmentsKind, opts), &storagev1.VolumeAttachmentList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &storagev1.VolumeAttachmentList{ListMeta: obj.(*storagev1.VolumeAttachmentList).ListMeta}
for _, item := range obj.(*storagev1.VolumeAttachmentList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested volumeAttachments.
func (c *FakeVolumeAttachments) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewRootWatchAction(volumeattachmentsResource, opts))
}
// Create takes the representation of a volumeAttachment and creates it. Returns the server's representation of the volumeAttachment, and an error, if there is any.
func (c *FakeVolumeAttachments) Create(volumeAttachment *storagev1.VolumeAttachment) (result *storagev1.VolumeAttachment, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootCreateAction(volumeattachmentsResource, volumeAttachment), &storagev1.VolumeAttachment{})
if obj == nil {
return nil, err
}
return obj.(*storagev1.VolumeAttachment), err
}
// Update takes the representation of a volumeAttachment and updates it. Returns the server's representation of the volumeAttachment, and an error, if there is any.
func (c *FakeVolumeAttachments) Update(volumeAttachment *storagev1.VolumeAttachment) (result *storagev1.VolumeAttachment, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateAction(volumeattachmentsResource, volumeAttachment), &storagev1.VolumeAttachment{})
if obj == nil {
return nil, err
}
return obj.(*storagev1.VolumeAttachment), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeVolumeAttachments) UpdateStatus(volumeAttachment *storagev1.VolumeAttachment) (*storagev1.VolumeAttachment, error) {
obj, err := c.Fake.
Invokes(testing.NewRootUpdateSubresourceAction(volumeattachmentsResource, "status", volumeAttachment), &storagev1.VolumeAttachment{})
if obj == nil {
return nil, err
}
return obj.(*storagev1.VolumeAttachment), err
}
// Delete takes name of the volumeAttachment and deletes it. Returns an error if one occurs.
func (c *FakeVolumeAttachments) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewRootDeleteAction(volumeattachmentsResource, name), &storagev1.VolumeAttachment{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeVolumeAttachments) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewRootDeleteCollectionAction(volumeattachmentsResource, listOptions)
_, err := c.Fake.Invokes(action, &storagev1.VolumeAttachmentList{})
return err
}
// Patch applies the patch and returns the patched volumeAttachment.
func (c *FakeVolumeAttachments) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *storagev1.VolumeAttachment, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootPatchSubresourceAction(volumeattachmentsResource, name, pt, data, subresources...), &storagev1.VolumeAttachment{})
if obj == nil {
return nil, err
}
return obj.(*storagev1.VolumeAttachment), err
}

View File

@@ -19,3 +19,5 @@ limitations under the License.
package v1
type StorageClassExpansion interface{}
type VolumeAttachmentExpansion interface{}

View File

@@ -28,6 +28,7 @@ import (
type StorageV1Interface interface {
RESTClient() rest.Interface
StorageClassesGetter
VolumeAttachmentsGetter
}
// StorageV1Client is used to interact with features provided by the storage.k8s.io group.
@@ -39,6 +40,10 @@ func (c *StorageV1Client) StorageClasses() StorageClassInterface {
return newStorageClasses(c)
}
func (c *StorageV1Client) VolumeAttachments() VolumeAttachmentInterface {
return newVolumeAttachments(c)
}
// NewForConfig creates a new StorageV1Client for the given config.
func NewForConfig(c *rest.Config) (*StorageV1Client, error) {
config := *c

View File

@@ -0,0 +1,163 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
v1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
scheme "k8s.io/client-go/kubernetes/scheme"
rest "k8s.io/client-go/rest"
)
// VolumeAttachmentsGetter has a method to return a VolumeAttachmentInterface.
// A group's client should implement this interface.
type VolumeAttachmentsGetter interface {
VolumeAttachments() VolumeAttachmentInterface
}
// VolumeAttachmentInterface has methods to work with VolumeAttachment resources.
type VolumeAttachmentInterface interface {
Create(*v1.VolumeAttachment) (*v1.VolumeAttachment, error)
Update(*v1.VolumeAttachment) (*v1.VolumeAttachment, error)
UpdateStatus(*v1.VolumeAttachment) (*v1.VolumeAttachment, error)
Delete(name string, options *metav1.DeleteOptions) error
DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
Get(name string, options metav1.GetOptions) (*v1.VolumeAttachment, error)
List(opts metav1.ListOptions) (*v1.VolumeAttachmentList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.VolumeAttachment, err error)
VolumeAttachmentExpansion
}
// volumeAttachments implements VolumeAttachmentInterface
type volumeAttachments struct {
client rest.Interface
}
// newVolumeAttachments returns a VolumeAttachments
func newVolumeAttachments(c *StorageV1Client) *volumeAttachments {
return &volumeAttachments{
client: c.RESTClient(),
}
}
// Get takes name of the volumeAttachment, and returns the corresponding volumeAttachment object, and an error if there is any.
func (c *volumeAttachments) Get(name string, options metav1.GetOptions) (result *v1.VolumeAttachment, err error) {
result = &v1.VolumeAttachment{}
err = c.client.Get().
Resource("volumeattachments").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of VolumeAttachments that match those selectors.
func (c *volumeAttachments) List(opts metav1.ListOptions) (result *v1.VolumeAttachmentList, err error) {
result = &v1.VolumeAttachmentList{}
err = c.client.Get().
Resource("volumeattachments").
VersionedParams(&opts, scheme.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested volumeAttachments.
func (c *volumeAttachments) Watch(opts metav1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Resource("volumeattachments").
VersionedParams(&opts, scheme.ParameterCodec).
Watch()
}
// Create takes the representation of a volumeAttachment and creates it. Returns the server's representation of the volumeAttachment, and an error, if there is any.
func (c *volumeAttachments) Create(volumeAttachment *v1.VolumeAttachment) (result *v1.VolumeAttachment, err error) {
result = &v1.VolumeAttachment{}
err = c.client.Post().
Resource("volumeattachments").
Body(volumeAttachment).
Do().
Into(result)
return
}
// Update takes the representation of a volumeAttachment and updates it. Returns the server's representation of the volumeAttachment, and an error, if there is any.
func (c *volumeAttachments) Update(volumeAttachment *v1.VolumeAttachment) (result *v1.VolumeAttachment, err error) {
result = &v1.VolumeAttachment{}
err = c.client.Put().
Resource("volumeattachments").
Name(volumeAttachment.Name).
Body(volumeAttachment).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *volumeAttachments) UpdateStatus(volumeAttachment *v1.VolumeAttachment) (result *v1.VolumeAttachment, err error) {
result = &v1.VolumeAttachment{}
err = c.client.Put().
Resource("volumeattachments").
Name(volumeAttachment.Name).
SubResource("status").
Body(volumeAttachment).
Do().
Into(result)
return
}
// Delete takes name of the volumeAttachment and deletes it. Returns an error if one occurs.
func (c *volumeAttachments) Delete(name string, options *metav1.DeleteOptions) error {
return c.client.Delete().
Resource("volumeattachments").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *volumeAttachments) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
return c.client.Delete().
Resource("volumeattachments").
VersionedParams(&listOptions, scheme.ParameterCodec).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched volumeAttachment.
func (c *volumeAttachments) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.VolumeAttachment, err error) {
result = &v1.VolumeAttachment{}
err = c.client.Patch(pt).
Resource("volumeattachments").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@@ -10,6 +10,7 @@ go_library(
srcs = [
"expansion_generated.go",
"storageclass.go",
"volumeattachment.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/client-go/listers/storage/v1",
importpath = "k8s.io/client-go/listers/storage/v1",

View File

@@ -21,3 +21,7 @@ package v1
// StorageClassListerExpansion allows custom methods to be added to
// StorageClassLister.
type StorageClassListerExpansion interface{}
// VolumeAttachmentListerExpansion allows custom methods to be added to
// VolumeAttachmentLister.
type VolumeAttachmentListerExpansion interface{}

View File

@@ -0,0 +1,65 @@
/*
Copyright 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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
import (
v1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// VolumeAttachmentLister helps list VolumeAttachments.
type VolumeAttachmentLister interface {
// List lists all VolumeAttachments in the indexer.
List(selector labels.Selector) (ret []*v1.VolumeAttachment, err error)
// Get retrieves the VolumeAttachment from the index for a given name.
Get(name string) (*v1.VolumeAttachment, error)
VolumeAttachmentListerExpansion
}
// volumeAttachmentLister implements the VolumeAttachmentLister interface.
type volumeAttachmentLister struct {
indexer cache.Indexer
}
// NewVolumeAttachmentLister returns a new VolumeAttachmentLister.
func NewVolumeAttachmentLister(indexer cache.Indexer) VolumeAttachmentLister {
return &volumeAttachmentLister{indexer: indexer}
}
// List lists all VolumeAttachments in the indexer.
func (s *volumeAttachmentLister) List(selector labels.Selector) (ret []*v1.VolumeAttachment, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1.VolumeAttachment))
})
return ret, err
}
// Get retrieves the VolumeAttachment from the index for a given name.
func (s *volumeAttachmentLister) Get(name string) (*v1.VolumeAttachment, error) {
obj, exists, err := s.indexer.GetByKey(name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1.Resource("volumeattachment"), name)
}
return obj.(*v1.VolumeAttachment), nil
}

View File

@@ -291,6 +291,14 @@ func GetEtcdStorageData() map[schema.GroupVersionResource]StorageData {
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1
gvr("storage.k8s.io", "v1", "volumeattachments"): {
Stub: `{"metadata": {"name": "va3"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv3"}}}`,
ExpectedEtcdPath: "/registry/volumeattachments/va3",
ExpectedGVK: gvkP("storage.k8s.io", "v1beta1", "VolumeAttachment"),
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1beta1
gvr("storage.k8s.io", "v1beta1", "storageclasses"): {
Stub: `{"metadata": {"name": "sc1"}, "provisioner": "aws"}`,