...

Source file src/k8s.io/kubectl/pkg/generate/versioned/service.go

Documentation: k8s.io/kubectl/pkg/generate/versioned

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

View as plain text