Esempio n. 1
0
        private KubeClientOptions GetKubeClientOptions(ClusterConnection settings)
        {
            switch (settings.Kind)
            {
            case ConfigKind.NoAuthentication:
                return(new KubeClientOptions
                {
                    ApiEndPoint = new Uri(settings.Url),
                    AuthStrategy = KubeAuthStrategy.None,
                    AllowInsecure = settings.AllowInsecure, // Don't validate server certificate
                    KubeNamespace = settings.DefaultNamespace,
                });

            case ConfigKind.KubeConfigFile:
                return(K8sConfig.Load(settings.KubeConfigFile).ToKubeClientOptions(
                           kubeContextName: settings.DefaultContext,
                           defaultKubeNamespace: settings.DefaultNamespace
                           ));

            case ConfigKind.BearerToken:
                return(new KubeClientOptions
                {
                    ApiEndPoint = new Uri(settings.Url),
                    AuthStrategy = KubeAuthStrategy.BearerToken,
                    AccessToken = settings.AccessToken,
                    AllowInsecure = settings.AllowInsecure, // Don't validate server certificate
                    KubeNamespace = settings.DefaultNamespace,
                });

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
Esempio n. 2
0
        protected override async Task ProcessRecordAsync(CancellationToken cancellationToken)
        {
            await base.ProcessRecordAsync(cancellationToken);

            K8sConfig config = K8sConfig.Load();

            WriteObject(config);
        }
Esempio n. 3
0
        private static KubeClientOptions GetKubeClientOptions()
        {
            var options = string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST"))
                ? K8sConfig.Load().ToKubeClientOptions()
                : KubeClientOptions.FromPodServiceAccount();

            options.KubeNamespace = "default";
            return(options);
        }
Esempio n. 4
0
 public void LinuxUsesHomeEnvironmentVariable()
 {
     if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
     {
         Environment.SetEnvironmentVariable("USERPROFILE", UserProfile);
         var home   = Environment.GetEnvironmentVariable("HOME");
         var config = K8sConfig.Locate();
         Assert.Equal(Path.Combine(home, ".kube", "config"), config);
     }
 }
Esempio n. 5
0
 public void WindowsUsesHomeFirst()
 {
     if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
     {
         Environment.SetEnvironmentVariable("USERPROFILE", UserProfile);
         Environment.SetEnvironmentVariable("HOMEDRIVE", HomeDrive);
         Environment.SetEnvironmentVariable("HOMEPATH", HomePath);
         Environment.SetEnvironmentVariable("HOME", Home);
         var config = K8sConfig.Locate();
         Assert.Equal(Path.Combine(Home, ".kube", "config"), config);
     }
 }
Esempio n. 6
0
        public void WindowsUsesUserProfileAsLastOption()
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                Environment.SetEnvironmentVariable("HOME", String.Empty);
                Environment.SetEnvironmentVariable("HOMEDRIVE", String.Empty);
                Environment.SetEnvironmentVariable("HOMEPATH", String.Empty);

                Environment.SetEnvironmentVariable("USERPROFILE", UserProfile);
                var config = K8sConfig.Locate();
                Assert.Equal(Path.Combine(UserProfile, ".kube", "config"), config);
            }
        }
Esempio n. 7
0
        protected override async Task ProcessRecordAsync(CancellationToken cancellationToken)
        {
            await base.ProcessRecordAsync(cancellationToken);

            Serializer serializer = new SerializerBuilder().Build();
            string     yaml       = serializer.Serialize(Config);
            string     configPath = K8sConfig.Locate();

            if (ShouldProcess(configPath, "update"))
            {
                await File.WriteAllTextAsync(configPath, yaml); // Do not pass cancellationToken to not corrupt config file
            }
        }
Esempio n. 8
0
        public void CanDeserializeCredentialPluginConfiguration(string configName)
        {
            FileInfo configFile = new FileInfo(
                Path.Combine("Configurations", $"{configName}.yml")
                );

            K8sConfig kubeConfig = K8sConfig.Load(configFile);
            var       awsUser    = kubeConfig.UserIdentities.FirstOrDefault(
                user => user.Name == "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster");

            Assert.Equal("aws", awsUser?.Config?.Exec?.Command);
            Assert.Equal(6, awsUser?.Config?.Exec?.Arguments?.Count);
            Assert.Equal(1, awsUser?.Config?.Exec?.EnvironmentVariables.Count);
        }
        public KubeApiClient CreateClient()
        {
            return(KubeApiClient.Create(K8sConfig.Load().ToKubeClientOptions(null, "default")));
            //if (WebHostEnvironment.IsDevelopment())
            //{
            //    return KubeApiClient.Create(K8sConfig.Load().ToKubeClientOptions());
            //}
            //else
            //{
            //    // Load from in-cluster configuration:
            //    var config = KubernetesClientConfiguration.InClusterConfig();

            //    return new Kubernetes(config);
            //}
        }
