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