public IDictionary CreateEnvVariablesForK8s()

in src/library/Connect/LocalEnvironmentManager.cs [423:521]


        public IDictionary<string, string> CreateEnvVariablesForK8s(WorkloadInfo workloadInfo)
        {
            var result = new Dictionary<string, string>(workloadInfo.EnvironmentVariables);

            foreach (var endpoint in workloadInfo.ReachableEndpoints)
            {
                if (endpoint.Ports == null || endpoint.Ports.Length == 0)
                {
                    _log.Verbose("Skipping endpoint {0} as it has no configured port", endpoint.DnsName);
                    continue;
                }

                if (string.Equals(endpoint.DnsName, DAPR, StringComparison.OrdinalIgnoreCase))
                {
                    // Override the DAPR env variables with the real local ports (that might be different if we neeeded to re-allocate them)
                    result["DAPR_HTTP_PORT"] = endpoint.Ports[0].LocalPort.ToString(); // TODO (lolodi): this is a hack that relies on the HTTP port to always be the first and GRPC port the second.
                    result["DAPR_GRPC_PORT"] = endpoint.Ports[1].LocalPort.ToString(); // We should probably name the port pairs (maybe with the env variable that we want to set with them).
                                                                                       // Once we do that, we can actually stop assigning the DAPR dns name ot this endpoint and just leave it empty, consistently with how the Remote agent works
                }

                // because we are using dns name instead of service we have to retrieve it by splitting when needed
                // If this ever cause issues we should consider larger refactor where we add serviceName member variable to EndpointInfo class.
                var dnsNameArray = endpoint.DnsName
                    .ToUpperInvariant()
                    .Split(".");

                var serviceName = dnsNameArray
                    .First()
                    .Replace("-", "_");

                // when !endpoint.IsInWorkloadNamespace
                var serviceNs = dnsNameArray.Length == 2
                    ? dnsNameArray
                        .Last()
                        .Replace("-", "_")
                    : null;

                // sometimes cross talk is desired between namespaces, append those not matching workload
                if (!string.IsNullOrWhiteSpace(serviceNs))
                {
                    serviceName = $"{serviceName}_{serviceNs}";
                }

                var host = _useKubernetesServiceEnvironmentVariables || string.Equals(endpoint.DnsName, DAPR, StringComparison.OrdinalIgnoreCase)
                    ? endpoint.LocalIP.ToString()
                    : endpoint.DnsName;

                if (string.Equals(serviceName, "KUBERNETES", StringComparison.OrdinalIgnoreCase))
                {
                    // reset KUBERNETES_SECRET_HOST to cluster name
                    host = _kubernetesClient.HostName;
                }

                // Service Host
                result[$"{serviceName}_SERVICE_HOST"] = host;

                // Service Port
                // tl;dr: allocate the first port in the service to the backwards-compatible environment variable in keeping with Kubernetes source code.
                // These environment variables are distinct per service definition as they do not contain metadata that distinguish them apart from other port mapping variables.
                // Kubernetes currently appears to bind the first port in the service definition to these ports, as such, we are initializing them in the outer loop here so
                // they cannot be overwritten by the final iteration of the inner loop below which would end up setting them to the final port in the collection
                var unnamedPort = _useKubernetesServiceEnvironmentVariables || string.Equals(endpoint.DnsName, DAPR, StringComparison.OrdinalIgnoreCase)
                    ? endpoint.Ports[0].LocalPort.ToString()
                    : endpoint.Ports[0].RemotePort.ToString();

                result[$"{serviceName}_SERVICE_PORT"] = unnamedPort;
                result[$"{serviceName}_PORT"] = $"{endpoint.Ports[0].Protocol}://{host}:{unnamedPort}";

                // All named ports (only the first may be unnamed according to Kubernetes source code)
                foreach (var portPair in endpoint.Ports)
                {
                    var port = _useKubernetesServiceEnvironmentVariables || string.Equals(endpoint.DnsName, DAPR, StringComparison.OrdinalIgnoreCase)
                        ? portPair.LocalPort
                        : portPair.RemotePort;

                    var protocolUpper = string.IsNullOrWhiteSpace(portPair.Protocol)
                        ? KubernetesConstants.Protocols.Tcp.ToUpperInvariant()
                        : portPair.Protocol.ToUpperInvariant();

                    result[$"{serviceName}_PORT_{port}_{protocolUpper}_PROTO"] = portPair.Protocol;
                    result[$"{serviceName}_PORT_{port}_{protocolUpper}"] = $"{portPair.Protocol}://{host}:{port}";
                    result[$"{serviceName}_PORT_{port}_{protocolUpper}_PORT"] = port.ToString();
                    result[$"{serviceName}_PORT_{port}_{protocolUpper}_ADDR"] = host;

                    if (!string.IsNullOrWhiteSpace(portPair.Name))
                    {
                        result[$"{serviceName}_SERVICE_PORT_{portPair.Name.ToUpperInvariant()}"] = port.ToString();
                    }

                    // if this is managed identity with useKubernetesServiceEnvironmentVariables set to true we have to update ms endpoint variable from dns name to ip:port
                    if (_useKubernetesServiceEnvironmentVariables && string.Equals(serviceName, ManagedIdentity.TargetServiceNameOnLocalMachine, StringComparison.OrdinalIgnoreCase))
                    {
                        result[ManagedIdentity.MSI_ENDPOINT_EnvironmentVariable] = $"http://{host}:{port}/metadata/identity/oauth2/token";
                    }
                }
            }

            return result;
        }