func (ServiceGenerator) Generate(params map[string]string) (runtime.Object, error) { selectorString, found := params["selector"] if !found || len(selectorString) == 0 { return nil, fmt.Errorf("'selector' is a required parameter.") } selector, err := ParseLabels(selectorString) if err != nil { return nil, err } labelsString, found := params["labels"] var labels map[string]string if found && len(labelsString) > 0 { labels, err = ParseLabels(labelsString) if err != nil { return nil, err } } name, found := params["name"] if !found || len(name) == 0 { name, found = params["default-name"] if !found || len(name) == 0 { return nil, fmt.Errorf("'name' is a required parameter.") } } portString, found := params["port"] if !found { return nil, fmt.Errorf("'port' is a required parameter.") } port, err := strconv.Atoi(portString) if err != nil { return nil, err } service := api.Service{ ObjectMeta: api.ObjectMeta{ Name: name, Labels: labels, }, Spec: api.ServiceSpec{ Selector: selector, Ports: []api.ServicePort{ { Name: "default", Port: port, Protocol: api.Protocol(params["protocol"]), }, }, }, } targetPort, found := params["target-port"] if !found { targetPort, found = params["container-port"] } if found && len(targetPort) > 0 { if portNum, err := strconv.Atoi(targetPort); err != nil { service.Spec.Ports[0].TargetPort = util.NewIntOrStringFromString(targetPort) } else { service.Spec.Ports[0].TargetPort = util.NewIntOrStringFromInt(portNum) } } else { service.Spec.Ports[0].TargetPort = util.NewIntOrStringFromInt(port) } if params["create-external-load-balancer"] == "true" { service.Spec.Type = api.ServiceTypeLoadBalancer } if len(params["public-ip"]) != 0 { service.Spec.DeprecatedPublicIPs = []string{params["public-ip"]} } if len(params["type"]) != 0 { service.Spec.Type = api.ServiceType(params["type"]) } return &service, nil }
func TestRunExposeService(t *testing.T) { tests := []struct { name string args []string ns string calls map[string]string input runtime.Object flags map[string]string output runtime.Object expected string status int }{ { name: "expose-service-from-service", 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"}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"app": "go"}, }, }, flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "12", Labels: map[string]string{"svc": "test"}}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Name: "default", Protocol: api.Protocol("UDP"), Port: 14, }, }, Selector: map[string]string{"func": "stream"}, }, }, status: 200, }, { name: "no-name-passed-from-the-cli", args: []string{"service", "mayor"}, ns: "default", calls: map[string]string{ "GET": "/namespaces/default/services/mayor", "POST": "/namespaces/default/services", }, input: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "mayor", Namespace: "default", ResourceVersion: "12"}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"run": "this"}, }, }, // No --name flag specified below. Service will use the rc's name passed via the 'default-name' parameter flags: map[string]string{"selector": "run=this", "port": "80", "labels": "runas=amayor"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "mayor", Namespace: "default", ResourceVersion: "12", Labels: map[string]string{"runas": "amayor"}}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Name: "default", Protocol: api.Protocol("TCP"), Port: 80, }, }, Selector: map[string]string{"run": "this"}, }, }, status: 200, }, { name: "expose-external-service", 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"}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Selector: map[string]string{"app": "go"}, }, }, flags: map[string]string{"selector": "func=stream", "protocol": "UDP", "port": "14", "name": "foo", "labels": "svc=test", "create-external-load-balancer": "true"}, output: &api.Service{ ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "12", Labels: map[string]string{"svc": "test"}}, TypeMeta: api.TypeMeta{Kind: "Service", APIVersion: "v1"}, Spec: api.ServiceSpec{ Ports: []api.ServicePort{ { Name: "default", Protocol: api.Protocol("UDP"), Port: 14, }, }, Selector: map[string]string{"func": "stream"}, Type: api.ServiceTypeLoadBalancer, }, }, expected: "you will also need to explicitly open a firewall", status: 200, }, } for _, test := range tests { f, tf, codec := NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &client.FakeRESTClient{ Codec: codec, Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case p == test.calls[m] && m == "GET": return &http.Response{StatusCode: test.status, Body: objBody(codec, test.input)}, nil case p == test.calls[m] && m == "POST": return &http.Response{StatusCode: test.status, Body: objBody(codec, test.output)}, nil default: t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) return nil, nil } }), } tf.Namespace = test.ns buf := bytes.NewBuffer([]byte{}) cmd := NewCmdExposeService(f, buf) cmd.SetOutput(buf) for flag, value := range test.flags { cmd.Flags().Set(flag, value) } cmd.Run(cmd, test.args) if len(test.expected) > 0 { out := buf.String() if !strings.Contains(out, test.expected) { t.Errorf("%s: unexpected output: %s", test.name, out) } } } }