Esempio n. 1
0
        /// <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);
        }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        /// <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);
            }
        }
Esempio n. 6
0
        /// <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));
        }
Esempio n. 7
0
        /// <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);
        }
Esempio n. 9
0
        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);
            }
        }
Esempio n. 11
0
        /// <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);
            }
        }