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