2019-07-25 14:44:08 +00:00
|
|
|
package structs
|
|
|
|
|
|
|
|
const (
|
|
|
|
VolumeTypeHost = "host"
|
|
|
|
)
|
|
|
|
|
volumes: Add support for mount propagation
This commit introduces support for configuring mount propagation when
mounting volumes with the `volume_mount` stanza on Linux targets.
Similar to Kubernetes, we expose 3 options for configuring mount
propagation:
- private, which is equivalent to `rprivate` on Linux, which does not allow the
container to see any new nested mounts after the chroot was created.
- host-to-task, which is equivalent to `rslave` on Linux, which allows new mounts
that have been created _outside of the container_ to be visible
inside the container after the chroot is created.
- bidirectional, which is equivalent to `rshared` on Linux, which allows both
the container to see new mounts created on the host, but
importantly _allows the container to create mounts that are
visible in other containers an don the host_
private and host-to-task are safe, but bidirectional mounts can be
dangerous, as if the code inside a container creates a mount, and does
not clean it up before tearing down the container, it can cause bad
things to happen inside the kernel.
To add a layer of safety here, we require that the user has ReadWrite
permissions on the volume before allowing bidirectional mounts, as a
defense in depth / validation case, although creating mounts should also require
a priviliged execution environment inside the container.
2019-09-13 21:13:20 +00:00
|
|
|
const (
|
|
|
|
VolumeMountPropagationPrivate = "private"
|
|
|
|
VolumeMountPropagationHostToTask = "host-to-task"
|
|
|
|
VolumeMountPropagationBidirectional = "bidirectional"
|
|
|
|
)
|
|
|
|
|
|
|
|
func MountPropagationModeIsValid(propagationMode string) bool {
|
|
|
|
switch propagationMode {
|
|
|
|
case "", VolumeMountPropagationPrivate, VolumeMountPropagationHostToTask, VolumeMountPropagationBidirectional:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-25 14:44:08 +00:00
|
|
|
// ClientHostVolumeConfig is used to configure access to host paths on a Nomad Client
|
|
|
|
type ClientHostVolumeConfig struct {
|
|
|
|
Name string `hcl:",key"`
|
2019-08-01 09:55:42 +00:00
|
|
|
Path string `hcl:"path"`
|
2019-07-25 14:44:08 +00:00
|
|
|
ReadOnly bool `hcl:"read_only"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *ClientHostVolumeConfig) Copy() *ClientHostVolumeConfig {
|
|
|
|
if p == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
c := new(ClientHostVolumeConfig)
|
|
|
|
*c = *p
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func CopyMapStringClientHostVolumeConfig(m map[string]*ClientHostVolumeConfig) map[string]*ClientHostVolumeConfig {
|
|
|
|
if m == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
nm := make(map[string]*ClientHostVolumeConfig, len(m))
|
|
|
|
for k, v := range m {
|
|
|
|
nm[k] = v.Copy()
|
|
|
|
}
|
|
|
|
|
|
|
|
return nm
|
|
|
|
}
|
|
|
|
|
2019-08-01 09:33:26 +00:00
|
|
|
func CopySliceClientHostVolumeConfig(s []*ClientHostVolumeConfig) []*ClientHostVolumeConfig {
|
|
|
|
l := len(s)
|
|
|
|
if l == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ns := make([]*ClientHostVolumeConfig, l)
|
|
|
|
for idx, cfg := range s {
|
|
|
|
ns[idx] = cfg.Copy()
|
|
|
|
}
|
|
|
|
|
|
|
|
return ns
|
|
|
|
}
|
|
|
|
|
2019-07-25 14:44:08 +00:00
|
|
|
func HostVolumeSliceMerge(a, b []*ClientHostVolumeConfig) []*ClientHostVolumeConfig {
|
|
|
|
n := make([]*ClientHostVolumeConfig, len(a))
|
2019-08-01 09:33:26 +00:00
|
|
|
seenKeys := make(map[string]int, len(a))
|
2019-07-25 14:44:08 +00:00
|
|
|
|
2019-08-01 09:33:26 +00:00
|
|
|
for i, config := range a {
|
|
|
|
n[i] = config.Copy()
|
|
|
|
seenKeys[config.Name] = i
|
2019-07-25 14:44:08 +00:00
|
|
|
}
|
2019-08-01 09:33:26 +00:00
|
|
|
|
|
|
|
for _, config := range b {
|
|
|
|
if fIndex, ok := seenKeys[config.Name]; ok {
|
|
|
|
n[fIndex] = config.Copy()
|
2019-07-25 14:44:08 +00:00
|
|
|
continue
|
|
|
|
}
|
2019-08-01 09:33:26 +00:00
|
|
|
|
|
|
|
n = append(n, config.Copy())
|
2019-07-25 14:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2019-08-01 09:33:26 +00:00
|
|
|
// VolumeRequest is a representation of a storage volume that a TaskGroup wishes to use.
|
|
|
|
type VolumeRequest struct {
|
2019-07-25 14:44:08 +00:00
|
|
|
Name string
|
|
|
|
Type string
|
config: Hoist volume.config.source into volume
Currently, using a Volume in a job uses the following configuration:
```
volume "alias-name" {
type = "volume-type"
read_only = true
config {
source = "host_volume_name"
}
}
```
This commit migrates to the following:
```
volume "alias-name" {
type = "volume-type"
source = "host_volume_name"
read_only = true
}
```
The original design was based due to being uncertain about the future of storage
plugins, and to allow maxium flexibility.
However, this causes a few issues, namely:
- We frequently need to parse this configuration during submission,
scheduling, and mounting
- It complicates the configuration from and end users perspective
- It complicates the ability to do validation
As we understand the problem space of CSI a little more, it has become
clear that we won't need the `source` to be in config, as it will be
used in the majority of cases:
- Host Volumes: Always need a source
- Preallocated CSI Volumes: Always needs a source from a volume or claim name
- Dynamic Persistent CSI Volumes*: Always needs a source to attach the volumes
to for managing upgrades and to avoid dangling.
- Dynamic Ephemeral CSI Volumes*: Less thought out, but `source` will probably point
to the plugin name, and a `config` block will
allow you to pass meta to the plugin. Or will
point to a pre-configured ephemeral config.
*If implemented
The new design simplifies this by merging the source into the volume
stanza to solve the above issues with usability, performance, and error
handling.
2019-09-13 02:09:58 +00:00
|
|
|
Source string
|
2019-07-25 14:44:08 +00:00
|
|
|
ReadOnly bool
|
|
|
|
}
|
|
|
|
|
2019-08-01 09:33:26 +00:00
|
|
|
func (v *VolumeRequest) Copy() *VolumeRequest {
|
2019-07-25 14:44:08 +00:00
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2019-08-01 09:33:26 +00:00
|
|
|
nv := new(VolumeRequest)
|
2019-07-25 14:44:08 +00:00
|
|
|
*nv = *v
|
|
|
|
|
|
|
|
return nv
|
|
|
|
}
|
|
|
|
|
2019-08-01 09:33:26 +00:00
|
|
|
func CopyMapVolumeRequest(s map[string]*VolumeRequest) map[string]*VolumeRequest {
|
2019-07-25 14:44:08 +00:00
|
|
|
if s == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
l := len(s)
|
2019-08-01 09:33:26 +00:00
|
|
|
c := make(map[string]*VolumeRequest, l)
|
2019-07-25 14:44:08 +00:00
|
|
|
for k, v := range s {
|
|
|
|
c[k] = v.Copy()
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2019-08-05 11:33:02 +00:00
|
|
|
// VolumeMount represents the relationship between a destination path in a task
|
|
|
|
// and the task group volume that should be mounted there.
|
2019-07-25 14:44:08 +00:00
|
|
|
type VolumeMount struct {
|
volumes: Add support for mount propagation
This commit introduces support for configuring mount propagation when
mounting volumes with the `volume_mount` stanza on Linux targets.
Similar to Kubernetes, we expose 3 options for configuring mount
propagation:
- private, which is equivalent to `rprivate` on Linux, which does not allow the
container to see any new nested mounts after the chroot was created.
- host-to-task, which is equivalent to `rslave` on Linux, which allows new mounts
that have been created _outside of the container_ to be visible
inside the container after the chroot is created.
- bidirectional, which is equivalent to `rshared` on Linux, which allows both
the container to see new mounts created on the host, but
importantly _allows the container to create mounts that are
visible in other containers an don the host_
private and host-to-task are safe, but bidirectional mounts can be
dangerous, as if the code inside a container creates a mount, and does
not clean it up before tearing down the container, it can cause bad
things to happen inside the kernel.
To add a layer of safety here, we require that the user has ReadWrite
permissions on the volume before allowing bidirectional mounts, as a
defense in depth / validation case, although creating mounts should also require
a priviliged execution environment inside the container.
2019-09-13 21:13:20 +00:00
|
|
|
Volume string
|
|
|
|
Destination string
|
|
|
|
ReadOnly bool
|
|
|
|
PropagationMode string
|
2019-07-25 14:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *VolumeMount) Copy() *VolumeMount {
|
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
nv := new(VolumeMount)
|
|
|
|
*nv = *v
|
|
|
|
return nv
|
|
|
|
}
|
|
|
|
|
|
|
|
func CopySliceVolumeMount(s []*VolumeMount) []*VolumeMount {
|
|
|
|
l := len(s)
|
|
|
|
if l == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
c := make([]*VolumeMount, l)
|
|
|
|
for i, v := range s {
|
|
|
|
c[i] = v.Copy()
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|