service.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package kubectl
  14. import (
  15. "fmt"
  16. "strconv"
  17. "strings"
  18. "k8s.io/kubernetes/pkg/api"
  19. "k8s.io/kubernetes/pkg/runtime"
  20. "k8s.io/kubernetes/pkg/util/intstr"
  21. )
  22. // The only difference between ServiceGeneratorV1 and V2 is that the service port is named "default" in V1, while it is left unnamed in V2.
  23. type ServiceGeneratorV1 struct{}
  24. func (ServiceGeneratorV1) ParamNames() []GeneratorParam {
  25. return paramNames()
  26. }
  27. func (ServiceGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
  28. params["port-name"] = "default"
  29. return generate(params)
  30. }
  31. type ServiceGeneratorV2 struct{}
  32. func (ServiceGeneratorV2) ParamNames() []GeneratorParam {
  33. return paramNames()
  34. }
  35. func (ServiceGeneratorV2) Generate(params map[string]interface{}) (runtime.Object, error) {
  36. return generate(params)
  37. }
  38. func paramNames() []GeneratorParam {
  39. return []GeneratorParam{
  40. {"default-name", true},
  41. {"name", false},
  42. {"selector", true},
  43. // port will be used if a user specifies --port OR the exposed object
  44. // has one port
  45. {"port", false},
  46. // ports will be used iff a user doesn't specify --port AND the
  47. // exposed object has multiple ports
  48. {"ports", false},
  49. {"labels", false},
  50. {"external-ip", false},
  51. {"create-external-load-balancer", false},
  52. {"load-balancer-ip", false},
  53. {"type", false},
  54. {"protocol", false},
  55. // protocols will be used to keep port-protocol mapping derived from
  56. // exposed object
  57. {"protocols", false},
  58. {"container-port", false}, // alias of target-port
  59. {"target-port", false},
  60. {"port-name", false},
  61. {"session-affinity", false},
  62. {"cluster-ip", false},
  63. }
  64. }
  65. func generate(genericParams map[string]interface{}) (runtime.Object, error) {
  66. params := map[string]string{}
  67. for key, value := range genericParams {
  68. strVal, isString := value.(string)
  69. if !isString {
  70. return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
  71. }
  72. params[key] = strVal
  73. }
  74. selectorString, found := params["selector"]
  75. if !found || len(selectorString) == 0 {
  76. return nil, fmt.Errorf("'selector' is a required parameter.")
  77. }
  78. selector, err := ParseLabels(selectorString)
  79. if err != nil {
  80. return nil, err
  81. }
  82. labelsString, found := params["labels"]
  83. var labels map[string]string
  84. if found && len(labelsString) > 0 {
  85. labels, err = ParseLabels(labelsString)
  86. if err != nil {
  87. return nil, err
  88. }
  89. }
  90. name, found := params["name"]
  91. if !found || len(name) == 0 {
  92. name, found = params["default-name"]
  93. if !found || len(name) == 0 {
  94. return nil, fmt.Errorf("'name' is a required parameter.")
  95. }
  96. }
  97. ports := []api.ServicePort{}
  98. servicePortName, found := params["port-name"]
  99. if !found {
  100. // Leave the port unnamed.
  101. servicePortName = ""
  102. }
  103. protocolsString, found := params["protocols"]
  104. var portProtocolMap map[string]string
  105. if found && len(protocolsString) > 0 {
  106. portProtocolMap, err = ParseProtocols(protocolsString)
  107. if err != nil {
  108. return nil, err
  109. }
  110. }
  111. // ports takes precedence over port since it will be
  112. // specified only when the user hasn't specified a port
  113. // via --port and the exposed object has multiple ports.
  114. var portString string
  115. if portString, found = params["ports"]; !found {
  116. portString, found = params["port"]
  117. if !found {
  118. return nil, fmt.Errorf("'port' is a required parameter.")
  119. }
  120. }
  121. portStringSlice := strings.Split(portString, ",")
  122. for i, stillPortString := range portStringSlice {
  123. port, err := strconv.Atoi(stillPortString)
  124. if err != nil {
  125. return nil, err
  126. }
  127. name := servicePortName
  128. // If we are going to assign multiple ports to a service, we need to
  129. // generate a different name for each one.
  130. if len(portStringSlice) > 1 {
  131. name = fmt.Sprintf("port-%d", i+1)
  132. }
  133. protocol := params["protocol"]
  134. switch {
  135. case len(protocol) == 0 && len(portProtocolMap) == 0:
  136. // Default to TCP, what the flag was doing previously.
  137. protocol = "TCP"
  138. case len(protocol) > 0 && len(portProtocolMap) > 0:
  139. // User has specified the --protocol while exposing a multiprotocol resource
  140. // We should stomp multiple protocols with the one specified ie. do nothing
  141. case len(protocol) == 0 && len(portProtocolMap) > 0:
  142. // no --protocol and we expose a multiprotocol resource
  143. protocol = "TCP" // have the default so we can stay sane
  144. if exposeProtocol, found := portProtocolMap[stillPortString]; found {
  145. protocol = exposeProtocol
  146. }
  147. }
  148. ports = append(ports, api.ServicePort{
  149. Name: name,
  150. Port: int32(port),
  151. Protocol: api.Protocol(protocol),
  152. })
  153. }
  154. service := api.Service{
  155. ObjectMeta: api.ObjectMeta{
  156. Name: name,
  157. Labels: labels,
  158. },
  159. Spec: api.ServiceSpec{
  160. Selector: selector,
  161. Ports: ports,
  162. },
  163. }
  164. targetPortString := params["target-port"]
  165. if len(targetPortString) == 0 {
  166. targetPortString = params["container-port"]
  167. }
  168. if len(targetPortString) > 0 {
  169. var targetPort intstr.IntOrString
  170. if portNum, err := strconv.Atoi(targetPortString); err != nil {
  171. targetPort = intstr.FromString(targetPortString)
  172. } else {
  173. targetPort = intstr.FromInt(portNum)
  174. }
  175. // Use the same target-port for every port
  176. for i := range service.Spec.Ports {
  177. service.Spec.Ports[i].TargetPort = targetPort
  178. }
  179. } else {
  180. // If --target-port or --container-port haven't been specified, this
  181. // should be the same as Port
  182. for i := range service.Spec.Ports {
  183. port := service.Spec.Ports[i].Port
  184. service.Spec.Ports[i].TargetPort = intstr.FromInt(int(port))
  185. }
  186. }
  187. if params["create-external-load-balancer"] == "true" {
  188. service.Spec.Type = api.ServiceTypeLoadBalancer
  189. }
  190. if len(params["external-ip"]) > 0 {
  191. service.Spec.ExternalIPs = []string{params["external-ip"]}
  192. }
  193. if len(params["type"]) != 0 {
  194. service.Spec.Type = api.ServiceType(params["type"])
  195. }
  196. if service.Spec.Type == api.ServiceTypeLoadBalancer {
  197. service.Spec.LoadBalancerIP = params["load-balancer-ip"]
  198. }
  199. if len(params["session-affinity"]) != 0 {
  200. switch api.ServiceAffinity(params["session-affinity"]) {
  201. case api.ServiceAffinityNone:
  202. service.Spec.SessionAffinity = api.ServiceAffinityNone
  203. case api.ServiceAffinityClientIP:
  204. service.Spec.SessionAffinity = api.ServiceAffinityClientIP
  205. default:
  206. return nil, fmt.Errorf("unknown session affinity: %s", params["session-affinity"])
  207. }
  208. }
  209. if len(params["cluster-ip"]) != 0 {
  210. if params["cluster-ip"] == "None" {
  211. service.Spec.ClusterIP = api.ClusterIPNone
  212. } else {
  213. service.Spec.ClusterIP = params["cluster-ip"]
  214. }
  215. }
  216. return &service, nil
  217. }