Esempio n. 10
0
        public KnativePlatform(string configFile)
        {
            this.configFile = configFile;
            KubeClientOptions options;

            if (!string.IsNullOrEmpty(configFile))
            {
                options = K8sConfig.Load(configFile).ToKubeClientOptions(defaultKubeNamespace: FUNCTIONS_NAMESPACE);
            }
            else
            {
                options = K8sConfig.Load().ToKubeClientOptions(defaultKubeNamespace: FUNCTIONS_NAMESPACE);
            }

            client = KubeApiClient.Create(options);
        }
Esempio n. 11
0
        public void CanLoadValidConfig(string configName)
        {
            FileInfo configFile = new FileInfo(
                Path.Combine("Configurations", $"{configName}.yml")
                );

            K8sConfig kubeConfig = K8sConfig.Load(configFile);

            Assert.Equal("v1", kubeConfig.ApiVersion);
            Assert.Equal("Config", kubeConfig.Kind);
            Assert.Equal("docker-for-desktop", kubeConfig.CurrentContextName);

            Assert.Equal(5, kubeConfig.Contexts.Count);
            Assert.Equal(5, kubeConfig.Clusters.Count);
            Assert.Equal(6, kubeConfig.UserIdentities.Count);
        }
Esempio n. 12
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddHostedService <SecretsWatcherHostedService>();
            services.AddControllers();
            var clientOptions = K8sConfig.Load(@"C:\Users\linjm\.kube\config").ToKubeClientOptions(
                defaultKubeNamespace: "default"
                );

            services.AddSingleton <KubeClientOptions>(clientOptions);

            var providers = new ISecretsProvider[]
            {
                new SecretsProvider()
            };

            services.AddSingleton <IEnumerable <ISecretsProvider> >(providers);
        }
Esempio n. 13
0
        protected override async Task ProcessRecordAsync(CancellationToken cancellationToken)
        {
            await base.ProcessRecordAsync(cancellationToken);

            string    configPath = K8sConfig.Locate();
            K8sConfig config     = K8sConfig.Load(configPath);

            config.CurrentContextName = Context;
            ISerializer serializer = new SerializerBuilder().Build();
            string      yaml       = serializer.Serialize(config);

            if (ShouldProcess(configPath, "update"))
            {
                using (var streamWriter = new StreamWriter(configPath)) {
                    await streamWriter.WriteAsync(yaml); // Do not pass cancellationToken to not corrupt config file
                }
            }
        }
Esempio n. 14
0
        protected override async Task BeginProcessingAsync(CancellationToken cancellationToken)
        {
            await base.BeginProcessingAsync(cancellationToken);

            config = K8sConfig.Load();
            KubeClientOptions clientOptions = config.ToKubeClientOptions(
                defaultKubeNamespace: "default"
                );

            clientOptions.AllowInsecure = AllowInsecure;
            if (ApiEndPoint != null)
            {
                clientOptions.ApiEndPoint = ApiEndPoint;
            }
            WriteVerbose($"Using endpoint {clientOptions.ApiEndPoint}");
            // clientOptions.LogHeaders = true;
            clientOptions.LogPayloads = LogPayloads;
            client = KubeApiClient.Create(clientOptions, LoggerFactory);
        }
