/// <summary> /// Gets the pod the current container is running upon. /// </summary> /// <returns></returns> public async Task <K8sPod> GetMyPodAsync() { EnsureNotDisposed(); var allPods = await GetPodsAsync().ConfigureAwait(false); K8sPod targetPod = null; string podName = Environment.GetEnvironmentVariable(@"APPINSIGHTS_KUBERNETES_POD_NAME"); string myContainerId = KubeHttpClient.Settings.ContainerId; if (!string.IsNullOrEmpty(podName)) { targetPod = allPods.FirstOrDefault(p => p.Metadata.Name.Equals(podName, StringComparison.Ordinal)); } else if (!string.IsNullOrEmpty(myContainerId)) { targetPod = allPods.FirstOrDefault(pod => pod.Status != null && pod.Status.ContainerStatuses != null && pod.Status.ContainerStatuses.Any( cs => !string.IsNullOrEmpty(cs.ContainerID) && cs.ContainerID.EndsWith(myContainerId, StringComparison.Ordinal))); } else { _logger.LogError("Neither container id nor %APPINSIGHTS_KUBERNETES_POD_NAME% could be fetched."); } return(targetPod); }
/// <summary> /// Wait until the container is ready. /// Refer document @ https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase for the Pod's lifecycle. /// </summary> /// <param name="timeout">Timeout on Application Insights data when the container is not ready after the period.</param> /// <param name="client">Query client to try getting info from the Kubernetes cluster API.</param> /// <param name="myContainerId">The container that we are interested in.</param> /// <returns></returns> private static async Task <bool> SpinWaitContainerReady(TimeSpan timeout, K8sQueryClient client, string myContainerId, ILogger <K8sEnvironment> logger) { DateTime tiemoutAt = DateTime.Now.Add(timeout); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); K8sPod myPod = null; do { // When my pod become available and it's status become ready, we recognize the container is ready. myPod = await client.GetMyPodAsync().ConfigureAwait(false); if (myPod != null && myPod.GetContainerStatus(myContainerId).Ready) { stopwatch.Stop(); logger?.LogDebug(Invariant($"K8s info avaialbe in: {stopwatch.ElapsedMilliseconds} ms.")); return(true); } // The time to get the container ready dependes on how much time will a container to be initialized. // But the minimum seems about 1000ms. Try invoke a probe on readiness every 500ms until the container is ready // Or it will timeout per the timeout settings. await Task.Delay(500).ConfigureAwait(false); } while (DateTime.Now < tiemoutAt); return(false); }
public static ContainerStatus GetContainerStatus(this K8sPod self, string containerId) { ContainerStatus result = self.Status.ContainerStatuses?.FirstOrDefault( cs => !string.IsNullOrEmpty(cs.ContainerID) && cs.ContainerID.EndsWith(containerId, StringComparison.Ordinal)); return(result); }
/// <summary> /// Wait until the container is ready. /// Refer document @ https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase for the Pod's lifecycle. /// </summary> /// <param name="timeout">Timeout on Application Insights data when the container is not ready after the period.</param> /// <param name="client">Query client to try getting info from the Kubernetes cluster API.</param> /// <param name="myContainerId">The container that we are interested in.</param> /// <returns></returns> private async Task <bool> SpinWaitContainerReady(DateTime timeoutAt, K8sQueryClient client, string myContainerId) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); K8sPod myPod = null; do { // When my pod become available and it's status become ready, we recognize the container is ready. try { myPod = await client.GetMyPodAsync().ConfigureAwait(false); } catch { myPod = null; } if (myPod != null && myPod.GetContainerStatus(myContainerId).Ready) { stopwatch.Stop(); _logger.LogDebug(Invariant($"K8s info avaialbe in: {stopwatch.ElapsedMilliseconds} ms.")); return(true); } // The time to get the container ready dependes on how much time will a container to be initialized. // When there is readiness probe, the pod info will not be available until the initial delay of it is elapsed. // When there is no readiness probe, the minimum seems about 1000ms. // Try invoke a probe on readiness every 500ms until the container is ready // Or it will timeout per the timeout settings. await Task.Delay(500).ConfigureAwait(false); } while (DateTime.Now < timeoutAt); return(false); }
/// <summary> /// Async factory method to build the instance of a K8sEnvironment. /// </summary> /// <returns></returns> public async Task <K8sEnvironment> CreateAsync(TimeSpan timeout) { K8sEnvironment instance = null; ILogger <K8sEnvironment> logger = null; try { using (IKubeHttpClient httpClient = _httpClientFactory.Create(_httpClientSettings)) using (K8sQueryClient queryClient = _k8sQueryClientFactory.Create(httpClient)) { string containerId = _httpClientSettings.ContainerId; if (await SpinWaitContainerReady(timeout, queryClient, containerId).ConfigureAwait(false)) { instance = new K8sEnvironment() { ContainerID = containerId }; K8sPod myPod = await queryClient.GetMyPodAsync().ConfigureAwait(false); instance.myPod = myPod; logger?.LogDebug(Invariant($"Getting container status of container-id: {containerId}")); instance.myContainerStatus = myPod.GetContainerStatus(containerId); IEnumerable <K8sReplicaSet> replicaSetList = await queryClient.GetReplicasAsync().ConfigureAwait(false); instance.myReplicaSet = myPod.GetMyReplicaSet(replicaSetList); if (instance.myReplicaSet != null) { IEnumerable <K8sDeployment> deploymentList = await queryClient.GetDeploymentsAsync().ConfigureAwait(false); instance.myDeployment = instance.myReplicaSet.GetMyDeployment(deploymentList); } if (instance.myPod != null) { IEnumerable <K8sNode> nodeList = await queryClient.GetNodesAsync().ConfigureAwait(false); string nodeName = instance.myPod.Spec.NodeName; if (!string.IsNullOrEmpty(nodeName)) { instance.myNode = nodeList.FirstOrDefault(node => !string.IsNullOrEmpty(node.Metadata?.Name) && node.Metadata.Name.Equals(nodeName, StringComparison.OrdinalIgnoreCase)); } } } else { logger?.LogError(Invariant($"Kubernetes info is not available within given time of {timeout.TotalMilliseconds} ms.")); } } return(instance); } catch (Exception ex) { logger?.LogCritical(ex.ToString()); return(null); } }
/// <summary> /// Gets the container status for the pod, where the current container is running upon. /// </summary> /// <returns></returns> public async Task <ContainerStatus> GetMyContainerStatusAsync() { EnsureNotDisposed(); string myContainerId = KubeHttpClient.Settings.ContainerId; K8sPod myPod = await GetMyPodAsync().ConfigureAwait(false); return(myPod.GetContainerStatus(myContainerId)); }
/// <summary> /// Gets the ReplicaSet for the current pod. /// </summary> /// <param name="self">The target pod.</param> /// <param name="scope">List of replicas to search from.</param> /// <returns>Returns the replicaSet of the pod. Returns null when the data doens't exist.</returns> public static K8sReplicaSet GetMyReplicaSet(this K8sPod self, IEnumerable <K8sReplicaSet> scope) { OwnerReference replicaRef = self.Metadata?.OwnerReferences?.FirstOrDefault(owner => owner.GetKind() != null && owner.GetKind() == typeof(K8sReplicaSet)); if (replicaRef != null) { K8sReplicaSet replica = scope?.FirstOrDefault( r => r.Metadata != null && r.Metadata.Uid != null && r.Metadata.Uid.Equals(replicaRef.Uid, StringComparison.OrdinalIgnoreCase)); return(replica); } return(null); }
/// <summary> /// Get the pod the current container is running upon. /// </summary> /// <returns></returns> public async Task <K8sPod> GetMyPodAsync() { EnsureNotDisposed(); string myContainerId = KubeHttpClient.Settings.ContainerId; IEnumerable <K8sPod> possiblePods = await GetPodsAsync().ConfigureAwait(false); if (possiblePods == null) { return(null); } K8sPod targetPod = possiblePods.FirstOrDefault(pod => pod.Status != null && pod.Status.ContainerStatuses != null && pod.Status.ContainerStatuses.Any( cs => !string.IsNullOrEmpty(cs.ContainerID) && cs.ContainerID.EndsWith(myContainerId, StringComparison.Ordinal))); return(targetPod); }
private async Task <K8sPod> SpinWaitUntilGetPod(DateTime timeoutAt, K8sQueryClient client) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); K8sPod myPod = null; do { // When my pod become available and it's status become ready, we recognize the container is ready. try { myPod = await client.GetMyPodAsync().ConfigureAwait(false); } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) { _logger.LogWarning($"Query exception while trying to get pod info: {ex.Message}"); _logger.LogDebug(ex.ToString()); myPod = null; } #pragma warning restore CA1031 // Do not catch general exception types if (myPod != null) { stopwatch.Stop(); _logger.LogDebug(Invariant($"K8s info avaialbe in: {stopwatch.ElapsedMilliseconds} ms.")); return(myPod); } // The time to get the container ready dependes on how much time will a container to be initialized. // When there is readiness probe, the pod info will not be available until the initial delay of it is elapsed. // When there is no readiness probe, the minimum seems about 1000ms. // Try invoke a probe on readiness every 500ms until the container is ready // Or it will timeout per the timeout settings. await Task.Delay(500).ConfigureAwait(false); } while (DateTime.Now < timeoutAt); return(null); }
public async Task GetMyPodAsyncShouldGetCorrectPod() { var httpClientSettingsMock = GetKubeHttpClientSettingsProviderForTest(); var httpClientMock = new Mock <IKubeHttpClient>(); httpClientMock.Setup(httpClient => httpClient.Settings).Returns(httpClientSettingsMock.Object); httpClientMock.Setup(httpClient => httpClient.GetStringAsync(It.IsAny <Uri>())).Returns(Task.FromResult( JsonConvert.SerializeObject(new K8sEntityList <K8sPod>() { Items = new List <K8sPod> { new K8sPod() { Status = new K8sPodStatus() { ContainerStatuses = new List <ContainerStatus>() { new ContainerStatus() { ContainerID = "noizy in front" } } } }, new K8sPod() { Status = new K8sPodStatus() { ContainerStatuses = new List <ContainerStatus>() { new ContainerStatus() { ContainerID = "containerId" } } } }, new K8sPod() { Status = new K8sPodStatus() { ContainerStatuses = new List <ContainerStatus>() { new ContainerStatus() { ContainerID = "noizy after" } } } } } }))); using (K8sQueryClient target = new K8sQueryClient(httpClientMock.Object)) { K8sPod result = await target.GetMyPodAsync(); Assert.NotNull(result); Assert.Single(result.Status.ContainerStatuses); Assert.Equal("containerId", result.Status.ContainerStatuses.First().ContainerID); } }
/// <summary> /// Async factory method to build the instance of a K8sEnvironment. /// </summary> /// <returns></returns> public async Task <IK8sEnvironment> CreateAsync(DateTime timeoutAt) { K8sEnvironment instance = null; try { using (IKubeHttpClient httpClient = _httpClientFactory.Create(_httpClientSettings)) using (K8sQueryClient queryClient = _k8sQueryClientFactory.Create(httpClient)) { // TODO: See if there's better way to fetch the container id K8sPod myPod = await SpinWaitUntilGetPod(timeoutAt, queryClient).ConfigureAwait(false); if (myPod != null) { string containerId = null; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // For Windows, is there a way to fetch the current container id from within the container? containerId = myPod.Status.ContainerStatuses.First().ContainerID; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { // For Linux, container id could be fetched directly from cGroup. containerId = _httpClientSettings.ContainerId; } // ~ if (await SpinWaitContainerReady(timeoutAt, queryClient, containerId).ConfigureAwait(false)) { instance = new K8sEnvironment() { ContainerID = containerId }; instance.myPod = myPod; _logger.LogDebug(Invariant($"Getting container status of container-id: {containerId}")); instance.myContainerStatus = myPod.GetContainerStatus(containerId); IEnumerable <K8sReplicaSet> replicaSetList = await queryClient.GetReplicasAsync().ConfigureAwait(false); instance.myReplicaSet = myPod.GetMyReplicaSet(replicaSetList); if (instance.myReplicaSet != null) { IEnumerable <K8sDeployment> deploymentList = await queryClient.GetDeploymentsAsync().ConfigureAwait(false); instance.myDeployment = instance.myReplicaSet.GetMyDeployment(deploymentList); } if (instance.myPod != null) { IEnumerable <K8sNode> nodeList = await queryClient.GetNodesAsync().ConfigureAwait(false); string nodeName = instance.myPod.Spec.NodeName; if (!string.IsNullOrEmpty(nodeName)) { instance.myNode = nodeList.FirstOrDefault(node => !string.IsNullOrEmpty(node.Metadata?.Name) && node.Metadata.Name.Equals(nodeName, StringComparison.OrdinalIgnoreCase)); } } } else { _logger.LogError(Invariant($"Kubernetes info is not available before the timeout at {timeoutAt}.")); } } else { // MyPod is null, meaning query timed out. _logger.LogCritical("Fail to fetch the pod information in time. Kubernetes info will not be available for the telemetry."); return(null); } } return(instance); } catch (Exception ex) { _logger.LogCritical(ex.ToString()); return(null); } }