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