Esempio n. 15
0
        /// <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)
        {
            ProgramOptions options = ProgramOptions.Parse(commandLineArguments);

            if (options == null)
            {
                return(ExitCodes.InvalidArguments);
            }

            ILoggerFactory loggerFactory = ConfigureLogging(options);

            try
            {
                const string configMapName      = "config-from-configmap";
                const string configMapNamespace = "default";

                KubeClientOptions clientOptions = K8sConfig.Load().ToKubeClientOptions(defaultKubeNamespace: configMapNamespace, loggerFactory: loggerFactory);

                if (options.Verbose)
                {
                    clientOptions.LogPayloads = true;
                }

                KubeApiClient client = KubeApiClient.Create(clientOptions);

                Log.Information("Checking for existing ConfigMap...");
                ConfigMapV1 configMap = await client.ConfigMapsV1().Get(configMapName, configMapNamespace);

                if (configMap != null)
                {
                    Log.Information("Deleting existing ConfigMap...");
                    await client.ConfigMapsV1().Delete(configMapName);

                    Log.Information("Existing ConfigMap deleted.");
                }

                Log.Information("Creating new ConfigMap...");
                configMap = await client.ConfigMapsV1().Create(new ConfigMapV1
                {
                    Metadata = new ObjectMetaV1
                    {
                        Name      = configMapName,
                        Namespace = configMapNamespace
                    },
                    Data =
                    {
                        ["Key1"] = "One",
                        ["Key2"] = "Two"
                    }
                });

                Log.Information("New ConfigMap created.");

                Log.Information("Building configuration...");
                IConfiguration configuration = new ConfigurationBuilder()
                                               .AddKubeConfigMap(clientOptions,
                                                                 configMapName: "config-from-configmap",
                                                                 reloadOnChange: true
                                                                 )
                                               .Build();
                Log.Information("Configuration built.");

                Log.Information("Got configuration:");
                Dump(configuration);

                Log.Information("Press enter to update ConfigMap...");

                Console.ReadLine();

                Log.Information("Registering callback for change-notifications...");

                ManualResetEvent configurationChanged = new ManualResetEvent(false);

                // See ConfigurationExtensions class below.
                IDisposable reloadNotifications = configuration.OnReload(() =>
                {
                    Log.Information("Got changed configuration:");
                    Dump(configuration);

                    configurationChanged.Set();
                });
                Log.Information("Change-notification callback registered.");

                using (configurationChanged)
                    using (reloadNotifications)
                    {
                        Log.Information("Updating ConfigMap...");

                        configMap.Data["One"] = "1";
                        configMap.Data["Two"] = "2";

                        // Replace the entire Data dictionary (to modify only some of the data, you'll need to use an untyped JsonPatchDocument).
                        await client.ConfigMapsV1().Update(configMapName, patch =>
                        {
                            patch.Replace(patchConfigMap => patchConfigMap.Data,
                                          value: configMap.Data
                                          );
                        });

                        Log.Information("Updated ConfigMap.");

                        Log.Information("Waiting for configuration change...");

                        configurationChanged.WaitOne();

                        Log.Information("Configuration changed.");
                    }

                return(ExitCodes.Success);
            }
            catch (HttpRequestException <StatusV1> kubeError)
            {
                Log.Error(kubeError, "Kubernetes API error: {@Status}", kubeError.Response);

                return(ExitCodes.UnexpectedError);
            }
            catch (Exception unexpectedError)
            {
                Log.Error(unexpectedError, "Unexpected error.");

                return(ExitCodes.UnexpectedError);
            }
        }
