Esempio n. 1
0
        /// <summary>
        /// Updates the running proxies to match the current cluster
        /// (if there is one).  This may only be called on the UI thread.
        /// </summary>
        private async Task UpdateProxiesAsync()
        {
            if (InvokeRequired)
            {
                throw new InvalidOperationException($"[{nameof(UpdateProxiesAsync)}()] may only be called on the UI thread.");
            }

            if (KubeHelper.CurrentContext == null)
            {
                StopProxies();
                StopPortForwards();
            }
            else
            {
                var cluster = Program.GetCluster();

                // We're going to use the current Kubenetes context name and cluster ID
                // to determine whether we're still connected to the same cluster.

                if (proxiedContext != null)
                {
                    if (proxiedContext.Name == KubeHelper.CurrentContext.Name &&
                        proxiedContext.Extension.ClusterId == KubeHelper.CurrentContext.Extension.ClusterId)
                    {
                        // We're still proxying the same cluster so no changes
                        // are required.

                        return;
                    }
                }

                StopProxies();
                StopPortForwards();

                if (KubeHelper.CurrentContext == null)
                {
                    // Wr're not logged into a cluster so don't start any proxies.

                    return;
                }

                try
                {
                    // Start the connecting animation if we're not already in the error state.

                    if (!InErrorState)
                    {
                        StartNotifyAnimation(connectingAnimation, $"{KubeHelper.CurrentContextName}: Connecting...", isTransient: true);
                    }

                    //-------------------------------------------------------------
                    // The Kubernetes dashboard reverse proxy.

                    // Setup a callback that transparently adds an [Authentication] header
                    // to all requests with the correct bearer token.  We'll need to
                    // obtain the token secret via two steps:
                    //
                    //      1. Identify the dashboard token secret by listing all secrets
                    //         in the [kube-system] namespace looking for one named like
                    //         [root-user-token-*].
                    //
                    //      2. Reading that secret and extracting the value.

                    var response       = (ExecuteResponse)null;
                    var secretName     = string.Empty;
                    var dashboardToken = string.Empty;

                    await Task.Run(
                        async() =>
                    {
                        response = KubeHelper.Kubectl("--namespace", "kube-system", "get", "secrets", "-o=name");
                        await Task.CompletedTask;
                    });

                    if (response.ExitCode != 0)
                    {
                        try
                        {
                            response.EnsureSuccess();
                        }
                        catch (Exception e)
                        {
                            Program.LogError(e);
                            SetErrorState($"{KubeHelper.CurrentContextName}: Kubernetes API failure");
                            return;
                        }
                    }

                    // Step 1: Determine the secret name.

                    using (var reader = new StringReader(response.OutputText))
                    {
                        const string secretPrefix = "secret/";

                        secretName = reader.Lines().FirstOrDefault(line => line.StartsWith($"{secretPrefix}root-user-token-"));

                        Covenant.Assert(!string.IsNullOrEmpty(secretName));

                        secretName = secretName.Substring(secretPrefix.Length);
                    }

                    // Step 2: Describe the secret and extract the token value.  This
                    //         is a bit of a hack because I'm making assumptions about
                    //         the output format.

                    await Task.Run(
                        async() =>
                    {
                        response = KubeHelper.Kubectl("--namespace", "kube-system", "describe", "secret", secretName);
                        await Task.CompletedTask;
                    });

                    if (response.ExitCode != 0)
                    {
                        try
                        {
                            response.EnsureSuccess();
                        }
                        catch (Exception e)
                        {
                            Program.LogError(e);
                            SetErrorState($"{KubeHelper.CurrentContextName}: Kubernetes API failure");
                            return;
                        }
                    }

                    using (var reader = new StringReader(response.OutputText))
                    {
                        var tokenLine = reader.Lines().FirstOrDefault(line => line.StartsWith("token:"));

                        Covenant.Assert(!string.IsNullOrEmpty(tokenLine));

                        dashboardToken = tokenLine.Split(new char[] { ' ' }, 2).Skip(1).First().Trim();
                    }

                    Action <RequestContext> dashboardRequestHandler =
                        context =>
                    {
                        context.Request.Headers.Add("Authorization", $"Bearer {dashboardToken}");
                    };

                    // Start the proxy.

                    var userContext   = KubeHelper.Config.GetUser(KubeHelper.CurrentContext.Properties.User);
                    var certPem       = Encoding.UTF8.GetString(Convert.FromBase64String(userContext.Properties.ClientCertificateData));
                    var keyPem        = Encoding.UTF8.GetString(Convert.FromBase64String(userContext.Properties.ClientKeyData));
                    var dashboardCert = TlsCertificate.Parse(KubeHelper.CurrentContext.Extension.KubernetesDashboardCertificate).ToX509(publicOnly: true);

                    var kubeDashboardProxy =
                        new ReverseProxy(
                            localPort: KubeHelper.ClientConfig.KubeDashboardProxyPort,
                            remotePort: KubeHostPorts.KubeDashboard,
                            remoteHost: cluster.GetReachableMaster().PrivateAddress.ToString(),
                            validCertificate: dashboardCert,
                            requestHandler: dashboardRequestHandler);

                    proxies.Add(kubeDashboardProxy);

                    var kibanaDashboardProxy =
                        new PortForward(
                            serviceName: "kibana-kibana",
                            localPort: KubeConst.KibanaDashboardProxyPort,
                            remotePort: KubeConst.KibanaDashboardProxyPort,
                            @namespace: "logging");

                    portForwards.Add(kibanaDashboardProxy);

                    var prometheusDashboardProxy =
                        new PortForward(
                            serviceName: "prometheus",
                            localPort: KubeConst.PrometheusDashboardProxyPort,
                            remotePort: KubeConst.PrometheusDashboardProxyPort,
                            @namespace: "istio-system");

                    portForwards.Add(prometheusDashboardProxy);

                    var kialiDashboardProxy =
                        new PortForward(
                            serviceName: "kiali",
                            localPort: KubeConst.KialiDashboardProxyPort,
                            remotePort: KubeConst.KialiDashboardProxyPort,
                            @namespace: "istio-system");

                    portForwards.Add(kialiDashboardProxy);

                    var grafanaDashboardProxy =
                        new PortForward(
                            serviceName: "grafana",
                            localPort: KubeConst.GrafanaDashboardProxyPort,
                            remotePort: KubeConst.GrafanaDashboardProxyPort,
                            @namespace: "istio-system");

                    portForwards.Add(grafanaDashboardProxy);

                    //-------------------------------------------------------------
                    // Remember which cluster context we're proxying.

                    proxiedContext = KubeHelper.CurrentContext;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
                finally
                {
                    // Stop any non-error transient animation on the top of the notify stack.

                    if (notifyStack.Count > 0 && notifyStack.Peek().IsTransient&& !notifyStack.Peek().IsError)
                    {
                        StopNotifyAnimation();
                    }
                }
            }
        }