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 }