Esempio n. 16
0
        /// <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)
        {
            ProgramOptions options = ProgramOptions.Parse(commandLineArguments);

            if (options == null)
            {
                return(ExitCodes.InvalidArguments);
            }

            ILoggerFactory loggerFactory = ConfigureLogging(options);

            try
            {
                KubeClientOptions clientOptions = K8sConfig.Load().ToKubeClientOptions(defaultKubeNamespace: options.KubeNamespace);
                if (options.Verbose)
                {
                    clientOptions.LogPayloads = true;
                }

                KubeApiClient client = KubeApiClient.Create(clientOptions, loggerFactory);

                Log.Information("Looking for existing Deployment {DeploymentName} in namespace {KubeNamespace}...",
                                options.DeploymentName,
                                options.KubeNamespace
                                );

                DeploymentV1 existingDeployment = await client.DeploymentsV1().Get(options.DeploymentName, options.KubeNamespace);

                if (existingDeployment != null)
                {
                    Log.Error("Cannot continue - deployment  {DeploymentName} in namespace {KubeNamespace} already exists.",
                              options.DeploymentName,
                              options.KubeNamespace
                              );

                    return(ExitCodes.AlreadyExists);
                }

                Log.Information("Ok, Deployment does not exist yet - we're ready to go.");

                Log.Information("Creating Deployment {DeploymentName} in namespace {KubeNamespace}...",
                                options.DeploymentName,
                                options.KubeNamespace
                                );
                DeploymentV1 initialDeployment = await CreateInitialDeployment(client, options.DeploymentName, options.KubeNamespace);

                int?initialRevision = initialDeployment.GetRevision();
                if (initialRevision == null)
                {
                    Log.Error("Unable to determine initial revision of Deployment {DeploymentName} in namespace {KubeNamespace} (missing annotation).",
                              options.DeploymentName,
                              options.KubeNamespace
                              );

                    return(ExitCodes.UnexpectedError);
                }
                Log.Information("Created Deployment {DeploymentName} in namespace {KubeNamespace} (revision {DeploymentRevision}).",
                                options.DeploymentName,
                                options.KubeNamespace,
                                initialRevision
                                );

                Log.Information("Updating Deployment {DeploymentName} in namespace {KubeNamespace}...",
                                options.DeploymentName,
                                options.KubeNamespace
                                );
                DeploymentV1 updatedDeployment = await UpdateDeployment(client, initialDeployment);

                int?updatedRevision = updatedDeployment.GetRevision();
                if (updatedRevision == null)
                {
                    Log.Error("Unable to determine updated revision of Deployment {DeploymentName} in namespace {KubeNamespace} (missing annotation).",
                              options.DeploymentName,
                              options.KubeNamespace
                              );

                    return(ExitCodes.UnexpectedError);
                }
                Log.Information("Updated Deployment {DeploymentName} in namespace {KubeNamespace} (revision {DeploymentRevision}).",
                                options.DeploymentName,
                                options.KubeNamespace,
                                updatedRevision
                                );

                Log.Information("Searching for ReplicaSet that corresponds to revision {Revision} of {DeploymentName} in namespace {KubeNamespace}...",
                                options.DeploymentName,
                                options.KubeNamespace,
                                initialRevision
                                );
                ReplicaSetV1 targetReplicaSet = await FindReplicaSetForRevision(client, updatedDeployment, initialRevision.Value);

                if (targetReplicaSet == null)
                {
                    Log.Error("Cannot find ReplicaSet that corresponds to revision {Revision} of {DeploymentName} in namespace {KubeNamespace}...",
                              options.DeploymentName,
                              options.KubeNamespace,
                              initialRevision
                              );

                    return(ExitCodes.NotFound);
                }
                Log.Information("Found ReplicaSet {ReplicaSetName} in namespace {KubeNamespace}.",
                                targetReplicaSet.Metadata.Name,
                                targetReplicaSet.Metadata.Namespace
                                );

                Log.Information("Rolling Deployment {DeploymentName} in namespace {KubeNamespace} back to initial revision {DeploymentRevision}...",
                                options.DeploymentName,
                                options.KubeNamespace,
                                initialRevision
                                );
                DeploymentV1 rolledBackDeployment = await RollbackDeployment(client, updatedDeployment, targetReplicaSet);

                Log.Information("Rollback initiated for Deployment {DeploymentName} in namespace {KubeNamespace} from revision {FromRevision} to {ToRevision} (new revision will be {NewRevision})...",
                                options.DeploymentName,
                                options.KubeNamespace,
                                updatedRevision,
                                initialRevision,
                                rolledBackDeployment.GetRevision()
                                );

                return(ExitCodes.Success);
            }
            catch (HttpRequestException <StatusV1> kubeError)
            {
                Log.Error(kubeError, "Kubernetes API error: {@Status}", kubeError.Response);

                return(ExitCodes.UnexpectedError);
            }
            catch (Exception unexpectedError)
            {
                Log.Error(unexpectedError, "Unexpected error.");

                return(ExitCodes.UnexpectedError);
            }
        }
Esempio n. 17
0
        /// <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)
        {
            ProgramOptions options = ProgramOptions.Parse(commandLineArguments);

            if (options == null)
            {
                return(ExitCodes.InvalidArguments);
            }

            ILoggerFactory loggerFactory = ConfigureLogging(options);

            try
            {
                KubeClientOptions clientOptions = K8sConfig.Load().ToKubeClientOptions(defaultKubeNamespace: options.KubeNamespace, loggerFactory: loggerFactory);
                if (options.Verbose)
                {
                    clientOptions.LogPayloads = true;
                }

                JsonSerializerSettings serializerSettings = KubeResourceClient.SerializerSettings;
                serializerSettings.Formatting = Formatting.Indented;

                KubeApiClient client        = KubeApiClient.Create(clientOptions);
                EventListV1   initialEvents = await client.EventsV1().List();

                ActionBlock <EventV1> eventProcessor = CreateEventProcessor(client);

                Log.Information("Initial events:");
                Log.Information("===============");

                if (initialEvents.Items.Count > 0)
                {
                    foreach (EventV1 initialEvent in initialEvents)
                    {
                        eventProcessor.Post(initialEvent);
                    }
                }
                else
                {
                    Log.Information("No initial events.");
                }

                Log.Information("===============");

                IObservable <IResourceEventV1 <EventV1> > eventStream;
                if (initialEvents.Items.Count > 0)
                {
                    EventV1 lastEvent = initialEvents.Items[initialEvents.Items.Count - 1];

                    eventStream = client.EventsV1().WatchAll(resourceVersion: lastEvent.InvolvedObject.ResourceVersion);
                }
                else
                {
                    eventStream = client.EventsV1().WatchAll();
                }

                IDisposable subscription = eventStream.Select(resourceEvent => resourceEvent.Resource).Subscribe(
                    subsequentEvent => eventProcessor.Post(subsequentEvent),
                    error => Log.Error(error, "Unexpected error while streaming events.")
                    );

                using (subscription)
                {
                    Log.Information("Watching for new events (press enter to terminate).");

                    Console.ReadLine();
                }

                Log.Information("Waiting for event processor to shut down...");

                eventProcessor.Complete();
                await eventProcessor.Completion;

                Log.Information("Event processor has shut down.");

                return(ExitCodes.Success);
            }
            catch (HttpRequestException <StatusV1> kubeError)
            {
                Log.Error(kubeError, "Kubernetes API error: {@Status}", kubeError.Response);

                return(ExitCodes.UnexpectedError);
            }
            catch (Exception unexpectedError)
            {
                Log.Error(unexpectedError, "Unexpected error.");

                return(ExitCodes.UnexpectedError);
            }
        }
