mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Merge pull request #22575 from MikaelCluseau/wip-issue-20466
Automatic merge from submit-queue Add subPath to mount a child dir or file of a volumeMount Allow users to specify a subPath in Container.volumeMounts so they can use a single volume for many mounts instead of creating many volumes. For instance, a user can now use a single PersistentVolume to store the Mysql database and the document root of an Apache server of a LAMP stack pod by mapping them to different subPaths in this single volume. Also solves https://github.com/kubernetes/kubernetes/issues/20466.
This commit is contained in:
commit
f2f3b49f58
@ -2031,6 +2031,10 @@
|
||||
"mountPath": {
|
||||
"type": "string",
|
||||
"description": "Path within the container at which the volume should be mounted. Must not contain ':'."
|
||||
},
|
||||
"subPath": {
|
||||
"type": "string",
|
||||
"description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root)."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2036,6 +2036,10 @@
|
||||
"mountPath": {
|
||||
"type": "string",
|
||||
"description": "Path within the container at which the volume should be mounted. Must not contain ':'."
|
||||
},
|
||||
"subPath": {
|
||||
"type": "string",
|
||||
"description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root)."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -7343,6 +7343,10 @@
|
||||
"mountPath": {
|
||||
"type": "string",
|
||||
"description": "Path within the container at which the volume should be mounted. Must not contain ':'."
|
||||
},
|
||||
"subPath": {
|
||||
"type": "string",
|
||||
"description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root)."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -17330,6 +17330,10 @@
|
||||
"mountPath": {
|
||||
"type": "string",
|
||||
"description": "Path within the container at which the volume should be mounted. Must not contain ':'."
|
||||
},
|
||||
"subPath": {
|
||||
"type": "string",
|
||||
"description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root)."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -556,6 +556,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
||||
<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">subPath</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Path within the volume from which the container’s volume should be mounted. Defaults to "" (volume’s root).</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>
|
||||
|
||||
@ -3972,7 +3979,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-05-02 18:47:09 UTC
|
||||
Last updated 2016-05-06 04:28:34 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -770,6 +770,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
||||
<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">subPath</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Path within the volume from which the container’s volume should be mounted. Defaults to "" (volume’s root).</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>
|
||||
|
||||
@ -5962,7 +5969,7 @@ Both these may change in the future. Incoming requests are matched against the h
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-05-02 18:47:04 UTC
|
||||
Last updated 2016-05-06 04:28:25 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -803,6 +803,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
||||
<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">subPath</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Path within the volume from which the container’s volume should be mounted. Defaults to "" (volume’s root).</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>
|
||||
|
||||
@ -7839,7 +7846,7 @@ The resulting set of endpoints can be viewed as:<br>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2016-05-02 18:46:58 UTC
|
||||
Last updated 2016-05-06 04:28:14 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -92,3 +92,4 @@ test/e2e/host_path.go: fmt.Sprintf("--retry_time=%d", retryDuration),
|
||||
test/images/mount-tester/mt.go: flag.BoolVar(&breakOnExpectedContent, "break_on_expected_content", true, "Break out of loop on expected content, (use with --file_content_in_loop flag only)")
|
||||
test/images/mount-tester/mt.go: flag.IntVar(&retryDuration, "retry_time", 180, "Retry time during the loop")
|
||||
test/images/mount-tester/mt.go: flag.StringVar(&readFileContentInLoopPath, "file_content_in_loop", "", "Path to read the file content in loop from")
|
||||
test/e2e/host_path.go: fmt.Sprintf("--file_content_in_loop=%v", filePathInReader),
|
||||
|
@ -2955,6 +2955,7 @@ func DeepCopy_api_VolumeMount(in VolumeMount, out *VolumeMount, c *conversion.Cl
|
||||
out.Name = in.Name
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.MountPath = in.MountPath
|
||||
out.SubPath = in.SubPath
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -14340,13 +14340,14 @@ func (x *VolumeMount) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [3]bool
|
||||
var yyq2 [4]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[1] = x.ReadOnly != false
|
||||
yyq2[3] = x.SubPath != ""
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(3)
|
||||
r.EncodeArrayStart(4)
|
||||
} else {
|
||||
yynn2 = 2
|
||||
for _, b := range yyq2 {
|
||||
@ -14420,6 +14421,31 @@ func (x *VolumeMount) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.MountPath))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[3] {
|
||||
yym13 := z.EncBinary()
|
||||
_ = yym13
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.SubPath))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[3] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("subPath"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym14 := z.EncBinary()
|
||||
_ = yym14
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.SubPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
@ -14499,6 +14525,12 @@ func (x *VolumeMount) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
} else {
|
||||
x.MountPath = string(r.DecodeString())
|
||||
}
|
||||
case "subPath":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.SubPath = ""
|
||||
} else {
|
||||
x.SubPath = string(r.DecodeString())
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
} // end switch yys3
|
||||
@ -14510,16 +14542,16 @@ func (x *VolumeMount) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj7 int
|
||||
var yyb7 bool
|
||||
var yyhl7 bool = l >= 0
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
var yyj8 int
|
||||
var yyb8 bool
|
||||
var yyhl8 bool = l >= 0
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -14529,13 +14561,13 @@ func (x *VolumeMount) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
} else {
|
||||
x.Name = string(r.DecodeString())
|
||||
}
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -14545,13 +14577,13 @@ func (x *VolumeMount) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
} else {
|
||||
x.ReadOnly = bool(r.DecodeBool())
|
||||
}
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -14561,18 +14593,34 @@ func (x *VolumeMount) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
} else {
|
||||
x.MountPath = string(r.DecodeString())
|
||||
}
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.SubPath = ""
|
||||
} else {
|
||||
x.SubPath = string(r.DecodeString())
|
||||
}
|
||||
for {
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
if yyb8 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj7-1, "")
|
||||
z.DecStructFieldNotFound(yyj8-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
@ -52291,7 +52339,7 @@ func (x codecSelfer1234) decSliceVolumeMount(v *[]VolumeMount, d *codec1978.Deco
|
||||
|
||||
yyrg1 := len(yyv1) > 0
|
||||
yyv21 := yyv1
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 40)
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 56)
|
||||
if yyrt1 {
|
||||
if yyrl1 <= cap(yyv1) {
|
||||
yyv1 = yyv1[:yyrl1]
|
||||
|
@ -758,6 +758,9 @@ type VolumeMount struct {
|
||||
ReadOnly bool `json:"readOnly,omitempty"`
|
||||
// Required. Must not contain ':'.
|
||||
MountPath string `json:"mountPath"`
|
||||
// Path within the volume from which the container's volume should be mounted.
|
||||
// Defaults to "" (volume's root).
|
||||
SubPath string `json:"subPath,omitempty"`
|
||||
}
|
||||
|
||||
// EnvVar represents an environment variable present in a Container.
|
||||
|
@ -6556,6 +6556,7 @@ func autoConvert_v1_VolumeMount_To_api_VolumeMount(in *VolumeMount, out *api.Vol
|
||||
out.Name = in.Name
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.MountPath = in.MountPath
|
||||
out.SubPath = in.SubPath
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -6567,6 +6568,7 @@ func autoConvert_api_VolumeMount_To_v1_VolumeMount(in *api.VolumeMount, out *Vol
|
||||
out.Name = in.Name
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.MountPath = in.MountPath
|
||||
out.SubPath = in.SubPath
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -2916,6 +2916,7 @@ func DeepCopy_v1_VolumeMount(in VolumeMount, out *VolumeMount, c *conversion.Clo
|
||||
out.Name = in.Name
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.MountPath = in.MountPath
|
||||
out.SubPath = in.SubPath
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -7164,6 +7164,10 @@ func (m *VolumeMount) MarshalTo(data []byte) (int, error) {
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.MountPath)))
|
||||
i += copy(data[i:], m.MountPath)
|
||||
data[i] = 0x22
|
||||
i++
|
||||
i = encodeVarintGenerated(data, i, uint64(len(m.SubPath)))
|
||||
i += copy(data[i:], m.SubPath)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@ -9726,6 +9730,8 @@ func (m *VolumeMount) Size() (n int) {
|
||||
n += 2
|
||||
l = len(m.MountPath)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
l = len(m.SubPath)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
||||
@ -32282,6 +32288,35 @@ func (m *VolumeMount) Unmarshal(data []byte) error {
|
||||
}
|
||||
m.MountPath = string(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field SubPath", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.SubPath = string(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(data[iNdEx:])
|
||||
|
@ -2705,6 +2705,10 @@ message VolumeMount {
|
||||
// Path within the container at which the volume should be mounted. Must
|
||||
// not contain ':'.
|
||||
optional string mountPath = 3;
|
||||
|
||||
// Path within the volume from which the container's volume should be mounted.
|
||||
// Defaults to "" (volume's root).
|
||||
optional string subPath = 4;
|
||||
}
|
||||
|
||||
// Represents the source of a volume to mount.
|
||||
|
@ -13943,13 +13943,14 @@ func (x *VolumeMount) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
} else {
|
||||
yysep2 := !z.EncBinary()
|
||||
yy2arr2 := z.EncBasicHandle().StructToArray
|
||||
var yyq2 [3]bool
|
||||
var yyq2 [4]bool
|
||||
_, _, _ = yysep2, yyq2, yy2arr2
|
||||
const yyr2 bool = false
|
||||
yyq2[1] = x.ReadOnly != false
|
||||
yyq2[3] = x.SubPath != ""
|
||||
var yynn2 int
|
||||
if yyr2 || yy2arr2 {
|
||||
r.EncodeArrayStart(3)
|
||||
r.EncodeArrayStart(4)
|
||||
} else {
|
||||
yynn2 = 2
|
||||
for _, b := range yyq2 {
|
||||
@ -14023,6 +14024,31 @@ func (x *VolumeMount) CodecEncodeSelf(e *codec1978.Encoder) {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.MountPath))
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if yyq2[3] {
|
||||
yym13 := z.EncBinary()
|
||||
_ = yym13
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.SubPath))
|
||||
}
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, "")
|
||||
}
|
||||
} else {
|
||||
if yyq2[3] {
|
||||
z.EncSendContainerState(codecSelfer_containerMapKey1234)
|
||||
r.EncodeString(codecSelferC_UTF81234, string("subPath"))
|
||||
z.EncSendContainerState(codecSelfer_containerMapValue1234)
|
||||
yym14 := z.EncBinary()
|
||||
_ = yym14
|
||||
if false {
|
||||
} else {
|
||||
r.EncodeString(codecSelferC_UTF81234, string(x.SubPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
if yyr2 || yy2arr2 {
|
||||
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
} else {
|
||||
@ -14102,6 +14128,12 @@ func (x *VolumeMount) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
|
||||
} else {
|
||||
x.MountPath = string(r.DecodeString())
|
||||
}
|
||||
case "subPath":
|
||||
if r.TryDecodeAsNil() {
|
||||
x.SubPath = ""
|
||||
} else {
|
||||
x.SubPath = string(r.DecodeString())
|
||||
}
|
||||
default:
|
||||
z.DecStructFieldNotFound(-1, yys3)
|
||||
} // end switch yys3
|
||||
@ -14113,16 +14145,16 @@ func (x *VolumeMount) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
var h codecSelfer1234
|
||||
z, r := codec1978.GenHelperDecoder(d)
|
||||
_, _, _ = h, z, r
|
||||
var yyj7 int
|
||||
var yyb7 bool
|
||||
var yyhl7 bool = l >= 0
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
var yyj8 int
|
||||
var yyb8 bool
|
||||
var yyhl8 bool = l >= 0
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -14132,13 +14164,13 @@ func (x *VolumeMount) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
} else {
|
||||
x.Name = string(r.DecodeString())
|
||||
}
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -14148,13 +14180,13 @@ func (x *VolumeMount) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
} else {
|
||||
x.ReadOnly = bool(r.DecodeBool())
|
||||
}
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
@ -14164,18 +14196,34 @@ func (x *VolumeMount) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
|
||||
} else {
|
||||
x.MountPath = string(r.DecodeString())
|
||||
}
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb8 {
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
return
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
if r.TryDecodeAsNil() {
|
||||
x.SubPath = ""
|
||||
} else {
|
||||
x.SubPath = string(r.DecodeString())
|
||||
}
|
||||
for {
|
||||
yyj7++
|
||||
if yyhl7 {
|
||||
yyb7 = yyj7 > l
|
||||
yyj8++
|
||||
if yyhl8 {
|
||||
yyb8 = yyj8 > l
|
||||
} else {
|
||||
yyb7 = r.CheckBreak()
|
||||
yyb8 = r.CheckBreak()
|
||||
}
|
||||
if yyb7 {
|
||||
if yyb8 {
|
||||
break
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
|
||||
z.DecStructFieldNotFound(yyj7-1, "")
|
||||
z.DecStructFieldNotFound(yyj8-1, "")
|
||||
}
|
||||
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
|
||||
}
|
||||
@ -52344,7 +52392,7 @@ func (x codecSelfer1234) decSliceVolumeMount(v *[]VolumeMount, d *codec1978.Deco
|
||||
|
||||
yyrg1 := len(yyv1) > 0
|
||||
yyv21 := yyv1
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 40)
|
||||
yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 56)
|
||||
if yyrt1 {
|
||||
if yyrl1 <= cap(yyv1) {
|
||||
yyv1 = yyv1[:yyrl1]
|
||||
|
@ -883,6 +883,9 @@ type VolumeMount struct {
|
||||
// Path within the container at which the volume should be mounted. Must
|
||||
// not contain ':'.
|
||||
MountPath string `json:"mountPath" protobuf:"bytes,3,opt,name=mountPath"`
|
||||
// Path within the volume from which the container's volume should be mounted.
|
||||
// Defaults to "" (volume's root).
|
||||
SubPath string `json:"subPath,omitempty" protobuf:"bytes,4,opt,name=subPath"`
|
||||
}
|
||||
|
||||
// EnvVar represents an environment variable present in a Container.
|
||||
|
@ -1628,6 +1628,7 @@ var map_VolumeMount = map[string]string{
|
||||
"name": "This must match the Name of a Volume.",
|
||||
"readOnly": "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.",
|
||||
"mountPath": "Path within the container at which the volume should be mounted. Must not contain ':'.",
|
||||
"subPath": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).",
|
||||
}
|
||||
|
||||
func (VolumeMount) SwaggerDoc() map[string]string {
|
||||
|
@ -727,6 +727,28 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSou
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// This validate will make sure targetPath:
|
||||
// 1. is not abs path
|
||||
// 2. does not start with '../'
|
||||
// 3. does not contain '/../'
|
||||
// 4. does not end with '/..'
|
||||
func validateSubPath(targetPath string, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if path.IsAbs(targetPath) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
|
||||
}
|
||||
if strings.HasPrefix(targetPath, "../") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not start with '../'"))
|
||||
}
|
||||
if strings.Contains(targetPath, "/../") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '/../'"))
|
||||
}
|
||||
if strings.HasSuffix(targetPath, "/..") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not end with '/..'"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// This validate will make sure targetPath:
|
||||
// 1. is not abs path
|
||||
// 2. does not contain '..'
|
||||
@ -1149,6 +1171,9 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, fldPath
|
||||
allErrs = append(allErrs, field.Invalid(idxPath.Child("mountPath"), mnt.MountPath, "must be unique"))
|
||||
}
|
||||
mountpoints.Insert(mnt.MountPath)
|
||||
if len(mnt.SubPath) > 0 {
|
||||
allErrs = append(allErrs, validateSubPath(mnt.SubPath, fldPath.Child("subPath"))...)
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
@ -1213,6 +1213,10 @@ func TestValidateVolumeMounts(t *testing.T) {
|
||||
{Name: "abc", MountPath: "/foo"},
|
||||
{Name: "123", MountPath: "/bar"},
|
||||
{Name: "abc-123", MountPath: "/baz"},
|
||||
{Name: "abc-123", MountPath: "/baa", SubPath: ""},
|
||||
{Name: "abc-123", MountPath: "/bab", SubPath: "baz"},
|
||||
{Name: "abc-123", MountPath: "/bac", SubPath: ".baz"},
|
||||
{Name: "abc-123", MountPath: "/bad", SubPath: "..baz"},
|
||||
}
|
||||
if errs := validateVolumeMounts(successCase, volumes, field.NewPath("field")); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
@ -1224,6 +1228,10 @@ func TestValidateVolumeMounts(t *testing.T) {
|
||||
"empty mountpath": {{Name: "abc", MountPath: ""}},
|
||||
"colon mountpath": {{Name: "abc", MountPath: "foo:bar"}},
|
||||
"mountpath collision": {{Name: "foo", MountPath: "/path/a"}, {Name: "bar", MountPath: "/path/a"}},
|
||||
"absolute subpath": {{Name: "abc", MountPath: "/bar", SubPath: "/baz"}},
|
||||
"subpath in ..": {{Name: "abc", MountPath: "/bar", SubPath: "../baz"}},
|
||||
"subpath contains ..": {{Name: "abc", MountPath: "/bar", SubPath: "baz/../bat"}},
|
||||
"subpath ends in ..": {{Name: "abc", MountPath: "/bar", SubPath: "./.."}},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
if errs := validateVolumeMounts(v, volumes, field.NewPath("field")); len(errs) == 0 {
|
||||
|
@ -1187,10 +1187,14 @@ func makeMounts(pod *api.Pod, podDir string, container *api.Container, hostName,
|
||||
vol.SELinuxLabeled = true
|
||||
relabelVolume = true
|
||||
}
|
||||
hostPath := vol.Mounter.GetPath()
|
||||
if mount.SubPath != "" {
|
||||
hostPath = filepath.Join(hostPath, mount.SubPath)
|
||||
}
|
||||
mounts = append(mounts, kubecontainer.Mount{
|
||||
Name: mount.Name,
|
||||
ContainerPath: mount.MountPath,
|
||||
HostPath: vol.Mounter.GetPath(),
|
||||
HostPath: hostPath,
|
||||
ReadOnly: mount.ReadOnly,
|
||||
SELinuxRelabel: relabelVolume,
|
||||
})
|
||||
|
@ -88,6 +88,37 @@ var _ = framework.KubeDescribe("hostPath", func() {
|
||||
}, namespace.Name,
|
||||
)
|
||||
})
|
||||
|
||||
It("should support subPath [Conformance]", func() {
|
||||
volumePath := "/test-volume"
|
||||
subPath := "sub-path"
|
||||
fileName := "test-file"
|
||||
retryDuration := 180
|
||||
|
||||
filePathInWriter := path.Join(volumePath, fileName)
|
||||
filePathInReader := path.Join(volumePath, subPath, fileName)
|
||||
|
||||
source := &api.HostPathVolumeSource{
|
||||
Path: "/tmp",
|
||||
}
|
||||
pod := testPodWithHostVol(volumePath, source)
|
||||
// Write the file in the subPath from container 0
|
||||
container := &pod.Spec.Containers[0]
|
||||
container.VolumeMounts[0].SubPath = subPath
|
||||
container.Args = []string{
|
||||
fmt.Sprintf("--new_file_0644=%v", filePathInWriter),
|
||||
fmt.Sprintf("--file_mode=%v", filePathInWriter),
|
||||
}
|
||||
// Read it from outside the subPath from container 1
|
||||
pod.Spec.Containers[1].Args = []string{
|
||||
fmt.Sprintf("--file_content_in_loop=%v", filePathInReader),
|
||||
fmt.Sprintf("--retry_time=%d", retryDuration),
|
||||
}
|
||||
|
||||
framework.TestContainerOutput("hostPath subPath", c, pod, 1, []string{
|
||||
"content of file \"" + filePathInReader + "\": mount-tester new file",
|
||||
}, namespace.Name)
|
||||
})
|
||||
})
|
||||
|
||||
//These constants are borrowed from the other test.
|
||||
|
Loading…
Reference in New Issue
Block a user