Exemple #1
0
        /// <summary>
        ///     Load configuration entries from the ConfigMap.
        /// </summary>
        public override void Load()
        {
            ConfigMapV1 configMap = _client.ConfigMapsV1().Get(_configMapName, _kubeNamespace).GetAwaiter().GetResult();

            if (configMap != null)
            {
                string sectionNamePrefix = !String.IsNullOrWhiteSpace(_sectionName) ? _sectionName + ":" : String.Empty;

                Data = configMap.Data.ToDictionary(
                    entry => sectionNamePrefix + entry.Key.Replace('.', ':'),
                    entry => entry.Value,
                    StringComparer.OrdinalIgnoreCase
                    );
            }
            else
            {
                Data = new Dictionary <string, string>();
            }

            if (_watch && _watchSubscription == null)
            {
                _watchSubscription = _client.ConfigMapsV1()
                                     .Watch(_configMapName, _kubeNamespace)
                                     .Subscribe(configMapEvent =>
                {
                    if (configMapEvent.EventType == ResourceEventType.Modified)
                    {
                        OnReload();
                    }
                });
            }
        }
        public void ConfigMapV1_Data_PreserveKeyCase(string key)
        {
            var model = new ConfigMapV1
            {
                Data =
                {
                    [key] = key
                }
            };

            JObject rootObject;

            using (JTokenWriter writer = new JTokenWriter())
            {
                JsonSerializer.Create(KubeResourceClient.SerializerSettings).Serialize(writer, model);
                writer.Flush();

                rootObject = (JObject)writer.Token;
            }

            Log.LogInformation("Serialized:\n{JSON:l}",
                               rootObject.ToString(Formatting.Indented)
                               );

            JObject data = rootObject.Value <JObject>("data");

            Assert.NotNull(data);

            Assert.Equal(key,
                         data.Value <string>(key)
                         );
        }
Exemple #3
0
        /// <summary>
        ///     Load data from the specified ConfigMap.
        /// </summary>
        /// <param name="configMap">
        ///     A <see cref="ConfigMapV1"/> representing the ConfigMap's current state, or <c>null</c> if the ConfigMap was not found.
        /// </param>
        /// <param name="isReload">
        ///     Is the ConfigMap is being reloaded? If <c>false</c>, then this is the initial load (and may throw an exception).
        /// </param>
        void Load(ConfigMapV1 configMap, bool isReload = false)
        {
            if (configMap != null)
            {
                Log.LogTrace("Found ConfigMap {ConfigMapName} in namespace {KubeNamespace} (isReload: {isReload}).", _configMapName, _kubeNamespace ?? _client.DefaultNamespace, isReload);

                string sectionNamePrefix = !String.IsNullOrWhiteSpace(_sectionName) ? _sectionName + ":" : String.Empty;

                Data = configMap.Data.ToDictionary(
                    entry => sectionNamePrefix + entry.Key.Replace('.', ':'),
                    entry => entry.Value,
                    StringComparer.OrdinalIgnoreCase
                    );
            }
            else
            {
                Data = new Dictionary <string, string>();

                Log.LogTrace("ConfigMap {ConfigMapName} was not found in namespace {KubeNamespace} (isReload: {isReload}).", _configMapName, _kubeNamespace ?? _client.DefaultNamespace, isReload);

                if (!isReload && _throwOnNotFound)
                {
                    throw new KubeClientException($"ConfigMap {_configMapName} was not found in namespace {_kubeNamespace}.");
                }
            }
        }
Exemple #4
0
        /// <summary>
        ///     Update the specified ConfigMap.
        /// </summary>
        /// <param name="client">
        ///     The <see cref="ConfigMapV1"/> resource client.
        /// </param>
        /// <param name="configMap">
        ///     A <see cref="ConfigMapV1"/> representing the new state for the ConfigMap.
        /// </param>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the operation.
        /// </param>
        /// <returns>
        ///     A <see cref="ConfigMapV1"/> representing the updated ConfigMap.
        /// </returns>
        /// <remarks>
        ///     Updates all mutable fields (if specified on <paramref name="configMap"/>).
        /// </remarks>
        public static Task <ConfigMapV1> Update(this ConfigMapClientV1 client, ConfigMapV1 configMap, CancellationToken cancellationToken = default)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (String.IsNullOrWhiteSpace(configMap?.Metadata?.Name))
            {
                throw new ArgumentException("Cannot update a ConfigMap if its metadata does not specify a name.", nameof(configMap));
            }

            if (String.IsNullOrWhiteSpace(configMap?.Metadata?.Namespace))
            {
                throw new ArgumentException("Cannot update a ConfigMap if its metadata does not specify a namespace.", nameof(configMap));
            }

            return(client.Update(
                       name: configMap.Metadata.Name,
                       kubeNamespace: configMap.Metadata.Namespace,
                       patchAction: patch =>
            {
                if (configMap.Metadata.Labels != null)
                {
                    patch.Replace(patchConfigMap => patchConfigMap.Metadata.Labels,
                                  value: configMap.Metadata.Labels
                                  );
                }

                if (configMap.Metadata.Annotations != null)
                {
                    patch.Replace(patchConfigMap => patchConfigMap.Metadata.Annotations,
                                  value: configMap.Metadata.Annotations
                                  );
                }

                if (configMap.Data != null)
                {
                    patch.Replace(patchConfigMap => patchConfigMap.Data,
                                  value: configMap.Data
                                  );
                }
            },
                       cancellationToken: cancellationToken
                       ));
        }
