/// <summary> /// <para> /// Connects to a Kubernetes cluster if it already exists. This sets the <see cref="KubeSetupProperty.K8sClient"/> /// property in the setup controller state when Kubernetes is running and a connection has not already /// been established. /// </para> /// <note> /// The <see cref="KubeSetupProperty.K8sClient"/> will not be set when Kubernetes has not been started, so /// <see cref="ObjectDictionary.Get{TValue}(string)"/> calls for this property will fail when the /// cluster has not been connected yet, which will be useful for debugging setup steps that require /// a connection but this hasn't happened yet. /// </note> /// </summary> /// <param name="controller">The setup controller.</param> public static void ConnectCluster(ISetupController controller) { Covenant.Requires <ArgumentNullException>(controller != null, nameof(controller)); if (controller.ContainsKey(KubeSetupProperty.K8sClient)) { return; // Already connected } var cluster = controller.Get <ClusterProxy>(KubeSetupProperty.ClusterProxy); var configFile = GetCurrentKubeConfigPath(); if (!string.IsNullOrEmpty(configFile) && File.Exists(configFile)) { // We're using a generated wrapper class to handle transient retries rather than // modifying the built-in base retry policy. We're really just trying to handle // the transients that happen during setup when the API server is unavailable for // some reaon (like it's being restarted). var k8s = new KubernetesWithRetry(KubernetesClientConfiguration.BuildConfigFromConfigFile(configFile, currentContext: cluster.KubeContext.Name)); k8s.RetryPolicy = new ExponentialRetryPolicy( transientDetector: exception => { var exceptionType = exception.GetType(); // Exceptions like this happen when a API server connection can't be established // because the server isn't running or ready. if (exceptionType == typeof(HttpRequestException) && exception.InnerException != null && exception.InnerException.GetType() == typeof(SocketException)) { return(true); } var httpOperationException = exception as HttpOperationException; if (httpOperationException != null) { var statusCode = httpOperationException.Response.StatusCode; switch (statusCode) { case HttpStatusCode.GatewayTimeout: case HttpStatusCode.InternalServerError: case HttpStatusCode.RequestTimeout: case HttpStatusCode.ServiceUnavailable: case (HttpStatusCode)423: // Locked case (HttpStatusCode)429: // Too many requests return(true); } } // This might be another variant of the check just above. This looks like an SSL negotiation problem. if (exceptionType == typeof(HttpRequestException) && exception.InnerException != null && exception.InnerException.GetType() == typeof(IOException)) { return(true); } return(false); }, maxAttempts: int.MaxValue, initialRetryInterval: TimeSpan.FromSeconds(1), maxRetryInterval: TimeSpan.FromSeconds(5), timeout: TimeSpan.FromMinutes(5)); controller.Add(KubeSetupProperty.K8sClient, k8s); } }