service.go 7.3 KB

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