protected override async Task ProcessRecordAsync(CancellationToken cancellationToken) { await base.ProcessRecordAsync(cancellationToken); IEnumerable <PodV1> podList; if (String.IsNullOrEmpty(Name) || WildcardPattern.ContainsWildcardCharacters(Name)) { podList = await client.PodsV1().List( kubeNamespace: Namespace, labelSelector: LabelSelector, cancellationToken: cancellationToken ); } else { PodV1 pod = await client.PodsV1().Get( name: Name, kubeNamespace: Namespace, cancellationToken: cancellationToken ); podList = new[] { pod }; } if (WildcardPattern.ContainsWildcardCharacters(Name)) { var pattern = new WildcardPattern(Name); podList = podList.Where(pod => pattern.IsMatch(pod.Metadata.Name)); } WriteObject(podList, true); }
public async Task ApplyModel() { MockMessageHandler handler = new MockMessageHandler(async(HttpRequestMessage request) => { Assert.Equal("PATCH", request.Method.Method); Assert.NotNull(request.Content); string requestBody = await request.Content.ReadAsStringAsync(); Log.LogInformation("Request body:\n{RequestBody:l}", requestBody); Assert.NotNull(request.Content.Headers.ContentType); Assert.Equal("application/apply-patch+yaml", request.Content.Headers.ContentType.MediaType); Assert.Contains("fieldManager=my-field-manager", request.RequestUri.Query); return(request.CreateResponse(HttpStatusCode.OK, responseBody: JsonConvert.SerializeObject(new PodV1 { Metadata = new ObjectMetaV1 { Name = "my-pod", Namespace = "my-namespace", Finalizers = // Array test { "foo", "bar" } } }), mediaType: "application/json" )); }); using (KubeApiClient client = handler.CreateClient(loggerFactory: LoggerFactory)) { PodV1 resource = new PodV1 { Metadata = new ObjectMetaV1 { Name = "my-pod", Namespace = "my-namespace", Finalizers = // Array test { "foo", "bar" } } }; await client.Dynamic().Apply(resource, fieldManager: "my-field-manager" ); } }
public void PodV1Result_From_SuccessStatus_ImplicitCast_Resource() { var result = new KubeResourceResultV1 <PodV1>(new StatusV1 { Status = "Success", Message = "Most definitely a success." }); PodV1 pod = result; Assert.Null(pod); }
private async Task DeletePod(string name, string kubeNamespace, CancellationToken cancellationToken) { if (!ShouldProcess($"Deleting pod \"{name}\" in namespace \"{kubeNamespace}\"", $"Delete pod \"{name}\" in namespace \"{kubeNamespace}\"?", "Confirm")) { return; } try { PodV1 pod = await client.PodsV1().Delete(name: name, kubeNamespace: kubeNamespace, cancellationToken: cancellationToken); WriteObject(pod); } catch (Exception e) { WriteError(new ErrorRecord(e, null, ErrorCategory.NotSpecified, null)); } }
public void PodV1Result_From_FailureStatus_ImplicitCast_Resource() { PodV1 pod = null; KubeApiException exception = Assert.Throws <KubeApiException>(() => { pod = new KubeResourceResultV1 <PodV1>(new StatusV1 { Status = "Failure", Message = "Most definitely not a success." }); }); Assert.Null(pod); Assert.True(exception.HasStatus); Assert.Equal("Failure", exception.Status.Status); Assert.Equal("Most definitely not a success.", exception.Status.Message); }
public void PodV1Result_From_PodV1_ImplicitCast_Resource() { PodV1 expected = new PodV1 { Metadata = new ObjectMetaV1 { Name = "my-pod", Namespace = "my-namespace" } }; var result = new KubeResourceResultV1 <PodV1>(expected); PodV1 actual = result; Assert.NotNull(actual); Assert.Same(expected, actual); }
/// <summary> /// Request creation of a <see cref="Pod"/>. /// </summary> /// <param name="newPod"> /// A <see cref="PodV1"/> representing the Pod to create. /// </param> /// <param name="cancellationToken"> /// An optional <see cref="CancellationToken"/> that can be used to cancel the request. /// </param> /// <returns> /// A <see cref="PodV1"/> representing the current state for the newly-created Pod. /// </returns> public async Task <PodV1> Create(PodV1 newPod, CancellationToken cancellationToken = default) { if (newPod == null) { throw new ArgumentNullException(nameof(newPod)); } return(await Http .PostAsJsonAsync( Requests.Collection.WithTemplateParameters(new { Namespace = newPod?.Metadata?.Namespace ?? Client.DefaultNamespace }), postBody : newPod, cancellationToken : cancellationToken ) .ReadContentAsAsync <PodV1, StatusV1>()); }
/// <summary> /// Request creation of a <see cref="PodV1"/>. /// </summary> /// <param name="newPod"> /// A <see cref="PodV1"/> representing the Pod to create. /// </param> /// <param name="cancellationToken"> /// An optional <see cref="CancellationToken"/> that can be used to cancel the request. /// </param> /// <returns> /// A <see cref="PodV1"/> representing the current state for the newly-created Pod. /// </returns> public async Task <PodV1> Create(PodV1 newPod, CancellationToken cancellationToken = default) { if (newPod == null) { throw new ArgumentNullException(nameof(newPod)); } return(await Http .PostAsJsonAsync( Requests.Collection.WithTemplateParameters(new { Namespace = newPod?.Metadata?.Namespace ?? KubeClient.DefaultNamespace }), postBody : newPod, cancellationToken : cancellationToken ) .ReadContentAsObjectV1Async <PodV1>( operationDescription: $"create v1/Pod resource in namespace {newPod?.Metadata?.Namespace ?? KubeClient.DefaultNamespace}" )); }
/// <summary> /// The main program entry-point. /// </summary> /// <param name="commandLineArguments"> /// The program's command-line arguments. /// </param> /// <returns> /// The program exit-code. /// </returns> static async Task <int> Main(string[] commandLineArguments) { // Show help if no arguments are specified. bool showHelp = commandLineArguments.Length == 0; if (showHelp) { commandLineArguments = new[] { "--help" } } ; try { SynchronizationContext.SetSynchronizationContext( new SynchronizationContext() ); ProgramOptions options = ProgramOptions.Parse(commandLineArguments); if (options == null) { return(showHelp ? ExitCodes.Success : ExitCodes.InvalidArguments); } ConfigureLogging(options); using (ServiceProvider serviceProvider = BuildServiceProvider(options)) using (AutoResetEvent done = new AutoResetEvent(initialState: false)) { KubeApiClient client = serviceProvider.GetRequiredService <KubeApiClient>(); KubeResources kubeResources = serviceProvider.GetRequiredService <KubeResources>(); string jobName = kubeResources.Names.DeployGliderGunRemoteJob(options); JobV1 existingJob = await client.JobsV1().Get(jobName); if (existingJob != null) { Log.Information("Found existing job {JobName} in namespace {KubeNamespace}; deleting...", existingJob.Metadata.Name, existingJob.Metadata.Namespace ); await client.JobsV1().Delete(jobName, propagationPolicy: DeletePropagationPolicy.Foreground ); Log.Information("Deleted existing job {JobName}.", existingJob.Metadata.Name, existingJob.Metadata.Namespace ); } string secretName = kubeResources.Names.DeployGliderGunRemoteSecret(options); SecretV1 existingSecret = await client.SecretsV1().Get(secretName); if (existingSecret != null) { Log.Information("Found existing secret {SecretName} in namespace {KubeNamespace}; deleting...", existingSecret.Metadata.Name, existingSecret.Metadata.Namespace ); await client.SecretsV1().Delete(secretName); Log.Information("Deleted existing secret {SecretName}.", existingSecret.Metadata.Name, existingSecret.Metadata.Namespace ); } Log.Information("Creating deployment secret {SecretName}...", secretName); SecretV1 deploymentSecret = kubeResources.DeployGliderGunRemoteSecret(options); try { deploymentSecret = await client.SecretsV1().Create(deploymentSecret); } catch (HttpRequestException <StatusV1> createSecretFailed) { Log.Error(createSecretFailed, "Failed to create Kubernetes Secret {SecretName} for deployment ({Reason}): {ErrorMessage}", secretName, createSecretFailed.Response.Reason, createSecretFailed.Response.Message ); return(ExitCodes.JobFailed); } Log.Information("Created deployment secret {SecretName}.", deploymentSecret.Metadata.Name); // Watch for job's associated pod to start, then monitor the pod's log until it completes. IDisposable jobLogWatch = null; IDisposable jobPodWatch = client.PodsV1().WatchAll( labelSelector: $"job-name={jobName}", kubeNamespace: options.KubeNamespace ).Subscribe( podEvent => { if (jobLogWatch != null) { return; } PodV1 jobPod = podEvent.Resource; if (jobPod.Status.Phase != "Pending") { Log.Information("Job {JobName} has started.", jobName); Log.Verbose("Hook up log monitor for Pod {PodName} of Job {JobName}...", jobPod.Metadata.Name, jobName ); jobLogWatch = client.PodsV1().StreamLogs( name: jobPod.Metadata.Name, kubeNamespace: jobPod.Metadata.Namespace ).Subscribe( logEntry => { Log.Information("[{PodName}] {LogEntry}", jobPod.Metadata.Name, logEntry); }, error => { if (error is HttpRequestException <StatusV1> requestError) { Log.Error(requestError, "Kubernetes API request error ({Reason}): {ErrorMessage:l}", requestError.Response.Reason, requestError.Response.Message ); } else { Log.Error(error, "JobLog Error"); } }, () => { Log.Information("[{PodName}] <end of log>", jobPod.Metadata.Name); done.Set(); } ); Log.Information("Monitoring log for Pod {PodName} of Job {JobName}.", jobPod.Metadata.Name, jobName ); } }, error => { Log.Error(error, "PodWatch Error"); }, () => { Log.Information("PodWatch End"); } ); Log.Information("Creating deployment job {JobName}...", jobName); JobV1 deploymentJob = kubeResources.DeployGliderGunRemoteJob(options); try { deploymentJob = await client.JobsV1().Create(deploymentJob); } catch (HttpRequestException <StatusV1> createJobFailed) { Log.Error(createJobFailed, "Failed to create Kubernetes Job {JobName} for deployment ({Reason}): {ErrorMessage}", jobName, createJobFailed.Response.Reason, createJobFailed.Response.Message ); return(ExitCodes.JobFailed); } Log.Information("Created deployment job {JobName}.", deploymentJob.Metadata.Name); TimeSpan timeout = TimeSpan.FromSeconds(options.Timeout); Log.Information("Waiting up to {TimeoutSeconds} seconds for deployment job {JobName} to complete.", timeout.TotalSeconds, jobName ); if (!done.WaitOne(timeout)) { using (jobPodWatch) using (jobLogWatch) { Log.Error("Timed out after waiting {TimeoutSeconds} seconds for deployment job {JobName} to complete.", timeout.TotalSeconds, jobName ); return(ExitCodes.JobTimeout); } } jobPodWatch?.Dispose(); jobLogWatch?.Dispose(); deploymentJob = await client.JobsV1().Get(jobName); if (deploymentJob == null) { Log.Error("Cannot find deployment job {JobName} in namespace {KubeNamespace}.", deploymentJob.Metadata.Name, deploymentJob.Metadata.Namespace ); return(ExitCodes.UnexpectedError); } if (deploymentJob.Status.Failed > 0) { Log.Error("Deployment job {JobName} failed.", deploymentJob.Metadata.Name ); foreach (JobConditionV1 jobCondition in deploymentJob.Status.Conditions) { Log.Error("Deployment job {JobName} failed ({Reason}): {ErrorMessage}.", deploymentJob.Metadata.Name, jobCondition.Reason, jobCondition.Message ); } return(ExitCodes.JobFailed); } if (deploymentJob.Status.Succeeded > 0) { Log.Information("Deployment job {JobName} completed successfully.", deploymentJob.Metadata.Name ); } } Log.Information("Done."); return(ExitCodes.Success); } catch (HttpRequestException <StatusV1> kubeRequestError) { Log.Error(kubeRequestError, "A Kubernetes API request failed while deploying the remote node ({Reason}): {ErrorMessage}", kubeRequestError.Response.Reason, kubeRequestError.Response.Message ); return(ExitCodes.JobFailed); } catch (Exception unexpectedError) { Log.Error(unexpectedError, "An unexpected error occurred while deploying the remote node."); return(ExitCodes.UnexpectedError); } finally { Log.CloseAndFlush(); } }
public async Task PodsV1_GetByName_NotFound() { var logEntries = new List <LogEntry>(); TestLogger logger = new TestLogger(LogLevel.Information); logger.LogEntries.Subscribe( logEntry => logEntries.Add(logEntry) ); ClientBuilder clientBuilder = new ClientBuilder() .WithLogging(logger); HttpClient httpClient = clientBuilder.CreateClient("http://localhost:1234", TestHandlers.RespondWith(request => { return(request.CreateResponse(HttpStatusCode.NotFound, responseBody: JsonConvert.SerializeObject(new StatusV1 { Reason = "NotFound" }), WellKnownMediaTypes.Json )); })); KubeClientOptions clientOptions = new KubeClientOptions("http://localhost:1234"); using (KubeApiClient kubeClient = KubeApiClient.Create(httpClient, clientOptions)) { PodV1 pod = await kubeClient.PodsV1().Get(name: "foo"); Assert.Null(pod); } Assert.Equal(2, logEntries.Count); LogEntry logEntry1 = logEntries[0]; Assert.Equal(LogEventIds.BeginRequest, logEntry1.EventId); Assert.Equal("Performing GET request to 'http://localhost:1234/api/v1/namespaces/default/pods/foo'.", logEntry1.Message ); Assert.Equal("GET", logEntry1.Properties["Method"] ); Assert.Equal(new Uri("http://localhost:1234/api/v1/namespaces/default/pods/foo"), logEntry1.Properties["RequestUri"] ); LogEntry logEntry2 = logEntries[1]; Assert.Equal(LogEventIds.EndRequest, logEntry2.EventId); Assert.Equal("Completed GET request to 'http://localhost:1234/api/v1/namespaces/default/pods/foo' (NotFound).", logEntry2.Message ); Assert.Equal("GET", logEntry2.Properties["Method"] ); Assert.Equal(new Uri("http://localhost:1234/api/v1/namespaces/default/pods/foo"), logEntry2.Properties["RequestUri"] ); Assert.Equal(HttpStatusCode.NotFound, logEntry2.Properties["StatusCode"] ); }
public async Task Delete(PodV1 entity) { var result = await _client.PodsV1().Delete(entity.Metadata.Name, entity.Metadata.Namespace, DeletePropagationPolicy.Background); return; }
/// <summary> /// The main program entry-point. /// </summary> /// <param name="commandLineArguments"> /// The program's command-line arguments. /// </param> /// <returns> /// The program exit-code. /// </returns> static async Task <int> Main(string[] commandLineArguments) { // Show help if no arguments are specified. bool showHelp = commandLineArguments.Length == 0; if (showHelp) { commandLineArguments = new[] { "--help" } } ; ProgramOptions options = ProgramOptions.Parse(commandLineArguments); if (options == null) { return(showHelp ? ExitCodes.Success : ExitCodes.InvalidArguments); } ILoggerFactory loggers = ConfigureLogging(options); try { KubeClientOptions clientOptions = K8sConfig.Load().ToKubeClientOptions( kubeContextName: options.KubeContext, defaultKubeNamespace: options.KubeNamespace ); using (KubeApiClient client = KubeApiClient.Create(clientOptions, loggers)) { Log.LogInformation("Finding target pod..."); PodV1 targetPod = await client.PodsV1().Get(options.PodName, kubeNamespace: options.KubeNamespace ); if (targetPod == null) { Log.LogError("Pod '{PodName}' not found in namespace '{KubeNamespace}' on cluster ({KubeContextName}).", options.PodName, options.KubeNamespace, options.KubeContext ); return(ExitCodes.NotFound); } if (!String.IsNullOrWhiteSpace(options.ContainerName)) { ContainerStatusV1 targetContainer = targetPod.Status.ContainerStatuses.Find( container => container.Name == options.ContainerName ); if (targetContainer == null) { Log.LogError("Container '{ContainerName}' not found in Pod '{PodName}' in namespace '{KubeNamespace}' on cluster ({KubeContextName}).", options.ContainerName, options.PodName, options.KubeNamespace, options.KubeContext ); return(ExitCodes.NotFound); } } else if (targetPod.Status.ContainerStatuses.Count > 1) { Log.LogError("Pod '{PodName}' in namespace '{KubeNamespace}' on cluster ({KubeContextName}) has more than one container. Please specify the name of the target container", options.PodName, options.KubeNamespace, options.KubeContext ); return(ExitCodes.InvalidArguments); } Log.LogDebug("Connecting..."); K8sMultiplexer multiplexer = await client.PodsV1().ExecAndConnect( podName: options.PodName, container: options.ContainerName, command: options.Command, kubeNamespace: options.KubeContext, stdin: true, stdout: true, stderr: true, tty: true // Required for interactivity ); Log.LogInformation("Connected."); Task stdInPump, stdOutPump, stdErrPump; using (multiplexer) using (CancellationTokenSource pumpCancellation = new CancellationTokenSource()) using (Stream localStdIn = Console.OpenStandardInput()) using (Stream remoteStdIn = multiplexer.GetStdIn()) using (Stream localStdOut = Console.OpenStandardOutput()) using (Stream remoteStdOut = multiplexer.GetStdOut()) using (Stream localStdErr = Console.OpenStandardError()) using (Stream remoteStdErr = multiplexer.GetStdErr()) { stdInPump = localStdIn.CopyToAsync(remoteStdIn, pumpCancellation.Token); stdOutPump = remoteStdOut.CopyToAsync(localStdOut, pumpCancellation.Token); stdErrPump = remoteStdErr.CopyToAsync(localStdErr, pumpCancellation.Token); await multiplexer.WhenConnectionClosed; // Terminate stream pumps. pumpCancellation.Cancel(); } Log.LogInformation("Connection closed."); Log.LogInformation("Done."); } return(ExitCodes.Success); } catch (Exception unexpectedError) { Log.LogError(unexpectedError.ToString()); Log.LogError(unexpectedError, "Unexpected error."); return(ExitCodes.UnexpectedError); } }