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 }