public async Task <T> DeleteNamespacedAsync <T>(string ns, string name, CancellationToken cancel = default) where T : IKubernetesObject { var resp = await kubernetes.DeleteNamespacedCustomObjectWithHttpMessagesAsync(group, version, ns, plural, name, cancellationToken : cancel).ConfigureAwait(false); return(KubernetesJson.Deserialize <T>(resp.Body.ToString())); }
public async Task <T> ListAsync <T>(CancellationToken cancel = default) where T : IKubernetesObject { var resp = await kubernetes.ListClusterCustomObjectWithHttpMessagesAsync(group, version, plural, cancellationToken : cancel).ConfigureAwait(false); return(KubernetesJson.Deserialize <T>(resp.Body.ToString())); }
internal static async IAsyncEnumerable <(WatchEventType, T)> CreateWatchEventEnumerator( Func <Task <TextReader> > streamReaderCreator, Action <Exception> onError = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { Task <TR> AttachCancellationToken <TR>(Task <TR> task) { if (!task.IsCompleted) { // here to pass cancellationToken into task return(task.ContinueWith(t => t.GetAwaiter().GetResult(), cancellationToken)); } return(task); } using var streamReader = await AttachCancellationToken(streamReaderCreator ()).ConfigureAwait(false); for (; ;) { // ReadLineAsync will return null when we've reached the end of the stream. var line = await AttachCancellationToken(streamReader.ReadLineAsync()).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); if (line == null) { yield break; } WatchEvent @event = null; try { var genericEvent = KubernetesJson.Deserialize <Watcher <KubernetesObject> .WatchEvent>(line); if (genericEvent.Object.Kind == "Status") { var statusEvent = KubernetesJson.Deserialize <Watcher <V1Status> .WatchEvent>(line); var exception = new KubernetesException(statusEvent.Object); onError?.Invoke(exception); } else { @event = KubernetesJson.Deserialize <WatchEvent>(line); } } catch (Exception e) { onError?.Invoke(e); } if (@event != null) { yield return(@event.Type, @event.Object); } } }
/// <summary> /// Implementation of the proposal for out-of-tree client /// authentication providers as described here -- /// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/auth/kubectl-exec-plugins.md /// Took inspiration from python exec_provider.py -- /// https://github.com/kubernetes-client/python-base/blob/master/config/exec_provider.py /// </summary> /// <param name="config">The external command execution configuration</param> /// <returns> /// The token, client certificate data, and the client key data received from the external command execution /// </returns> public static ExecCredentialResponse ExecuteExternalCommand(ExternalExecution config) { if (config == null) { throw new ArgumentNullException(nameof(config)); } var process = CreateRunnableExternalProcess(config); try { process.Start(); } catch (Exception ex) { throw new KubeConfigException($"external exec failed due to: {ex.Message}"); } var stdout = process.StandardOutput.ReadToEnd(); var stderr = process.StandardError.ReadToEnd(); if (string.IsNullOrWhiteSpace(stderr) == false) { throw new KubeConfigException($"external exec failed due to: {stderr}"); } // Wait for a maximum of 5 seconds, if a response takes longer probably something went wrong... process.WaitForExit(5); try { var responseObject = KubernetesJson.Deserialize <ExecCredentialResponse>(stdout); if (responseObject == null || responseObject.ApiVersion != config.ApiVersion) { throw new KubeConfigException( $"external exec failed because api version {responseObject.ApiVersion} does not match {config.ApiVersion}"); } if (responseObject.Status.IsValid()) { return(responseObject); } else { throw new KubeConfigException($"external exec failed missing token or clientCertificateData field in plugin output"); } } catch (JsonException ex) { throw new KubeConfigException($"external exec failed due to failed deserialization process: {ex}"); } catch (Exception ex) { throw new KubeConfigException($"external exec failed due to uncaught exception: {ex}"); } }
public async Task <int> NamespacedPodExecAsync(string name, string @namespace, string container, IEnumerable <string> command, bool tty, ExecAsyncCallback action, CancellationToken cancellationToken) { // All other parameters are being validated by MuxedStreamNamespacedPodExecAsync if (action == null) { throw new ArgumentNullException(nameof(action)); } try { using (var muxedStream = await MuxedStreamNamespacedPodExecAsync( name, @namespace, command, container, tty: tty, cancellationToken: cancellationToken).ConfigureAwait(false)) using (var stdIn = muxedStream.GetStream(null, ChannelIndex.StdIn)) using (var stdOut = muxedStream.GetStream(ChannelIndex.StdOut, null)) using (var stdErr = muxedStream.GetStream(ChannelIndex.StdErr, null)) using (var error = muxedStream.GetStream(ChannelIndex.Error, null)) using (var errorReader = new StreamReader(error)) { muxedStream.Start(); await action(stdIn, stdOut, stdErr).ConfigureAwait(false); var errors = await errorReader.ReadToEndAsync().ConfigureAwait(false); // StatusError is defined here: // https://github.com/kubernetes/kubernetes/blob/068e1642f63a1a8c48c16c18510e8854a4f4e7c5/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go#L37 var returnMessage = KubernetesJson.Deserialize <V1Status>(errors); return(GetExitCodeOrThrow(returnMessage)); } } catch (HttpOperationException httpEx) when(httpEx.Body is V1Status) { throw new KubernetesException((V1Status)httpEx.Body, httpEx); } }
protected async Task <WebSocket> StreamConnectAsync(Uri uri, string webSocketSubProtocol = null, Dictionary <string, List <string> > customHeaders = null, CancellationToken cancellationToken = default) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } // Create WebSocket transport objects var webSocketBuilder = CreateWebSocketBuilder(); // Set Headers if (customHeaders != null) { foreach (var header in customHeaders) { webSocketBuilder.SetRequestHeader(header.Key, string.Join(" ", header.Value)); } } // Set Credentials if (this.ClientCert != null) { webSocketBuilder.AddClientCertificate(this.ClientCert); } if (this.HttpClientHandler != null) { #if NET5_0_OR_GREATER foreach (var cert in this.HttpClientHandler.SslOptions.ClientCertificates.OfType <X509Certificate2>()) #else foreach (var cert in this.HttpClientHandler.ClientCertificates.OfType <X509Certificate2>()) #endif { webSocketBuilder.AddClientCertificate(cert); } } if (Credentials != null) { // Copy the default (credential-related) request headers from the HttpClient to the WebSocket var message = new HttpRequestMessage(); await Credentials.ProcessHttpRequestAsync(message, cancellationToken).ConfigureAwait(false); foreach (var header in message.Headers) { webSocketBuilder.SetRequestHeader(header.Key, string.Join(" ", header.Value)); } } if (this.CaCerts != null) { webSocketBuilder.ExpectServerCertificate(this.CaCerts); } if (this.SkipTlsVerify) { webSocketBuilder.SkipServerCertificateValidation(); } if (webSocketSubProtocol != null) { webSocketBuilder.Options.AddSubProtocol(webSocketSubProtocol); } // Send Request cancellationToken.ThrowIfCancellationRequested(); WebSocket webSocket = null; try { webSocket = await webSocketBuilder.BuildAndConnectAsync(uri, CancellationToken.None) .ConfigureAwait(false); } catch (WebSocketException wse) when(wse.WebSocketErrorCode == WebSocketError.HeaderError || (wse.InnerException is WebSocketException && ((WebSocketException)wse.InnerException).WebSocketErrorCode == WebSocketError.HeaderError)) { // This usually indicates the server sent an error message, like 400 Bad Request. Unfortunately, the WebSocket client // class doesn't give us a lot of information about what went wrong. So, retry the connection. var uriBuilder = new UriBuilder(uri); uriBuilder.Scheme = uri.Scheme == "wss" ? "https" : "http"; var response = await HttpClient.GetAsync(uriBuilder.Uri, cancellationToken).ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.SwitchingProtocols) { // This should never happen - the server just allowed us to switch to WebSockets but the previous call didn't work. // Rethrow the original exception response.Dispose(); throw; } else { #if NET5_0_OR_GREATER var content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); #else var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); #endif // Try to parse the content as a V1Status object var genericObject = KubernetesJson.Deserialize <KubernetesObject>(content); V1Status status = null; if (genericObject.ApiVersion == "v1" && genericObject.Kind == "Status") { status = KubernetesJson.Deserialize <V1Status>(content); } var ex = new HttpOperationException( $"The operation returned an invalid status code: {response.StatusCode}", wse) { Response = new HttpResponseMessageWrapper(response, content), Body = status != null ? (object)status : content, }; response.Dispose(); throw ex; } } catch (Exception) { throw; } return(webSocket); }