// ResourceLocation returns a URL to which one can send traffic for the specified pod. func ResourceLocation(getter ResourceGetter, rt http.RoundTripper, ctx genericapirequest.Context, id string) (*url.URL, http.RoundTripper, error) { // Allow ID as "podname" or "podname:port" or "scheme:podname:port". // If port is not specified, try to use the first defined port on the pod. scheme, name, port, valid := utilnet.SplitSchemeNamePort(id) if !valid { return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid pod request %q", id)) } // TODO: if port is not a number but a "(container)/(portname)", do a name lookup. pod, err := getPod(getter, ctx, name) if err != nil { return nil, nil, err } // Try to figure out a port. if port == "" { for i := range pod.Spec.Containers { if len(pod.Spec.Containers[i].Ports) > 0 { port = fmt.Sprintf("%d", pod.Spec.Containers[i].Ports[0].ContainerPort) break } } } loc := &url.URL{ Scheme: scheme, } if port == "" { loc.Host = pod.Status.PodIP } else { loc.Host = net.JoinHostPort(pod.Status.PodIP, port) } return loc, rt, nil }
// ResourceLocation returns an URL and transport which one can use to send traffic for the specified node. func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGetter, proxyTransport http.RoundTripper, ctx genericapirequest.Context, id string) (*url.URL, http.RoundTripper, error) { schemeReq, name, portReq, valid := utilnet.SplitSchemeNamePort(id) if !valid { return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid node request %q", id)) } info, err := connection.GetConnectionInfo(types.NodeName(name)) if err != nil { return nil, nil, err } // We check if we want to get a default Kubelet's transport. It happens if either: // - no port is specified in request (Kubelet's port is default) // - the requested port matches the kubelet port for this node if portReq == "" || portReq == info.Port { return &url.URL{ Scheme: info.Scheme, Host: net.JoinHostPort(info.Hostname, info.Port), }, info.Transport, nil } // Otherwise, return the requested scheme and port, and the proxy transport return &url.URL{Scheme: schemeReq, Host: net.JoinHostPort(info.Hostname, portReq)}, proxyTransport, nil }
// ResourceLocation returns a URL to which one can send traffic for the specified service. func (rs *REST) ResourceLocation(ctx genericapirequest.Context, id string) (*url.URL, http.RoundTripper, error) { // Allow ID as "svcname", "svcname:port", or "scheme:svcname:port". svcScheme, svcName, portStr, valid := utilnet.SplitSchemeNamePort(id) if !valid { return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid service request %q", id)) } // If a port *number* was specified, find the corresponding service port name if portNum, err := strconv.ParseInt(portStr, 10, 64); err == nil { svc, err := rs.registry.GetService(ctx, svcName, &metav1.GetOptions{}) if err != nil { return nil, nil, err } found := false for _, svcPort := range svc.Spec.Ports { if int64(svcPort.Port) == portNum { // use the declared port's name portStr = svcPort.Name found = true break } } if !found { return nil, nil, errors.NewServiceUnavailable(fmt.Sprintf("no service port %d found for service %q", portNum, svcName)) } } eps, err := rs.endpoints.GetEndpoints(ctx, svcName, &metav1.GetOptions{}) if err != nil { return nil, nil, err } if len(eps.Subsets) == 0 { return nil, nil, errors.NewServiceUnavailable(fmt.Sprintf("no endpoints available for service %q", svcName)) } // Pick a random Subset to start searching from. ssSeed := rand.Intn(len(eps.Subsets)) // Find a Subset that has the port. for ssi := 0; ssi < len(eps.Subsets); ssi++ { ss := &eps.Subsets[(ssSeed+ssi)%len(eps.Subsets)] if len(ss.Addresses) == 0 { continue } for i := range ss.Ports { if ss.Ports[i].Name == portStr { // Pick a random address. ip := ss.Addresses[rand.Intn(len(ss.Addresses))].IP port := int(ss.Ports[i].Port) return &url.URL{ Scheme: svcScheme, Host: net.JoinHostPort(ip, strconv.Itoa(port)), }, rs.proxyTransport, nil } } } return nil, nil, errors.NewServiceUnavailable(fmt.Sprintf("no endpoints available for service %q", id)) }