1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/daemon/cluster/convert/container.go
Drew Erny 6f1d7ddfa4 Use Runtime target
The Swarmkit api specifies a target for configs called called "Runtime"
which indicates that the config is not mounted into the container but
has some other use. This commit updates the Docker api to reflect this.

Signed-off-by: Drew Erny <drew.erny@docker.com>
2019-02-19 13:14:17 -06:00

466 lines
13 KiB
Go

package convert // import "github.com/docker/docker/daemon/cluster/convert"
import (
"fmt"
"strings"
"github.com/docker/docker/api/types/container"
mounttypes "github.com/docker/docker/api/types/mount"
types "github.com/docker/docker/api/types/swarm"
swarmapi "github.com/docker/swarmkit/api"
gogotypes "github.com/gogo/protobuf/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
if c == nil {
return nil
}
containerSpec := &types.ContainerSpec{
Image: c.Image,
Labels: c.Labels,
Command: c.Command,
Args: c.Args,
Hostname: c.Hostname,
Env: c.Env,
Dir: c.Dir,
User: c.User,
Groups: c.Groups,
StopSignal: c.StopSignal,
TTY: c.TTY,
OpenStdin: c.OpenStdin,
ReadOnly: c.ReadOnly,
Hosts: c.Hosts,
Secrets: secretReferencesFromGRPC(c.Secrets),
Configs: configReferencesFromGRPC(c.Configs),
Isolation: IsolationFromGRPC(c.Isolation),
Init: initFromGRPC(c.Init),
Sysctls: c.Sysctls,
}
if c.DNSConfig != nil {
containerSpec.DNSConfig = &types.DNSConfig{
Nameservers: c.DNSConfig.Nameservers,
Search: c.DNSConfig.Search,
Options: c.DNSConfig.Options,
}
}
// Privileges
if c.Privileges != nil {
containerSpec.Privileges = &types.Privileges{}
if c.Privileges.CredentialSpec != nil {
containerSpec.Privileges.CredentialSpec = credentialSpecFromGRPC(c.Privileges.CredentialSpec)
}
if c.Privileges.SELinuxContext != nil {
containerSpec.Privileges.SELinuxContext = &types.SELinuxContext{
Disable: c.Privileges.SELinuxContext.Disable,
User: c.Privileges.SELinuxContext.User,
Type: c.Privileges.SELinuxContext.Type,
Role: c.Privileges.SELinuxContext.Role,
Level: c.Privileges.SELinuxContext.Level,
}
}
}
// Mounts
for _, m := range c.Mounts {
mount := mounttypes.Mount{
Target: m.Target,
Source: m.Source,
Type: mounttypes.Type(strings.ToLower(swarmapi.Mount_MountType_name[int32(m.Type)])),
ReadOnly: m.ReadOnly,
}
if m.BindOptions != nil {
mount.BindOptions = &mounttypes.BindOptions{
Propagation: mounttypes.Propagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])),
}
}
if m.VolumeOptions != nil {
mount.VolumeOptions = &mounttypes.VolumeOptions{
NoCopy: m.VolumeOptions.NoCopy,
Labels: m.VolumeOptions.Labels,
}
if m.VolumeOptions.DriverConfig != nil {
mount.VolumeOptions.DriverConfig = &mounttypes.Driver{
Name: m.VolumeOptions.DriverConfig.Name,
Options: m.VolumeOptions.DriverConfig.Options,
}
}
}
if m.TmpfsOptions != nil {
mount.TmpfsOptions = &mounttypes.TmpfsOptions{
SizeBytes: m.TmpfsOptions.SizeBytes,
Mode: m.TmpfsOptions.Mode,
}
}
containerSpec.Mounts = append(containerSpec.Mounts, mount)
}
if c.StopGracePeriod != nil {
grace, _ := gogotypes.DurationFromProto(c.StopGracePeriod)
containerSpec.StopGracePeriod = &grace
}
if c.Healthcheck != nil {
containerSpec.Healthcheck = healthConfigFromGRPC(c.Healthcheck)
}
return containerSpec
}
func initFromGRPC(v *gogotypes.BoolValue) *bool {
if v == nil {
return nil
}
value := v.GetValue()
return &value
}
func initToGRPC(v *bool) *gogotypes.BoolValue {
if v == nil {
return nil
}
return &gogotypes.BoolValue{Value: *v}
}
func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
refs := make([]*swarmapi.SecretReference, 0, len(sr))
for _, s := range sr {
ref := &swarmapi.SecretReference{
SecretID: s.SecretID,
SecretName: s.SecretName,
}
if s.File != nil {
ref.Target = &swarmapi.SecretReference_File{
File: &swarmapi.FileTarget{
Name: s.File.Name,
UID: s.File.UID,
GID: s.File.GID,
Mode: s.File.Mode,
},
}
}
refs = append(refs, ref)
}
return refs
}
func secretReferencesFromGRPC(sr []*swarmapi.SecretReference) []*types.SecretReference {
refs := make([]*types.SecretReference, 0, len(sr))
for _, s := range sr {
target := s.GetFile()
if target == nil {
// not a file target
logrus.Warnf("secret target not a file: secret=%s", s.SecretID)
continue
}
refs = append(refs, &types.SecretReference{
File: &types.SecretReferenceFileTarget{
Name: target.Name,
UID: target.UID,
GID: target.GID,
Mode: target.Mode,
},
SecretID: s.SecretID,
SecretName: s.SecretName,
})
}
return refs
}
func configReferencesToGRPC(sr []*types.ConfigReference) ([]*swarmapi.ConfigReference, error) {
refs := make([]*swarmapi.ConfigReference, 0, len(sr))
for _, s := range sr {
ref := &swarmapi.ConfigReference{
ConfigID: s.ConfigID,
ConfigName: s.ConfigName,
}
switch {
case s.Runtime == nil && s.File == nil:
return nil, errors.New("either File or Runtime should be set")
case s.Runtime != nil && s.File != nil:
return nil, errors.New("cannot specify both File and Runtime")
case s.Runtime != nil:
// Runtime target was added in API v1.40 and takes precedence over
// File target. However, File and Runtime targets are mutually exclusive,
// so we should never have both.
ref.Target = &swarmapi.ConfigReference_Runtime{
Runtime: &swarmapi.RuntimeTarget{},
}
case s.File != nil:
ref.Target = &swarmapi.ConfigReference_File{
File: &swarmapi.FileTarget{
Name: s.File.Name,
UID: s.File.UID,
GID: s.File.GID,
Mode: s.File.Mode,
},
}
}
refs = append(refs, ref)
}
return refs, nil
}
func configReferencesFromGRPC(sr []*swarmapi.ConfigReference) []*types.ConfigReference {
refs := make([]*types.ConfigReference, 0, len(sr))
for _, s := range sr {
r := &types.ConfigReference{
ConfigID: s.ConfigID,
ConfigName: s.ConfigName,
}
if target := s.GetRuntime(); target != nil {
r.Runtime = &types.ConfigReferenceRuntimeTarget{}
} else if target := s.GetFile(); target != nil {
r.File = &types.ConfigReferenceFileTarget{
Name: target.Name,
UID: target.UID,
GID: target.GID,
Mode: target.Mode,
}
} else {
// not a file target
logrus.Warnf("config target not known: config=%s", s.ConfigID)
continue
}
refs = append(refs, r)
}
return refs
}
func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
containerSpec := &swarmapi.ContainerSpec{
Image: c.Image,
Labels: c.Labels,
Command: c.Command,
Args: c.Args,
Hostname: c.Hostname,
Env: c.Env,
Dir: c.Dir,
User: c.User,
Groups: c.Groups,
StopSignal: c.StopSignal,
TTY: c.TTY,
OpenStdin: c.OpenStdin,
ReadOnly: c.ReadOnly,
Hosts: c.Hosts,
Secrets: secretReferencesToGRPC(c.Secrets),
Isolation: isolationToGRPC(c.Isolation),
Init: initToGRPC(c.Init),
Sysctls: c.Sysctls,
}
if c.DNSConfig != nil {
containerSpec.DNSConfig = &swarmapi.ContainerSpec_DNSConfig{
Nameservers: c.DNSConfig.Nameservers,
Search: c.DNSConfig.Search,
Options: c.DNSConfig.Options,
}
}
if c.StopGracePeriod != nil {
containerSpec.StopGracePeriod = gogotypes.DurationProto(*c.StopGracePeriod)
}
// Privileges
if c.Privileges != nil {
containerSpec.Privileges = &swarmapi.Privileges{}
if c.Privileges.CredentialSpec != nil {
cs, err := credentialSpecToGRPC(c.Privileges.CredentialSpec)
if err != nil {
return nil, errors.Wrap(err, "invalid CredentialSpec")
}
containerSpec.Privileges.CredentialSpec = cs
}
if c.Privileges.SELinuxContext != nil {
containerSpec.Privileges.SELinuxContext = &swarmapi.Privileges_SELinuxContext{
Disable: c.Privileges.SELinuxContext.Disable,
User: c.Privileges.SELinuxContext.User,
Type: c.Privileges.SELinuxContext.Type,
Role: c.Privileges.SELinuxContext.Role,
Level: c.Privileges.SELinuxContext.Level,
}
}
}
if c.Configs != nil {
configs, err := configReferencesToGRPC(c.Configs)
if err != nil {
return nil, errors.Wrap(err, "invalid Config")
}
containerSpec.Configs = configs
}
// Mounts
for _, m := range c.Mounts {
mount := swarmapi.Mount{
Target: m.Target,
Source: m.Source,
ReadOnly: m.ReadOnly,
}
if mountType, ok := swarmapi.Mount_MountType_value[strings.ToUpper(string(m.Type))]; ok {
mount.Type = swarmapi.Mount_MountType(mountType)
} else if string(m.Type) != "" {
return nil, fmt.Errorf("invalid MountType: %q", m.Type)
}
if m.BindOptions != nil {
if mountPropagation, ok := swarmapi.Mount_BindOptions_MountPropagation_value[strings.ToUpper(string(m.BindOptions.Propagation))]; ok {
mount.BindOptions = &swarmapi.Mount_BindOptions{Propagation: swarmapi.Mount_BindOptions_MountPropagation(mountPropagation)}
} else if string(m.BindOptions.Propagation) != "" {
return nil, fmt.Errorf("invalid MountPropagation: %q", m.BindOptions.Propagation)
}
if m.BindOptions.NonRecursive {
// TODO(AkihiroSuda): NonRecursive is unsupported for Swarm-mode now because of mutual vendoring
// across moby and swarmkit. Will be available soon after the moby PR gets merged.
return nil, fmt.Errorf("invalid NonRecursive: %q", m.BindOptions.Propagation)
}
}
if m.VolumeOptions != nil {
mount.VolumeOptions = &swarmapi.Mount_VolumeOptions{
NoCopy: m.VolumeOptions.NoCopy,
Labels: m.VolumeOptions.Labels,
}
if m.VolumeOptions.DriverConfig != nil {
mount.VolumeOptions.DriverConfig = &swarmapi.Driver{
Name: m.VolumeOptions.DriverConfig.Name,
Options: m.VolumeOptions.DriverConfig.Options,
}
}
}
if m.TmpfsOptions != nil {
mount.TmpfsOptions = &swarmapi.Mount_TmpfsOptions{
SizeBytes: m.TmpfsOptions.SizeBytes,
Mode: m.TmpfsOptions.Mode,
}
}
containerSpec.Mounts = append(containerSpec.Mounts, mount)
}
if c.Healthcheck != nil {
containerSpec.Healthcheck = healthConfigToGRPC(c.Healthcheck)
}
return containerSpec, nil
}
func credentialSpecFromGRPC(c *swarmapi.Privileges_CredentialSpec) *types.CredentialSpec {
cs := &types.CredentialSpec{}
switch c.Source.(type) {
case *swarmapi.Privileges_CredentialSpec_Config:
cs.Config = c.GetConfig()
case *swarmapi.Privileges_CredentialSpec_File:
cs.File = c.GetFile()
case *swarmapi.Privileges_CredentialSpec_Registry:
cs.Registry = c.GetRegistry()
}
return cs
}
func credentialSpecToGRPC(c *types.CredentialSpec) (*swarmapi.Privileges_CredentialSpec, error) {
var opts []string
if c.Config != "" {
opts = append(opts, `"config"`)
}
if c.File != "" {
opts = append(opts, `"file"`)
}
if c.Registry != "" {
opts = append(opts, `"registry"`)
}
l := len(opts)
switch {
case l == 0:
return nil, errors.New(`must either provide "file", "registry", or "config" for credential spec`)
case l == 2:
return nil, fmt.Errorf("cannot specify both %s and %s credential specs", opts[0], opts[1])
case l > 2:
return nil, fmt.Errorf("cannot specify both %s, and %s credential specs", strings.Join(opts[:l-1], ", "), opts[l-1])
}
spec := &swarmapi.Privileges_CredentialSpec{}
switch {
case c.Config != "":
spec.Source = &swarmapi.Privileges_CredentialSpec_Config{
Config: c.Config,
}
case c.File != "":
spec.Source = &swarmapi.Privileges_CredentialSpec_File{
File: c.File,
}
case c.Registry != "":
spec.Source = &swarmapi.Privileges_CredentialSpec_Registry{
Registry: c.Registry,
}
}
return spec, nil
}
func healthConfigFromGRPC(h *swarmapi.HealthConfig) *container.HealthConfig {
interval, _ := gogotypes.DurationFromProto(h.Interval)
timeout, _ := gogotypes.DurationFromProto(h.Timeout)
startPeriod, _ := gogotypes.DurationFromProto(h.StartPeriod)
return &container.HealthConfig{
Test: h.Test,
Interval: interval,
Timeout: timeout,
Retries: int(h.Retries),
StartPeriod: startPeriod,
}
}
func healthConfigToGRPC(h *container.HealthConfig) *swarmapi.HealthConfig {
return &swarmapi.HealthConfig{
Test: h.Test,
Interval: gogotypes.DurationProto(h.Interval),
Timeout: gogotypes.DurationProto(h.Timeout),
Retries: int32(h.Retries),
StartPeriod: gogotypes.DurationProto(h.StartPeriod),
}
}
// IsolationFromGRPC converts a swarm api container isolation to a moby isolation representation
func IsolationFromGRPC(i swarmapi.ContainerSpec_Isolation) container.Isolation {
switch i {
case swarmapi.ContainerIsolationHyperV:
return container.IsolationHyperV
case swarmapi.ContainerIsolationProcess:
return container.IsolationProcess
case swarmapi.ContainerIsolationDefault:
return container.IsolationDefault
}
return container.IsolationEmpty
}
func isolationToGRPC(i container.Isolation) swarmapi.ContainerSpec_Isolation {
if i.IsHyperV() {
return swarmapi.ContainerIsolationHyperV
}
if i.IsProcess() {
return swarmapi.ContainerIsolationProcess
}
return swarmapi.ContainerIsolationDefault
}