Exemple #5
0
        /// <summary>
        ///     Request creation of a <see cref="ConfigMapV1"/>.
        /// </summary>
        /// <param name="newConfigMap">
        ///     A <see cref="ConfigMapV1"/> representing the ConfigMap to create.
        /// </param>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the request.
        /// </param>
        /// <returns>
        ///     A <see cref="ConfigMapV1"/> representing the current state for the newly-created ConfigMap.
        /// </returns>
        public async Task <ConfigMapV1> Create(ConfigMapV1 newConfigMap, CancellationToken cancellationToken = default)
        {
            if (newConfigMap == null)
            {
                throw new ArgumentNullException(nameof(newConfigMap));
            }

            return(await Http
                   .PostAsJsonAsync(
                       Requests.Collection.WithTemplateParameters(new
            {
                Namespace = newConfigMap?.Metadata?.Namespace ?? KubeClient.DefaultNamespace
            }),
                       postBody : newConfigMap,
                       cancellationToken : cancellationToken
                       )
                   .ReadContentAsAsync <ConfigMapV1, StatusV1>());
        }
Exemple #6
0
        /// <summary>
        ///     Load configuration entries from the ConfigMap.
        /// </summary>
        public override void Load()
        {
            Log.LogTrace("Attempting to load ConfigMap {ConfigMapName} in namespace {KubeNamespace}...", _configMapName, _kubeNamespace ?? _client.DefaultNamespace);

            ConfigMapV1 configMap = _client.ConfigMapsV1().Get(_configMapName, _kubeNamespace).GetAwaiter().GetResult();

            Load(configMap);

            if (_watch && _watchSubscription == null)
            {
                Log.LogTrace("Creating watch-event stream for ConfigMap {ConfigMapName} in namespace {KubeNamespace}...", _configMapName, _kubeNamespace ?? _client.DefaultNamespace);

                _watchSubscription = _client.ConfigMapsV1()
                                     .Watch(_configMapName, _kubeNamespace)
                                     .Subscribe(OnConfigMapChanged);

                Log.LogTrace("Watch-event stream created for ConfigMap {ConfigMapName} in namespace {KubeNamespace}.", _configMapName, _kubeNamespace ?? _client.DefaultNamespace);
            }
        }
Exemple #7
0
        /// <summary>
        ///     Load data from the specified ConfigMap.
        /// </summary>
        /// <param name="configMap">
        ///     A <see cref="ConfigMapV1"/> representing the ConfigMap's current state, or <c>null</c> if the ConfigMap was not found.
        /// </param>
        void Load(ConfigMapV1 configMap)
        {
            if (configMap != null)
            {
                Log.LogTrace("Found ConfigMap {ConfigMapName} in namespace {KubeNamespace}.", _configMapName, _kubeNamespace ?? _client.DefaultNamespace);

                string sectionNamePrefix = !String.IsNullOrWhiteSpace(_sectionName) ? _sectionName + ":" : String.Empty;

                Data = configMap.Data.ToDictionary(
                    entry => sectionNamePrefix + entry.Key.Replace('.', ':'),
                    entry => entry.Value,
                    StringComparer.OrdinalIgnoreCase
                    );
            }
            else
            {
                Log.LogTrace("ConfigMap {ConfigMapName} was not found in namespace {KubeNamespace}.", _configMapName, _kubeNamespace ?? _client.DefaultNamespace);

                Data = new Dictionary <string, string>();
            }
        }
        /// <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);
            }
        }
Exemple #9
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 = Config.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);
            }
        }