Exemplo n.º 1
0
        /// <summary>
        /// <para>
        /// Ensures that <b>kubectl</b> tool whose version is at least as great as the Kubernetes
        /// cluster version is installed to the <b>neonKUBE</b> programs folder by copying the
        /// tool from the cache if necessary.
        /// </para>
        /// <note>
        /// This will probably require elevated privileges.
        /// </note>
        /// <note>
        /// This assumes that <b>kubectl</b> has already been downloaded and cached and also that
        /// more recent <b>kubectl</b> releases are backwards compatible with older deployed versions
        /// of Kubernetes.
        /// </note>
        /// </summary>
        /// <param name="setupInfo">The KUbernetes setup information.</param>
        public static void InstallKubeCtl(KubeSetupInfo setupInfo)
        {
            Covenant.Requires <ArgumentNullException>(setupInfo != null);

            var hostPlatform      = KubeHelper.HostPlatform;
            var cachedKubeCtlPath = KubeHelper.GetCachedComponentPath(hostPlatform, "kubectl", setupInfo.Versions.Kubernetes);
            var targetPath        = Path.Combine(KubeHelper.ProgramFolder);

            switch (hostPlatform)
            {
            case KubeHostPlatform.Windows:

                targetPath = Path.Combine(targetPath, "kubectl.exe");

                // Ensure that the KUBECONFIG environment variable exists and includes
                // the path to the user's [.neonkube] configuration.

                var kubeConfigVar = Environment.GetEnvironmentVariable("KUBECONFIG");

                if (string.IsNullOrEmpty(kubeConfigVar))
                {
                    // The [KUBECONFIG] environment variable doesn't exist so we'll set it.

                    Registry.SetValue(@"HKEY_CURRENT_USER\Environment", "KUBECONFIG", KubeConfigPath, RegistryValueKind.ExpandString);
                    Environment.SetEnvironmentVariable("KUBECONFIG", KubeConfigPath);
                }
                else
                {
                    // The [KUBECONFIG] environment variable exists but we still need to
                    // ensure that the path to our [USER/.neonkube] config is present.

                    var sb    = new StringBuilder();
                    var found = false;

                    foreach (var path in kubeConfigVar.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
                    {
                        if (path == KubeConfigPath)
                        {
                            found = true;
                        }

                        sb.AppendWithSeparator(path, ";");
                    }

                    if (!found)
                    {
                        sb.AppendWithSeparator(KubeConfigPath, ";");
                    }

                    var newKubeConfigVar = sb.ToString();

                    if (newKubeConfigVar != kubeConfigVar)
                    {
                        Registry.SetValue(@"HKEY_CURRENT_USER\Environment", "KUBECONFIG", newKubeConfigVar, RegistryValueKind.ExpandString);
                        Environment.SetEnvironmentVariable("KUBECONFIG", newKubeConfigVar);
                    }
                }

                if (!File.Exists(targetPath))
                {
                    File.Copy(cachedKubeCtlPath, targetPath);
                }
                else
                {
                    // Execute the existing target to obtain its version and update it
                    // to the cached copy if the cluster installed a more recent version
                    // of Kubernetes.

                    // $hack(jeff.lill): Simple client version extraction

                    var pattern  = "GitVersion:\"v";
                    var response = NeonHelper.ExecuteCapture(targetPath, "version");
                    var pStart   = response.OutputText.IndexOf(pattern);
                    var error    = "Cannot identify existing [kubectl] version.";

                    if (pStart == -1)
                    {
                        throw new KubeException(error);
                    }

                    pStart += pattern.Length;

                    var pEnd = response.OutputText.IndexOf("\"", pStart);

                    if (pEnd == -1)
                    {
                        throw new KubeException(error);
                    }

                    var currentVersionString = response.OutputText.Substring(pStart, pEnd - pStart);

                    if (!Version.TryParse(currentVersionString, out var currentVersion))
                    {
                        throw new KubeException(error);
                    }

                    if (Version.Parse(setupInfo.Versions.Kubernetes) > currentVersion)
                    {
                        // We need to copy the latest version.

                        if (File.Exists(targetPath))
                        {
                            File.Delete(targetPath);
                        }

                        File.Copy(cachedKubeCtlPath, targetPath);
                    }
                }
                break;

            case KubeHostPlatform.Linux:
            case KubeHostPlatform.Osx:
            default:

                throw new NotImplementedException($"[{hostPlatform}] support is not implemented.");
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// <para>
        /// Ensures that <b>helm</b> tool whose version is at least as great as the requested
        /// cluster version is installed to the <b>neonKUBE</b> programs folder by copying the
        /// tool from the cache if necessary.
        /// </para>
        /// <note>
        /// This will probably require elevated privileges.
        /// </note>
        /// <note>
        /// This assumes that <b>Helm</b> has already been downloaded and cached and also that
        /// more recent <b>Helm</b> releases are backwards compatible with older deployed versions
        /// of Tiller.
        /// </note>
        /// </summary>
        /// <param name="setupInfo">The KUbernetes setup information.</param>
        public static void InstallHelm(KubeSetupInfo setupInfo)
        {
            Covenant.Requires <ArgumentNullException>(setupInfo != null);

            var hostPlatform   = KubeHelper.HostPlatform;
            var cachedHelmPath = KubeHelper.GetCachedComponentPath(hostPlatform, "helm", setupInfo.Versions.Helm);
            var targetPath     = Path.Combine(KubeHelper.ProgramFolder);

            switch (hostPlatform)
            {
            case KubeHostPlatform.Windows:

                targetPath = Path.Combine(targetPath, "helm.exe");

                if (!File.Exists(targetPath))
                {
                    File.Copy(cachedHelmPath, targetPath);
                }
                else
                {
                    // Execute the existing target to obtain its version and update it
                    // to the cached copy if the cluster installed a more recent version
                    // of Kubernetes.

                    // $hack(jeff.lill): Simple client version extraction

                    var pattern  = "SemVer:\"v";
                    var response = NeonHelper.ExecuteCapture(targetPath, "version");
                    var pStart   = response.OutputText.IndexOf(pattern);
                    var error    = "Cannot identify existing [helm] version.";

                    if (pStart == -1)
                    {
                        throw new KubeException(error);
                    }

                    pStart += pattern.Length;

                    var pEnd = response.OutputText.IndexOf("\"", pStart);

                    if (pEnd == -1)
                    {
                        throw new KubeException(error);
                    }

                    var currentVersionString = response.OutputText.Substring(pStart, pEnd - pStart);

                    if (!Version.TryParse(currentVersionString, out var currentVersion))
                    {
                        throw new KubeException(error);
                    }

                    if (Version.Parse(setupInfo.Versions.Helm) > currentVersion)
                    {
                        // We need to copy the latest version.

                        File.Copy(cachedHelmPath, targetPath);
                    }
                }
                break;

            case KubeHostPlatform.Linux:
            case KubeHostPlatform.Osx:
            default:

                throw new NotImplementedException($"[{hostPlatform}] support is not implemented.");
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Returns information required for setting up a Kubernetes cluster.  This
        /// includes things like the URIs to be used for downloading the <b>kubectl</b>
        /// and <b>kubeadm</b> tools.
        /// </summary>
        /// <param name="clusterDefinition">The cluster definition.</param>
        /// <returns>A <see cref="KubeSetupInfo"/> with the information.</returns>
        public async Task <KubeSetupInfo> GetSetupInfoAsync(ClusterDefinition clusterDefinition)
        {
            await SyncContext.ClearAsync;

            Covenant.Requires <ArgumentNullException>(clusterDefinition != null, nameof(clusterDefinition));

            var kubeVersion = Version.Parse(defaultKubeVersion);

            if (!clusterDefinition.Kubernetes.Version.Equals("default", StringComparison.InvariantCultureIgnoreCase))
            {
                kubeVersion = Version.Parse(clusterDefinition.Kubernetes.Version);
            }

            var kubeDashboardVersion = Version.Parse(defaultKubeDashboardVersion);

            if (!clusterDefinition.Kubernetes.DashboardVersion.Equals("default", StringComparison.InvariantCultureIgnoreCase))
            {
                kubeDashboardVersion = Version.Parse(clusterDefinition.Kubernetes.DashboardVersion);
            }

            // Ensure that the we have package versions defined for the selected Kubernetes version.

            Covenant.Assert(ubuntuKubeAdmPackages.ContainsKey(kubeVersion.ToString()));
            Covenant.Assert(ubuntuKubeCtlPackages.ContainsKey(kubeVersion.ToString()));
            Covenant.Assert(ubuntuKubeletPackages.ContainsKey(kubeVersion.ToString()));

            // $todo(jefflill): Hardcoded
            // $todo(jefflill): Verify Docker/Kubernetes version compatibility.

            var dockerVersion = clusterDefinition.Docker.Version;

            if (dockerVersion == "default")
            {
                dockerVersion = defaultDockerVersion;
            }

            if (supportedDockerVersions.SingleOrDefault(v => v == dockerVersion) == null)
            {
                throw new KubeException($"[{dockerVersion}] is not a supported Docker version.");
            }

            var helmVersion = clusterDefinition.Kubernetes.HelmVersion;

            if (helmVersion == "default")
            {
                helmVersion = defaultHelmVersion;
            }

            if (supportedHelmVersions.SingleOrDefault(v => v == helmVersion) == null)
            {
                throw new KubeException($"[{helmVersion}] is not a supported Helm version.");
            }

            // $todo(jefflill):
            //
            // The code below supports only the Calico CNI for now.  This will probably be
            // replaced by the integrated Istio CNI soon.

            if (clusterDefinition.Network.Cni != NetworkCni.Calico)
            {
                throw new NotImplementedException($"The [{clusterDefinition.Network.Cni}] CNI is not currently supported.");
            }

            var calicoVersion = clusterDefinition.Network.CniVersion;

            if (calicoVersion == "default")
            {
                calicoVersion = defaultCalicoVersion;
            }

            if (clusterDefinition.Network.Cni == NetworkCni.Calico && supportedCalicoVersions.SingleOrDefault(v => v == calicoVersion) == null)
            {
                throw new KubeException($"[{calicoVersion}] is not a supported Calico version.");
            }

            var istioVersion = clusterDefinition.Network.IstioVersion;

            if (istioVersion == "default")
            {
                istioVersion = defaultIstioVersion;
            }

            if (supportedIstioVersions.SingleOrDefault(v => v == istioVersion) == null)
            {
                throw new KubeException($"[{istioVersion}] is not a supported Istio version.");
            }

            var setupInfo = new KubeSetupInfo()
            {
                KubeAdmLinuxUri = $"https://storage.googleapis.com/kubernetes-release/release/v{kubeVersion}/linux/amd64/kubeadm",
                KubeCtlLinuxUri = $"https://storage.googleapis.com/kubernetes-release/release/v{kubeVersion}/linux/amd64/kubectl",
                KubeletLinuxUri = $"https://storage.googleapis.com/kubernetes-release/release/v{kubeVersion}/linux/amd64/kubelet",

                KubeCtlOsxUri = $"https://storage.googleapis.com/kubernetes-release/release/v{kubeVersion}/bin/darwin/amd64/kubectl",

                KubeCtlWindowsUri = $"https://storage.googleapis.com/kubernetes-release/release/v{kubeVersion}/bin/windows/amd64/kubectl.exe",

                DockerPackageUbuntuUri      = $"https://s3-us-west-2.amazonaws.com/neonforge/kube/{dockerVersion}-ubuntu-bionic-stable-amd64.deb",
                KubeAdmPackageUbuntuVersion = ubuntuKubeAdmPackages[kubeVersion.ToString()],
                KubeCtlPackageUbuntuVersion = ubuntuKubeCtlPackages[kubeVersion.ToString()],
                KubeletPackageUbuntuVersion = ubuntuKubeletPackages[kubeVersion.ToString()],

                HelmLinuxUri   = $"https://storage.googleapis.com/kubernetes-helm/helm-v{helmVersion}-linux-amd64.tar.gz",
                HelmOsxUri     = $"https://storage.googleapis.com/kubernetes-helm/helm-v{helmVersion}-darwin-amd64.tar.gz",
                HelmWindowsUri = $"https://storage.googleapis.com/kubernetes-helm/helm-v{helmVersion}-windows-amd64.zip",

                // $todo(jefflill):
                //
                // I'm a little worried about where the "1.7" in the [CalicoSetupUri] came from.  I suspect that
                // this will vary too.  Once the Istio CNI is stable, we'll probably delete this anyway but if
                // we do decide to allow for custom CNIs, we'll need to figure this out.

                CalicoRbacYamlUri  = $"https://docs.projectcalico.org/v{calicoVersion}/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml",
                CalicoSetupYamlUri = $"https://docs.projectcalico.org/v{calicoVersion}/manifests/calico.yaml",

                IstioLinuxUri = $"https://github.com/istio/istio/releases/download/{istioVersion}/istio-{istioVersion}-linux.tar.gz"
            };

            var kubeVersionString = kubeVersion.ToString();

            setupInfo.Versions.Kubernetes = kubeVersionString;

            var kubeDashboardConfig = KubeDashboardConfig.GetDashboardConfigFor(kubeVersion.ToString());

            setupInfo.KubeDashboardYaml            = kubeDashboardConfig.ConfigYaml;
            setupInfo.Versions.KubernetesDashboard = kubeDashboardConfig.Version;

            setupInfo.Versions.Docker = dockerVersion;
            setupInfo.Versions.Helm   = helmVersion;
            setupInfo.Versions.Calico = calicoVersion;
            setupInfo.Versions.Istio  = istioVersion;

            await Task.CompletedTask;

            return(setupInfo);
        }