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