placement.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // +build kube
  2. /*
  3. Copyright 2020 Docker Compose CLI authors
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package resources
  15. import (
  16. "regexp"
  17. "strings"
  18. "github.com/compose-spec/compose-go/types"
  19. "github.com/pkg/errors"
  20. apiv1 "k8s.io/api/core/v1"
  21. )
  22. var constraintEquals = regexp.MustCompile(`([\w\.]*)\W*(==|!=)\W*([\w\.]*)`)
  23. const (
  24. kubernetesOs = "beta.kubernetes.io/os"
  25. kubernetesArch = "beta.kubernetes.io/arch"
  26. kubernetesHostname = "kubernetes.io/hostname"
  27. )
  28. // node.id Node ID node.id == 2ivku8v2gvtg4
  29. // node.hostname Node hostname node.hostname != node-2
  30. // node.role Node role node.role == manager
  31. // node.labels user defined node labels node.labels.security == high
  32. // engine.labels Docker Engine's labels engine.labels.operatingsystem == ubuntu 14.04
  33. func toNodeAffinity(deploy *types.DeployConfig) (*apiv1.Affinity, error) {
  34. constraints := []string{}
  35. if deploy != nil && deploy.Placement.Constraints != nil {
  36. constraints = deploy.Placement.Constraints
  37. }
  38. requirements := []apiv1.NodeSelectorRequirement{}
  39. for _, constraint := range constraints {
  40. matches := constraintEquals.FindStringSubmatch(constraint)
  41. if len(matches) == 4 {
  42. key := matches[1]
  43. operator, err := toRequirementOperator(matches[2])
  44. if err != nil {
  45. return nil, err
  46. }
  47. value := matches[3]
  48. switch {
  49. case key == constraintOs:
  50. requirements = append(requirements, apiv1.NodeSelectorRequirement{
  51. Key: kubernetesOs,
  52. Operator: operator,
  53. Values: []string{value},
  54. })
  55. case key == constraintArch:
  56. requirements = append(requirements, apiv1.NodeSelectorRequirement{
  57. Key: kubernetesArch,
  58. Operator: operator,
  59. Values: []string{value},
  60. })
  61. case key == constraintHostname:
  62. requirements = append(requirements, apiv1.NodeSelectorRequirement{
  63. Key: kubernetesHostname,
  64. Operator: operator,
  65. Values: []string{value},
  66. })
  67. case strings.HasPrefix(key, constraintLabelPrefix):
  68. requirements = append(requirements, apiv1.NodeSelectorRequirement{
  69. Key: strings.TrimPrefix(key, constraintLabelPrefix),
  70. Operator: operator,
  71. Values: []string{value},
  72. })
  73. }
  74. }
  75. }
  76. if !hasRequirement(requirements, kubernetesOs) {
  77. requirements = append(requirements, apiv1.NodeSelectorRequirement{
  78. Key: kubernetesOs,
  79. Operator: apiv1.NodeSelectorOpIn,
  80. Values: []string{"linux"},
  81. })
  82. }
  83. if !hasRequirement(requirements, kubernetesArch) {
  84. requirements = append(requirements, apiv1.NodeSelectorRequirement{
  85. Key: kubernetesArch,
  86. Operator: apiv1.NodeSelectorOpIn,
  87. Values: []string{"amd64"},
  88. })
  89. }
  90. return &apiv1.Affinity{
  91. NodeAffinity: &apiv1.NodeAffinity{
  92. RequiredDuringSchedulingIgnoredDuringExecution: &apiv1.NodeSelector{
  93. NodeSelectorTerms: []apiv1.NodeSelectorTerm{
  94. {
  95. MatchExpressions: requirements,
  96. },
  97. },
  98. },
  99. },
  100. }, nil
  101. }
  102. const (
  103. constraintOs = "node.platform.os"
  104. constraintArch = "node.platform.arch"
  105. constraintHostname = "node.hostname"
  106. constraintLabelPrefix = "node.labels."
  107. )
  108. func hasRequirement(requirements []apiv1.NodeSelectorRequirement, key string) bool {
  109. for _, r := range requirements {
  110. if r.Key == key {
  111. return true
  112. }
  113. }
  114. return false
  115. }
  116. func toRequirementOperator(sign string) (apiv1.NodeSelectorOperator, error) {
  117. switch sign {
  118. case "==":
  119. return apiv1.NodeSelectorOpIn, nil
  120. case "!=":
  121. return apiv1.NodeSelectorOpNotIn, nil
  122. case ">":
  123. return apiv1.NodeSelectorOpGt, nil
  124. case "<":
  125. return apiv1.NodeSelectorOpLt, nil
  126. default:
  127. return "", errors.Errorf("operator %s not supported", sign)
  128. }
  129. }