mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #32811 from fraenkel/headless_service
Automatic merge from submit-queue (batch tested with PRs 38260, 32811, 28458, 33570, 37096) Allow no ports when exposing headless service fixes #32795
This commit is contained in:
		@@ -177,6 +177,8 @@ func RunExpose(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
 | 
				
			|||||||
			params["selector"] = s
 | 
								params["selector"] = s
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							isHeadlessService := params["cluster-ip"] == "None"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// For objects that need a port, derive it from the exposed object in case a user
 | 
							// For objects that need a port, derive it from the exposed object in case a user
 | 
				
			||||||
		// didn't explicitly specify one via --port
 | 
							// didn't explicitly specify one via --port
 | 
				
			||||||
		if port, found := params["port"]; found && kubectl.IsZero(port) {
 | 
							if port, found := params["port"]; found && kubectl.IsZero(port) {
 | 
				
			||||||
@@ -186,7 +188,9 @@ func RunExpose(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			switch len(ports) {
 | 
								switch len(ports) {
 | 
				
			||||||
			case 0:
 | 
								case 0:
 | 
				
			||||||
				return cmdutil.UsageError(cmd, "couldn't find port via --port flag or introspection")
 | 
									if !isHeadlessService {
 | 
				
			||||||
 | 
										return cmdutil.UsageError(cmd, "couldn't find port via --port flag or introspection")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			case 1:
 | 
								case 1:
 | 
				
			||||||
				params["port"] = ports[0]
 | 
									params["port"] = ports[0]
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -264,6 +264,32 @@ func TestRunExposeService(t *testing.T) {
 | 
				
			|||||||
			expected: "service \"foo\" exposed",
 | 
								expected: "service \"foo\" exposed",
 | 
				
			||||||
			status:   200,
 | 
								status:   200,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "expose-headless-service-no-port",
 | 
				
			||||||
 | 
								args: []string{"service", "baz"},
 | 
				
			||||||
 | 
								ns:   "test",
 | 
				
			||||||
 | 
								calls: map[string]string{
 | 
				
			||||||
 | 
									"GET":  "/namespaces/test/services/baz",
 | 
				
			||||||
 | 
									"POST": "/namespaces/test/services",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								input: &api.Service{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
 | 
				
			||||||
 | 
									Spec: api.ServiceSpec{
 | 
				
			||||||
 | 
										Selector: map[string]string{"app": "go"},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								flags: map[string]string{"selector": "func=stream", "name": "foo", "labels": "svc=test", "cluster-ip": "None", "dry-run": "true"},
 | 
				
			||||||
 | 
								output: &api.Service{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
 | 
				
			||||||
 | 
									Spec: api.ServiceSpec{
 | 
				
			||||||
 | 
										Ports:     []api.ServicePort{},
 | 
				
			||||||
 | 
										Selector:  map[string]string{"func": "stream"},
 | 
				
			||||||
 | 
										ClusterIP: api.ClusterIPNone,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: "service \"foo\" exposed",
 | 
				
			||||||
 | 
								status:   200,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "expose-from-file",
 | 
								name: "expose-from-file",
 | 
				
			||||||
			args: []string{},
 | 
								args: []string{},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,6 +110,9 @@ func generate(genericParams map[string]interface{}) (runtime.Object, error) {
 | 
				
			|||||||
			return nil, fmt.Errorf("'name' is a required parameter.")
 | 
								return nil, fmt.Errorf("'name' is a required parameter.")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isHeadlessService := params["cluster-ip"] == "None"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ports := []api.ServicePort{}
 | 
						ports := []api.ServicePort{}
 | 
				
			||||||
	servicePortName, found := params["port-name"]
 | 
						servicePortName, found := params["port-name"]
 | 
				
			||||||
	if !found {
 | 
						if !found {
 | 
				
			||||||
@@ -131,44 +134,46 @@ func generate(genericParams map[string]interface{}) (runtime.Object, error) {
 | 
				
			|||||||
	var portString string
 | 
						var portString string
 | 
				
			||||||
	if portString, found = params["ports"]; !found {
 | 
						if portString, found = params["ports"]; !found {
 | 
				
			||||||
		portString, found = params["port"]
 | 
							portString, found = params["port"]
 | 
				
			||||||
		if !found {
 | 
							if !found && !isHeadlessService {
 | 
				
			||||||
			return nil, fmt.Errorf("'port' is a required parameter.")
 | 
								return nil, fmt.Errorf("'port' is a required parameter.")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	portStringSlice := strings.Split(portString, ",")
 | 
						if portString != "" {
 | 
				
			||||||
	for i, stillPortString := range portStringSlice {
 | 
							portStringSlice := strings.Split(portString, ",")
 | 
				
			||||||
		port, err := strconv.Atoi(stillPortString)
 | 
							for i, stillPortString := range portStringSlice {
 | 
				
			||||||
		if err != nil {
 | 
								port, err := strconv.Atoi(stillPortString)
 | 
				
			||||||
			return nil, err
 | 
								if err != nil {
 | 
				
			||||||
		}
 | 
									return nil, err
 | 
				
			||||||
		name := servicePortName
 | 
					 | 
				
			||||||
		// If we are going to assign multiple ports to a service, we need to
 | 
					 | 
				
			||||||
		// generate a different name for each one.
 | 
					 | 
				
			||||||
		if len(portStringSlice) > 1 {
 | 
					 | 
				
			||||||
			name = fmt.Sprintf("port-%d", i+1)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		protocol := params["protocol"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		switch {
 | 
					 | 
				
			||||||
		case len(protocol) == 0 && len(portProtocolMap) == 0:
 | 
					 | 
				
			||||||
			// Default to TCP, what the flag was doing previously.
 | 
					 | 
				
			||||||
			protocol = "TCP"
 | 
					 | 
				
			||||||
		case len(protocol) > 0 && len(portProtocolMap) > 0:
 | 
					 | 
				
			||||||
			// User has specified the --protocol while exposing a multiprotocol resource
 | 
					 | 
				
			||||||
			// We should stomp multiple protocols with the one specified ie. do nothing
 | 
					 | 
				
			||||||
		case len(protocol) == 0 && len(portProtocolMap) > 0:
 | 
					 | 
				
			||||||
			// no --protocol and we expose a multiprotocol resource
 | 
					 | 
				
			||||||
			protocol = "TCP" // have the default so we can stay sane
 | 
					 | 
				
			||||||
			if exposeProtocol, found := portProtocolMap[stillPortString]; found {
 | 
					 | 
				
			||||||
				protocol = exposeProtocol
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								name := servicePortName
 | 
				
			||||||
 | 
								// If we are going to assign multiple ports to a service, we need to
 | 
				
			||||||
 | 
								// generate a different name for each one.
 | 
				
			||||||
 | 
								if len(portStringSlice) > 1 {
 | 
				
			||||||
 | 
									name = fmt.Sprintf("port-%d", i+1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								protocol := params["protocol"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								switch {
 | 
				
			||||||
 | 
								case len(protocol) == 0 && len(portProtocolMap) == 0:
 | 
				
			||||||
 | 
									// Default to TCP, what the flag was doing previously.
 | 
				
			||||||
 | 
									protocol = "TCP"
 | 
				
			||||||
 | 
								case len(protocol) > 0 && len(portProtocolMap) > 0:
 | 
				
			||||||
 | 
									// User has specified the --protocol while exposing a multiprotocol resource
 | 
				
			||||||
 | 
									// We should stomp multiple protocols with the one specified ie. do nothing
 | 
				
			||||||
 | 
								case len(protocol) == 0 && len(portProtocolMap) > 0:
 | 
				
			||||||
 | 
									// no --protocol and we expose a multiprotocol resource
 | 
				
			||||||
 | 
									protocol = "TCP" // have the default so we can stay sane
 | 
				
			||||||
 | 
									if exposeProtocol, found := portProtocolMap[stillPortString]; found {
 | 
				
			||||||
 | 
										protocol = exposeProtocol
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ports = append(ports, api.ServicePort{
 | 
				
			||||||
 | 
									Name:     name,
 | 
				
			||||||
 | 
									Port:     int32(port),
 | 
				
			||||||
 | 
									Protocol: api.Protocol(protocol),
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ports = append(ports, api.ServicePort{
 | 
					 | 
				
			||||||
			Name:     name,
 | 
					 | 
				
			||||||
			Port:     int32(port),
 | 
					 | 
				
			||||||
			Protocol: api.Protocol(protocol),
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	service := api.Service{
 | 
						service := api.Service{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -536,6 +536,29 @@ func TestGenerateService(t *testing.T) {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								generator: ServiceGeneratorV2{},
 | 
				
			||||||
 | 
								params: map[string]interface{}{
 | 
				
			||||||
 | 
									"selector":       "foo=bar,baz=blah",
 | 
				
			||||||
 | 
									"name":           "test",
 | 
				
			||||||
 | 
									"protocol":       "TCP",
 | 
				
			||||||
 | 
									"container-port": "1234",
 | 
				
			||||||
 | 
									"cluster-ip":     "None",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								expected: api.Service{
 | 
				
			||||||
 | 
									ObjectMeta: api.ObjectMeta{
 | 
				
			||||||
 | 
										Name: "test",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Spec: api.ServiceSpec{
 | 
				
			||||||
 | 
										Selector: map[string]string{
 | 
				
			||||||
 | 
											"foo": "bar",
 | 
				
			||||||
 | 
											"baz": "blah",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Ports:     []api.ServicePort{},
 | 
				
			||||||
 | 
										ClusterIP: api.ClusterIPNone,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, test := range tests {
 | 
						for _, test := range tests {
 | 
				
			||||||
		obj, err := test.generator.Generate(test.params)
 | 
							obj, err := test.generator.Generate(test.params)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user