in src/library/Connect/WorkloadInformationProvider.cs [148:299]
public async Task<WorkloadInfo> GatherWorkloadInfo(
ILocalProcessConfig localProcessConfig,
CancellationToken cancellationToken)
{
using (var perfLogger = _log.StartPerformanceLogger(Events.WorkloadInformationProvider.AreaName, Events.WorkloadInformationProvider.Operations.GatherWorkloadInfo))
{
var remoteContainerConnectionDetails = await _remoteContainerConnectionDetails;
var container = remoteContainerConnectionDetails.Container ?? throw new ArgumentNullException("The container should always be resolved before proceding to establish a connection");
WorkloadInfo workloadInfo = new WorkloadInfo()
{
EnvironmentVariables = new Dictionary<string, string>(),
ReversePortForwardInfo = new List<PortForwardStartInfo>(),
VolumeMounts = new List<ContainerVolumeMountInfo>(),
ReachableEndpoints = new List<EndpointInfo>(),
Namespace = remoteContainerConnectionDetails.NamespaceName,
WorkloadName = remoteContainerConnectionDetails.ServiceName ?? remoteContainerConnectionDetails.DeploymentName ?? remoteContainerConnectionDetails.PodName
};
remoteContainerConnectionDetails.Container?.VolumeMounts?.ExecuteForEach(v =>
{
workloadInfo.VolumeMounts = workloadInfo.VolumeMounts.Append(new ContainerVolumeMountInfo()
{
Name = v.Name,
LocalVolumeName = _resourceNamingService.GetVolumeName(v.Name),
ContainerPath = v.MountPath,
SubPath = v.SubPath
});
});
List<string> args = new List<string>();
if (container.Command?.Any() ?? false)
{
workloadInfo.Entrypoint = container.Command.First();
args.AddRange(container.Command.Skip(1));
}
if (container?.Args != null)
{
args.AddRange(container.Args);
}
workloadInfo.Args = args.ToArray();
workloadInfo.EnvironmentVariables = await this.WorkloadEnvironment;
var isDapr = workloadInfo.EnvironmentVariables.ContainsKey("DAPR_HTTP_PORT") &&
workloadInfo.EnvironmentVariables.ContainsKey("DAPR_GRPC_PORT");
workloadInfo.ReachableEndpoints = await this.GetReachableEndpointsAsync(remoteContainerConnectionDetails.NamespaceName, localProcessConfig, includeSameNamespaceServices: !isDapr, cancellationToken);
#region localPorts
// Ports exposed by the user workload
IEnumerable<int> ports = new List<int>();
if (remoteContainerConnectionDetails.Service != null)
{
var servicePorts = remoteContainerConnectionDetails.Service.Spec?.Ports?.Select(p => GetServicePortValue(p, container)) ?? new List<int>();
// Check for DAPR
if (isDapr)
{
// We are targeting a DAPR container, it might be that the service is pointing to the user's workload container, or it might be pointing to the sidecar
// If there are ports that are both in the service and the container we might assume that the service is pointing to the user's workload
ports = servicePorts.Intersect(container.Ports?.Select(p => p.ContainerPort) ?? new int[0]);
if (ports.Any())
{
// The service is actually targeting the user's workload, let's use all the services port.
ports = servicePorts;
}
else
{
// The service is actually targeting the DAPR sidecar, we should fetch the port from the container spec
// NOTE: it is not required to declare the ports in the container spec, let's hope that the user did that otherwise there is no way for us to know what ports to map
ports = container.Ports?.Select(p => p.ContainerPort) ?? new List<int>();
if (!ports.Any())
{
_log.Info("No container ports found");
}
}
}
else
{
// NO DAPR, behave as usual
ports = servicePorts;
}
}
else
{
if (remoteContainerConnectionDetails.Pod != null)
{
// We don't have a service, but we do have a Pod
var services = await GetServicesPointingToPod(remoteContainerConnectionDetails.Pod, cancellationToken);
ports = services.SelectMany(svc => svc.Spec.Ports).Distinct().Select(p => GetServicePortValue(p, container));
}
else if (remoteContainerConnectionDetails.Deployment != null)
{
var services = await GetServicesFromDeployment(remoteContainerConnectionDetails.Deployment, cancellationToken);
ports = services.SelectMany(svc => svc.Spec.Ports).Distinct().Select(p => GetServicePortValue(p, container));
}
}
if (!ports.Any())
{
_log.Info("The remote workload is not exposing any port");
}
foreach (var port in ports)
{
var cp = new PortForwardStartInfo();
cp.Port = port;
List<string> probes = new List<string>();
if (container.LivenessProbe?.HttpGet != null)
{
probes.Add(container.LivenessProbe.HttpGet.Path);
}
if (container.ReadinessProbe?.HttpGet != null)
{
probes.Add(container.ReadinessProbe.HttpGet.Path);
}
cp.HttpProbes = probes.Distinct().ToArray();
workloadInfo.ReversePortForwardInfo = workloadInfo.ReversePortForwardInfo.Append(cp);
}
#endregion localPorts
// Check for DAPR
if (workloadInfo.EnvironmentVariables.TryGetValue("DAPR_HTTP_PORT", out string httpPort) &&
workloadInfo.EnvironmentVariables.TryGetValue("DAPR_GRPC_PORT", out string grpcPort))
{
_log.Event("DaprDetected", new Dictionary<string, object> { { "DAPR_HTTP_PORT", httpPort }, { "DAPR_GRPC_PORT", grpcPort } });
int httpPortInt, grpcPortInt;
try
{
httpPortInt = int.Parse(httpPort);
grpcPortInt = int.Parse(grpcPort);
}
catch (Exception ex)
{
_log.Exception(ex);
throw new UserVisibleException(_operationContext, $"Unable to parse DAPR ports: {ex.Message}");
}
workloadInfo.ReachableEndpoints = workloadInfo.ReachableEndpoints.Append(new EndpointInfo
{
Ports = new PortPair[] {
new PortPair(Common.Constants.IP.PortPlaceHolder, httpPortInt), //NOTE: The order in this array is important as LocalEnvironmentManager relies on it.
new PortPair(Common.Constants.IP.PortPlaceHolder, grpcPortInt) },
DnsName = Common.Constants.DAPR,
LocalIP = IPAddress.Loopback
});
}
perfLogger.SetSucceeded();
return workloadInfo;
}
}