github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/daemon/cluster/convert/container.go (about)

     1  package convert // import "github.com/docker/docker/daemon/cluster/convert"
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/docker/docker/api/types/container"
     8  	mounttypes "github.com/docker/docker/api/types/mount"
     9  	types "github.com/docker/docker/api/types/swarm"
    10  	swarmapi "github.com/docker/swarmkit/api"
    11  	gogotypes "github.com/gogo/protobuf/types"
    12  	"github.com/pkg/errors"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
    17  	if c == nil {
    18  		return nil
    19  	}
    20  	containerSpec := &types.ContainerSpec{
    21  		Image:      c.Image,
    22  		Labels:     c.Labels,
    23  		Command:    c.Command,
    24  		Args:       c.Args,
    25  		Hostname:   c.Hostname,
    26  		Env:        c.Env,
    27  		Dir:        c.Dir,
    28  		User:       c.User,
    29  		Groups:     c.Groups,
    30  		StopSignal: c.StopSignal,
    31  		TTY:        c.TTY,
    32  		OpenStdin:  c.OpenStdin,
    33  		ReadOnly:   c.ReadOnly,
    34  		Hosts:      c.Hosts,
    35  		Secrets:    secretReferencesFromGRPC(c.Secrets),
    36  		Configs:    configReferencesFromGRPC(c.Configs),
    37  		Isolation:  IsolationFromGRPC(c.Isolation),
    38  		Init:       initFromGRPC(c.Init),
    39  		Sysctls:    c.Sysctls,
    40  	}
    41  
    42  	if c.DNSConfig != nil {
    43  		containerSpec.DNSConfig = &types.DNSConfig{
    44  			Nameservers: c.DNSConfig.Nameservers,
    45  			Search:      c.DNSConfig.Search,
    46  			Options:     c.DNSConfig.Options,
    47  		}
    48  	}
    49  
    50  	// Privileges
    51  	if c.Privileges != nil {
    52  		containerSpec.Privileges = &types.Privileges{}
    53  
    54  		if c.Privileges.CredentialSpec != nil {
    55  			containerSpec.Privileges.CredentialSpec = credentialSpecFromGRPC(c.Privileges.CredentialSpec)
    56  		}
    57  
    58  		if c.Privileges.SELinuxContext != nil {
    59  			containerSpec.Privileges.SELinuxContext = &types.SELinuxContext{
    60  				Disable: c.Privileges.SELinuxContext.Disable,
    61  				User:    c.Privileges.SELinuxContext.User,
    62  				Type:    c.Privileges.SELinuxContext.Type,
    63  				Role:    c.Privileges.SELinuxContext.Role,
    64  				Level:   c.Privileges.SELinuxContext.Level,
    65  			}
    66  		}
    67  	}
    68  
    69  	// Mounts
    70  	for _, m := range c.Mounts {
    71  		mount := mounttypes.Mount{
    72  			Target:   m.Target,
    73  			Source:   m.Source,
    74  			Type:     mounttypes.Type(strings.ToLower(swarmapi.Mount_MountType_name[int32(m.Type)])),
    75  			ReadOnly: m.ReadOnly,
    76  		}
    77  
    78  		if m.BindOptions != nil {
    79  			mount.BindOptions = &mounttypes.BindOptions{
    80  				Propagation: mounttypes.Propagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])),
    81  			}
    82  		}
    83  
    84  		if m.VolumeOptions != nil {
    85  			mount.VolumeOptions = &mounttypes.VolumeOptions{
    86  				NoCopy: m.VolumeOptions.NoCopy,
    87  				Labels: m.VolumeOptions.Labels,
    88  			}
    89  			if m.VolumeOptions.DriverConfig != nil {
    90  				mount.VolumeOptions.DriverConfig = &mounttypes.Driver{
    91  					Name:    m.VolumeOptions.DriverConfig.Name,
    92  					Options: m.VolumeOptions.DriverConfig.Options,
    93  				}
    94  			}
    95  		}
    96  
    97  		if m.TmpfsOptions != nil {
    98  			mount.TmpfsOptions = &mounttypes.TmpfsOptions{
    99  				SizeBytes: m.TmpfsOptions.SizeBytes,
   100  				Mode:      m.TmpfsOptions.Mode,
   101  			}
   102  		}
   103  		containerSpec.Mounts = append(containerSpec.Mounts, mount)
   104  	}
   105  
   106  	if c.StopGracePeriod != nil {
   107  		grace, _ := gogotypes.DurationFromProto(c.StopGracePeriod)
   108  		containerSpec.StopGracePeriod = &grace
   109  	}
   110  
   111  	if c.Healthcheck != nil {
   112  		containerSpec.Healthcheck = healthConfigFromGRPC(c.Healthcheck)
   113  	}
   114  
   115  	return containerSpec
   116  }
   117  
   118  func initFromGRPC(v *gogotypes.BoolValue) *bool {
   119  	if v == nil {
   120  		return nil
   121  	}
   122  	value := v.GetValue()
   123  	return &value
   124  }
   125  
   126  func initToGRPC(v *bool) *gogotypes.BoolValue {
   127  	if v == nil {
   128  		return nil
   129  	}
   130  	return &gogotypes.BoolValue{Value: *v}
   131  }
   132  
   133  func secretReferencesToGRPC(sr []*types.SecretReference) []*swarmapi.SecretReference {
   134  	refs := make([]*swarmapi.SecretReference, 0, len(sr))
   135  	for _, s := range sr {
   136  		ref := &swarmapi.SecretReference{
   137  			SecretID:   s.SecretID,
   138  			SecretName: s.SecretName,
   139  		}
   140  		if s.File != nil {
   141  			ref.Target = &swarmapi.SecretReference_File{
   142  				File: &swarmapi.FileTarget{
   143  					Name: s.File.Name,
   144  					UID:  s.File.UID,
   145  					GID:  s.File.GID,
   146  					Mode: s.File.Mode,
   147  				},
   148  			}
   149  		}
   150  
   151  		refs = append(refs, ref)
   152  	}
   153  
   154  	return refs
   155  }
   156  
   157  func secretReferencesFromGRPC(sr []*swarmapi.SecretReference) []*types.SecretReference {
   158  	refs := make([]*types.SecretReference, 0, len(sr))
   159  	for _, s := range sr {
   160  		target := s.GetFile()
   161  		if target == nil {
   162  			// not a file target
   163  			logrus.Warnf("secret target not a file: secret=%s", s.SecretID)
   164  			continue
   165  		}
   166  		refs = append(refs, &types.SecretReference{
   167  			File: &types.SecretReferenceFileTarget{
   168  				Name: target.Name,
   169  				UID:  target.UID,
   170  				GID:  target.GID,
   171  				Mode: target.Mode,
   172  			},
   173  			SecretID:   s.SecretID,
   174  			SecretName: s.SecretName,
   175  		})
   176  	}
   177  
   178  	return refs
   179  }
   180  
   181  func configReferencesToGRPC(sr []*types.ConfigReference) ([]*swarmapi.ConfigReference, error) {
   182  	refs := make([]*swarmapi.ConfigReference, 0, len(sr))
   183  	for _, s := range sr {
   184  		ref := &swarmapi.ConfigReference{
   185  			ConfigID:   s.ConfigID,
   186  			ConfigName: s.ConfigName,
   187  		}
   188  		switch {
   189  		case s.Runtime == nil && s.File == nil:
   190  			return nil, errors.New("either File or Runtime should be set")
   191  		case s.Runtime != nil && s.File != nil:
   192  			return nil, errors.New("cannot specify both File and Runtime")
   193  		case s.Runtime != nil:
   194  			// Runtime target was added in API v1.40 and takes precedence over
   195  			// File target. However, File and Runtime targets are mutually exclusive,
   196  			// so we should never have both.
   197  			ref.Target = &swarmapi.ConfigReference_Runtime{
   198  				Runtime: &swarmapi.RuntimeTarget{},
   199  			}
   200  		case s.File != nil:
   201  			ref.Target = &swarmapi.ConfigReference_File{
   202  				File: &swarmapi.FileTarget{
   203  					Name: s.File.Name,
   204  					UID:  s.File.UID,
   205  					GID:  s.File.GID,
   206  					Mode: s.File.Mode,
   207  				},
   208  			}
   209  		}
   210  
   211  		refs = append(refs, ref)
   212  	}
   213  
   214  	return refs, nil
   215  }
   216  
   217  func configReferencesFromGRPC(sr []*swarmapi.ConfigReference) []*types.ConfigReference {
   218  	refs := make([]*types.ConfigReference, 0, len(sr))
   219  	for _, s := range sr {
   220  
   221  		r := &types.ConfigReference{
   222  			ConfigID:   s.ConfigID,
   223  			ConfigName: s.ConfigName,
   224  		}
   225  		if target := s.GetRuntime(); target != nil {
   226  			r.Runtime = &types.ConfigReferenceRuntimeTarget{}
   227  		} else if target := s.GetFile(); target != nil {
   228  			r.File = &types.ConfigReferenceFileTarget{
   229  				Name: target.Name,
   230  				UID:  target.UID,
   231  				GID:  target.GID,
   232  				Mode: target.Mode,
   233  			}
   234  		} else {
   235  			// not a file target
   236  			logrus.Warnf("config target not known: config=%s", s.ConfigID)
   237  			continue
   238  		}
   239  		refs = append(refs, r)
   240  	}
   241  
   242  	return refs
   243  }
   244  
   245  func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
   246  	containerSpec := &swarmapi.ContainerSpec{
   247  		Image:      c.Image,
   248  		Labels:     c.Labels,
   249  		Command:    c.Command,
   250  		Args:       c.Args,
   251  		Hostname:   c.Hostname,
   252  		Env:        c.Env,
   253  		Dir:        c.Dir,
   254  		User:       c.User,
   255  		Groups:     c.Groups,
   256  		StopSignal: c.StopSignal,
   257  		TTY:        c.TTY,
   258  		OpenStdin:  c.OpenStdin,
   259  		ReadOnly:   c.ReadOnly,
   260  		Hosts:      c.Hosts,
   261  		Secrets:    secretReferencesToGRPC(c.Secrets),
   262  		Isolation:  isolationToGRPC(c.Isolation),
   263  		Init:       initToGRPC(c.Init),
   264  		Sysctls:    c.Sysctls,
   265  	}
   266  
   267  	if c.DNSConfig != nil {
   268  		containerSpec.DNSConfig = &swarmapi.ContainerSpec_DNSConfig{
   269  			Nameservers: c.DNSConfig.Nameservers,
   270  			Search:      c.DNSConfig.Search,
   271  			Options:     c.DNSConfig.Options,
   272  		}
   273  	}
   274  
   275  	if c.StopGracePeriod != nil {
   276  		containerSpec.StopGracePeriod = gogotypes.DurationProto(*c.StopGracePeriod)
   277  	}
   278  
   279  	// Privileges
   280  	if c.Privileges != nil {
   281  		containerSpec.Privileges = &swarmapi.Privileges{}
   282  
   283  		if c.Privileges.CredentialSpec != nil {
   284  			cs, err := credentialSpecToGRPC(c.Privileges.CredentialSpec)
   285  			if err != nil {
   286  				return nil, errors.Wrap(err, "invalid CredentialSpec")
   287  			}
   288  			containerSpec.Privileges.CredentialSpec = cs
   289  		}
   290  
   291  		if c.Privileges.SELinuxContext != nil {
   292  			containerSpec.Privileges.SELinuxContext = &swarmapi.Privileges_SELinuxContext{
   293  				Disable: c.Privileges.SELinuxContext.Disable,
   294  				User:    c.Privileges.SELinuxContext.User,
   295  				Type:    c.Privileges.SELinuxContext.Type,
   296  				Role:    c.Privileges.SELinuxContext.Role,
   297  				Level:   c.Privileges.SELinuxContext.Level,
   298  			}
   299  		}
   300  	}
   301  
   302  	if c.Configs != nil {
   303  		configs, err := configReferencesToGRPC(c.Configs)
   304  		if err != nil {
   305  			return nil, errors.Wrap(err, "invalid Config")
   306  		}
   307  		containerSpec.Configs = configs
   308  	}
   309  
   310  	// Mounts
   311  	for _, m := range c.Mounts {
   312  		mount := swarmapi.Mount{
   313  			Target:   m.Target,
   314  			Source:   m.Source,
   315  			ReadOnly: m.ReadOnly,
   316  		}
   317  
   318  		if mountType, ok := swarmapi.Mount_MountType_value[strings.ToUpper(string(m.Type))]; ok {
   319  			mount.Type = swarmapi.Mount_MountType(mountType)
   320  		} else if string(m.Type) != "" {
   321  			return nil, fmt.Errorf("invalid MountType: %q", m.Type)
   322  		}
   323  
   324  		if m.BindOptions != nil {
   325  			if mountPropagation, ok := swarmapi.Mount_BindOptions_MountPropagation_value[strings.ToUpper(string(m.BindOptions.Propagation))]; ok {
   326  				mount.BindOptions = &swarmapi.Mount_BindOptions{Propagation: swarmapi.Mount_BindOptions_MountPropagation(mountPropagation)}
   327  			} else if string(m.BindOptions.Propagation) != "" {
   328  				return nil, fmt.Errorf("invalid MountPropagation: %q", m.BindOptions.Propagation)
   329  			}
   330  
   331  			if m.BindOptions.NonRecursive {
   332  				// TODO(AkihiroSuda): NonRecursive is unsupported for Swarm-mode now because of mutual vendoring
   333  				// across moby and swarmkit. Will be available soon after the moby PR gets merged.
   334  				return nil, fmt.Errorf("invalid NonRecursive: %q", m.BindOptions.Propagation)
   335  			}
   336  		}
   337  
   338  		if m.VolumeOptions != nil {
   339  			mount.VolumeOptions = &swarmapi.Mount_VolumeOptions{
   340  				NoCopy: m.VolumeOptions.NoCopy,
   341  				Labels: m.VolumeOptions.Labels,
   342  			}
   343  			if m.VolumeOptions.DriverConfig != nil {
   344  				mount.VolumeOptions.DriverConfig = &swarmapi.Driver{
   345  					Name:    m.VolumeOptions.DriverConfig.Name,
   346  					Options: m.VolumeOptions.DriverConfig.Options,
   347  				}
   348  			}
   349  		}
   350  
   351  		if m.TmpfsOptions != nil {
   352  			mount.TmpfsOptions = &swarmapi.Mount_TmpfsOptions{
   353  				SizeBytes: m.TmpfsOptions.SizeBytes,
   354  				Mode:      m.TmpfsOptions.Mode,
   355  			}
   356  		}
   357  
   358  		containerSpec.Mounts = append(containerSpec.Mounts, mount)
   359  	}
   360  
   361  	if c.Healthcheck != nil {
   362  		containerSpec.Healthcheck = healthConfigToGRPC(c.Healthcheck)
   363  	}
   364  
   365  	return containerSpec, nil
   366  }
   367  
   368  func credentialSpecFromGRPC(c *swarmapi.Privileges_CredentialSpec) *types.CredentialSpec {
   369  	cs := &types.CredentialSpec{}
   370  	switch c.Source.(type) {
   371  	case *swarmapi.Privileges_CredentialSpec_Config:
   372  		cs.Config = c.GetConfig()
   373  	case *swarmapi.Privileges_CredentialSpec_File:
   374  		cs.File = c.GetFile()
   375  	case *swarmapi.Privileges_CredentialSpec_Registry:
   376  		cs.Registry = c.GetRegistry()
   377  	}
   378  	return cs
   379  }
   380  
   381  func credentialSpecToGRPC(c *types.CredentialSpec) (*swarmapi.Privileges_CredentialSpec, error) {
   382  	var opts []string
   383  
   384  	if c.Config != "" {
   385  		opts = append(opts, `"config"`)
   386  	}
   387  	if c.File != "" {
   388  		opts = append(opts, `"file"`)
   389  	}
   390  	if c.Registry != "" {
   391  		opts = append(opts, `"registry"`)
   392  	}
   393  	l := len(opts)
   394  	switch {
   395  	case l == 0:
   396  		return nil, errors.New(`must either provide "file", "registry", or "config" for credential spec`)
   397  	case l == 2:
   398  		return nil, fmt.Errorf("cannot specify both %s and %s credential specs", opts[0], opts[1])
   399  	case l > 2:
   400  		return nil, fmt.Errorf("cannot specify both %s, and %s credential specs", strings.Join(opts[:l-1], ", "), opts[l-1])
   401  	}
   402  
   403  	spec := &swarmapi.Privileges_CredentialSpec{}
   404  	switch {
   405  	case c.Config != "":
   406  		spec.Source = &swarmapi.Privileges_CredentialSpec_Config{
   407  			Config: c.Config,
   408  		}
   409  	case c.File != "":
   410  		spec.Source = &swarmapi.Privileges_CredentialSpec_File{
   411  			File: c.File,
   412  		}
   413  	case c.Registry != "":
   414  		spec.Source = &swarmapi.Privileges_CredentialSpec_Registry{
   415  			Registry: c.Registry,
   416  		}
   417  	}
   418  
   419  	return spec, nil
   420  }
   421  
   422  func healthConfigFromGRPC(h *swarmapi.HealthConfig) *container.HealthConfig {
   423  	interval, _ := gogotypes.DurationFromProto(h.Interval)
   424  	timeout, _ := gogotypes.DurationFromProto(h.Timeout)
   425  	startPeriod, _ := gogotypes.DurationFromProto(h.StartPeriod)
   426  	return &container.HealthConfig{
   427  		Test:        h.Test,
   428  		Interval:    interval,
   429  		Timeout:     timeout,
   430  		Retries:     int(h.Retries),
   431  		StartPeriod: startPeriod,
   432  	}
   433  }
   434  
   435  func healthConfigToGRPC(h *container.HealthConfig) *swarmapi.HealthConfig {
   436  	return &swarmapi.HealthConfig{
   437  		Test:        h.Test,
   438  		Interval:    gogotypes.DurationProto(h.Interval),
   439  		Timeout:     gogotypes.DurationProto(h.Timeout),
   440  		Retries:     int32(h.Retries),
   441  		StartPeriod: gogotypes.DurationProto(h.StartPeriod),
   442  	}
   443  }
   444  
   445  // IsolationFromGRPC converts a swarm api container isolation to a moby isolation representation
   446  func IsolationFromGRPC(i swarmapi.ContainerSpec_Isolation) container.Isolation {
   447  	switch i {
   448  	case swarmapi.ContainerIsolationHyperV:
   449  		return container.IsolationHyperV
   450  	case swarmapi.ContainerIsolationProcess:
   451  		return container.IsolationProcess
   452  	case swarmapi.ContainerIsolationDefault:
   453  		return container.IsolationDefault
   454  	}
   455  	return container.IsolationEmpty
   456  }
   457  
   458  func isolationToGRPC(i container.Isolation) swarmapi.ContainerSpec_Isolation {
   459  	if i.IsHyperV() {
   460  		return swarmapi.ContainerIsolationHyperV
   461  	}
   462  	if i.IsProcess() {
   463  		return swarmapi.ContainerIsolationProcess
   464  	}
   465  	return swarmapi.ContainerIsolationDefault
   466  }