github.com/moby/docker@v26.1.3+incompatible/daemon/cluster/convert/container.go (about)

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