Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Services without cluster IP do not get forwarded #167

Closed
giulianob opened this issue Feb 16, 2023 · 5 comments
Closed

Services without cluster IP do not get forwarded #167

giulianob opened this issue Feb 16, 2023 · 5 comments
Assignees
Labels
bug Something isn't working inprogress

Comments

@giulianob
Copy link
Member

Describe the bug

I have a service named metadata which does not have a cluster IP assigned but has an endpoint. The service does not get added to my hosts file when I use dsc.

The list of services I have:

k get services

NAME                                TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
metadata                            ClusterIP   None           <none>        7300/TCP            11m
frontend                            ClusterIP   10.0.175.119   <none>        80/TCP              19h

If I forward the frontend service then you can see metadata does not get added to my hosts file:

dsc connect --service frontend

Waiting for 'frontend-6bf4d6f477-rjbt8' in namespace 'gbarberi2' to reach running state...
Deployment 'gbarberi2/frontend' patched to run agent.
Remote agent deployed in container 'frontend' in pod 'frontend-6bf4d6f477-rjbt8'.
Preparing to run Bridge To Kubernetes configured as pod gbarberi2/frontend-6bf4d6f477-rjbt8 ...
Connection established.
Hosts file updated.
Service 'frontend' is available on 127.1.1.17:80.
Container port 80 is available at localhost:null.

The metadata service does have an endpoint:

k describe service metadata

Name:              metadata
Namespace:         gbarberi2
Labels:            app.kubernetes.io/managed-by=Helm
Annotations:       meta.helm.sh/release-name: metadata
                   meta.helm.sh/release-namespace: gbarberi2
Selector:          app=metadata
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                None
IPs:               None
Port:              grpc  7300/TCP
TargetPort:        7300/TCP
Endpoints:         10.240.10.189:7300
Session Affinity:  None
Events:            <none>

I can port forward the metadata service with kubectl

k port-forward service/metadata 7300:7300

Forwarding from 127.0.0.1:7300 -> 7300
Forwarding from [::1]:7300 -> 7300

I can also exec into the service in my cluster and ping metadata so I would expect it to be also be available locally:

k exec --stdin --tty frontend-57b9f954df-2kpr5 -- /bin/bash

root@frontend-57b9f954df-2kpr5:/App# ping metadata
PING metadata.gbarberi2.svc.cluster.local (10.240.10.189) 56(84) bytes of data.
64 bytes from 10-240-10-189.metadata.gbarberi2.svc.cluster.local (10.240.10.189): icmp_seq=1 ttl=63 time=0.092 ms

Mention the platform you are using
Windows 11 with dsc CLI v1.0.20230124.1

Expected behavior
Services that do not have a Cluster IP defined should get forwarded

@giulianob
Copy link
Member Author

I traced the issue to the following check, the issue is my service does not have a hostname specified in the endpoint so it's skipped:

            foreach (V1Endpoints endpoint in headlessServiceEndpointsToRouteMap.Values)
            {
                var isInWorkloadNamespace = StringComparer.OrdinalIgnoreCase.Equals(endpoint.Metadata.Namespace(), workloadNamespace);
                foreach (var subset in endpoint.Subsets)
                {
                    // For headless service, we only add entries that specify hostname,
                    // if hostname its not specify there is no dsn to reach the replicate.
                    var addresses = subset.Addresses?.Where(a => !string.IsNullOrWhiteSpace(a?.Hostname));
                    if (addresses == null || !addresses.Any())
                    {
                        continue;
                    }

                    foreach (var address in addresses)
                    {
                        servicesToRouteEndpointInfos.Add(new EndpointInfo()
                        {
                            DnsName = isInWorkloadNamespace ?
                                        $"{address.Hostname}.{endpoint.Metadata.Name}" :
                                        $"{address.Hostname}.{endpoint.Metadata.Name}.{endpoint.Metadata.Namespace()}",
                            Ports = subset.Ports?.Where(port => this._IsSupportedProtocol(port.Protocol, endpoint.Metadata.Name) && !(portToIgnoreForHeadlessServiceEndpoints.GetValueOrDefault(endpoint.Metadata.Name)?.Contains(port.Port) ?? false)).Select(p => new PortPair(remotePort: p.Port)).ToArray() ?? new PortPair[] { },
                            IsInWorkloadNamespace = isInWorkloadNamespace
                        });
                    }
                }
            }