Esempio n. 18
0
        /// <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);
            }
        }
Esempio n. 19
0
        /// <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)
        {
            ProgramOptions options = ProgramOptions.Parse(commandLineArguments);

            if (options == null)
            {
                return(ExitCodes.InvalidArguments);
            }

            ILoggerFactory loggerFactory = ConfigureLogging(options);

            try
            {
                const string configMapName      = "config-from-configmap";
                const string configMapNamespace = "default";

                KubeClientOptions clientOptions = K8sConfig.Load().ToKubeClientOptions(defaultKubeNamespace: configMapNamespace);
                if (options.Verbose)
                {
                    clientOptions.LogPayloads = true;
                }

                KubeApiClient client = KubeApiClient.Create(clientOptions, loggerFactory);

                Log.Information("Checking for existing ConfigMap...");
                ConfigMapV1 configMap = await client.ConfigMapsV1().Get(configMapName, configMapNamespace);

                if (configMap != null)
                {
                    Log.Information("Deleting existing ConfigMap...");
                    await client.ConfigMapsV1().Delete(configMapName);

                    Log.Information("Existing ConfigMap deleted.");
                }

                Log.Information("Creating new ConfigMap...");
                configMap = await client.ConfigMapsV1().Create(new ConfigMapV1
                {
                    Metadata = new ObjectMetaV1
                    {
                        Name      = configMapName,
                        Namespace = configMapNamespace
                    },
                    Data = new Dictionary <string, string>
                    {
                        ["Key1"] = "One",
                        ["Key2"] = "Two"
                    }
                });

                Log.Information("New ConfigMap created.");

                Log.Information("Building configuration...");
                IConfiguration configuration = new ConfigurationBuilder()
                                               .AddKubeConfigMap(clientOptions,
                                                                 configMapName: "config-from-configmap",
                                                                 reloadOnChange: true
                                                                 )
                                               .Build();
                Log.Information("Configuration built.");

                configuration.GetReloadToken().RegisterChangeCallback(_ =>
                {
                    Log.Information("Got changed configuration:");
                    foreach (var item in configuration.AsEnumerable())
                    {
                        Log.Information("\t'{Key}' = '{Value}'", item.Key, item.Value);
                    }
                }, state: null);

                Log.Information("Got configuration:");
                foreach (var item in configuration.AsEnumerable())
                {
                    Log.Information("\t'{Key}' = '{Value}'", item.Key, item.Value);
                }

                Log.Information("Press enter to update ConfigMap...");

                Console.ReadLine();

                Log.Information("Updating ConfigMap...");

                configMap.Data["One"] = "1";
                configMap.Data["Two"] = "2";

                // Replace the entire Data dictionary (otherwise, use an untyped JsonPatchDocument).
                await client.ConfigMapsV1().Update(configMapName, patch =>
                {
                    patch.Replace(patchConfigMap => patchConfigMap.Data,
                                  value: configMap.Data
                                  );
                });

                Log.Information("Updated ConfigMap.");

                Log.Information("Waiting for configuration change; press enter to terminate.");

                Console.ReadLine();

                return(ExitCodes.Success);
            }
            catch (HttpRequestException <StatusV1> kubeError)
            {
                Log.Error(kubeError, "Kubernetes API error: {@Status}", kubeError.Response);

                return(ExitCodes.UnexpectedError);
            }
            catch (Exception unexpectedError)
            {
                Log.Error(unexpectedError, "Unexpected error.");

                return(ExitCodes.UnexpectedError);
            }
        }
 public string[] GetValidValues()
 {
     return(K8sConfig.Load().Contexts.ConvertAll(context => context.Name).ToArray());
 }