A change like this fixes it but I don't know the repercussions and if it's safe:

            // Add headless services info
            foreach (V1Endpoints endpoint in headlessServiceEndpointsToRouteMap.Values)
            {
                var isInWorkloadNamespace = StringComparer.OrdinalIgnoreCase.Equals(endpoint.Metadata.Namespace(), workloadNamespace);
                foreach (var subset in endpoint.Subsets)
                {
                    foreach (var address in subset.Addresses)
                    {
                        servicesToRouteEndpointInfos.Add(new EndpointInfo()
                        {
                            DnsName = (hasAddress: !string.IsNullOrWhiteSpace(address.Hostname), isInWorkloadNamespace) switch
                            {
                                (hasAddress: false, isInWorkloadNamespace: true) => endpoint.Metadata.Name,
                                (hasAddress: false, isInWorkloadNamespace: false) => $"{endpoint.Metadata.Name}.{endpoint.Metadata.Namespace()}",
                                (hasAddress: true, isInWorkloadNamespace: true) => $"{address.Hostname}.{endpoint.Metadata.Name}",
                                (hasAddress: true, isInWorkloadNamespace: false) => $"{address.Hostname}.{endpoint.Metadata.Name}.{endpoint.Metadata.Namespace()}"
                            },
                            Ports = subset.Ports?.Where(port => this._IsSupportedProtocol(port.Protocol, endpoint.Metadata.Name) && !(portToIgnoreForHeadlessServiceEndpoints.GetValueOrDefault(endpoint.Metadata.Name)?.Contains(port.Port) ?? false)).Select(p => new PortPair(remotePort: p.Port)).ToArray() ?? new PortPair[] { },
                            IsInWorkloadNamespace = isInWorkloadNamespace
                        });
                    }
                }
            }

@hsubramanianaks hsubramanianaks added the enhancement New feature or request label Feb 20, 2023
@elenavillamil
Copy link
Contributor

elenavillamil commented Mar 7, 2023

@giulianob thank you for the issue and all the details :) Since this is not a headless service I think the code that would need updating is below. Bridge uses dns to set up its forwarding logic, can a service of type NodePort still be address using dns or just using is nodeIP? In other words when you communicate from serviceA to your serviceB (of type nodeport), do you do NodeIP:Port or ServiceB:port? If it can be accessed using dns, then it would be just a small change to expand that || to include "NodePort", but if it is not reachable using dns then it would be a bigger change.

if ((s.Spec.Type == "ClusterIP" || s.Spec.Type == "LoadBalancer") && !string.IsNullOrWhiteSpace(s.Spec.ClusterIP) && (s.Spec.Ports?.Any() ?? false))

if ((s.Spec.Type == "ClusterIP" || s.Spec.Type == "LoadBalancer") && !string.IsNullOrWhiteSpace(s.Spec.ClusterIP) && (s.Spec.Ports?.Any() ?? false))

@elenavillamil elenavillamil added the waiting for response Waiting for a response by the original author of the ticket label Mar 7, 2023
@giulianob
Copy link
Member Author

@elenavillamil We use DNS like metadata, metadata.somenamespace, etc... We never use the IP directly

@elenavillamil
Copy link
Contributor

elenavillamil commented Mar 8, 2023

@giulianob thank you. This issue is related/duplicate with #155 . I am currently working on a fix. Your code snipped was correct as to why, my comment is wrong since your service is not of type nodeport (not sure how I came to conclusion you were using NodePort service).

@elenavillamil elenavillamil added bug Something isn't working inprogress and removed enhancement New feature or request waiting for response Waiting for a response by the original author of the ticket labels Mar 9, 2023
@elenavillamil elenavillamil self-assigned this Mar 9, 2023
@elenavillamil
Copy link
Contributor

@giulianob Fix for this got deployed today. Please feel free to re-open if you still the issue after upgrading to latest version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working inprogress
Projects
Development

No branches or pull requests

3 participants