public static async Task Main(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args)
                          .ConfigureAppConfiguration((hostContext, cb) =>
            {
                cb.AddJsonFile("appsettings.json", false, false)
                .AddJsonFile(
                    $"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json",
                    true, false);

                if (hostContext.HostingEnvironment.IsDevelopment())
                {
                    cb.AddUserSecrets <Program>();
                }
                else
                {
                    var config = cb.Build();

                    if (!hostContext.HostingEnvironment.IsDevelopment())
                    {
                        cb.AddKubeConfigMap(KubeClientOptions.FromPodServiceAccount(),
                                            config[KubeConfigMapName],
                                            config[KubeNamespace],
                                            reloadOnChange: false);
                    }
                    // .AddKubeSecret(
                    //     KubeClientOptions.FromPodServiceAccount(),
                    //     config[KubeSecretName],
                    //     config[KubeNamespace],
                    //     reloadOnChange: true);
                }

                cb.AddEnvironmentVariables();

                if (args != null)
                {
                    cb.AddCommandLine(args);
                }
            })
                          .ConfigureServices((hostContext, services) =>
            {
                services
                .AddOptions()
                .Configure <DataFabricSplitterConfig>(hostContext.Configuration);

                services
                .AddTransient <IEventHub>(sp =>
                                          new RabbitMQHub(new ConnectionFactory
                {
                    Endpoint = new AmqpTcpEndpoint(new Uri(hostContext.Configuration[nameof(DataFabricSplitterConfig.EventHubEndpoint)])),
                    DispatchConsumersAsync = true
                }))
                .AddHostedService <DataFabricSplitterService>();
            })
                          .ConfigureLogging((hostingContext, logging) => {
                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
            });

            await builder.RunConsoleAsync();
        }
        /// <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,
                    loggerFactory: loggers
                    );

                using (KubeApiClient client = KubeApiClient.Create(clientOptions))
                {
                    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);
            }
        }
        /// <summary>
        ///     Add the specified Kubernetes ConfigMap as a configuration source.
        /// </summary>
        /// <param name="configurationBuilder">
        ///     The <see cref="IConfigurationBuilder"/> to configure.
        /// </param>
        /// <param name="clientOptions">
        ///     <see cref="KubeClientOptions"/> for the <see cref="KubeApiClient"/> used to communicate with the Kubernetes API.
        /// </param>
        /// <param name="configMapName">
        ///     The name of the target ConfigMap.
        /// </param>
        /// <param name="kubeNamespace">
        ///     The Kubernetes namespace that contains the target ConfigMap.
        /// </param>
        /// <param name="sectionName">
        ///     The name of the target configuration section (if any).
        /// </param>
        /// <param name="reloadOnChange">
        ///     Reload the configuration if the ConfigMap changes?
        /// </param>
        /// <returns>
        ///     The configured <see cref="IConfigurationBuilder"/>.
        /// </returns>
        public static IConfigurationBuilder AddKubeConfigMap(this IConfigurationBuilder configurationBuilder, KubeClientOptions clientOptions, string configMapName, string kubeNamespace = null, string sectionName = null, bool reloadOnChange = false)
        {
            if (configurationBuilder == null)
            {
                throw new ArgumentNullException(nameof(configurationBuilder));
            }

            KubeApiClient client = KubeApiClient.Create(clientOptions);

            return(configurationBuilder.AddKubeConfigMap(client, configMapName, kubeNamespace, sectionName, reloadOnChange));
        }
Exemple #4
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;
                }

                KubeApiClient client = KubeApiClient.Create(clientOptions);

                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);
            }
        }
        /// <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);
            }
        }
        static void Main(string[] args)
        {
            var config = new Plugin
            {
                Namespace   = Environment.GetEnvironmentVariable("PLUGIN_NAMESPACE"),
                Name        = Environment.GetEnvironmentVariable("PLUGIN_NAME"),
                Environment = Environment.GetEnvironmentVariable("PLUGIN_ENVIRONMENT") ?? "production",
                Image       = Environment.GetEnvironmentVariable("PLUGIN_IMAGE"),
                Cpu         = Environment.GetEnvironmentVariable("PLUGIN_CPU"),
                Mem         = Environment.GetEnvironmentVariable("PLUGIN_MEM"),
                Rsvp        = Environment.GetEnvironmentVariable("PLUGIN_RSVP") == "true",
                Port        = Environment.GetEnvironmentVariable("PLUGIN_PORT") == null
                    ? 0
                    : int.Parse(Environment.GetEnvironmentVariable("PLUGIN_PORT")),
                ServiceType    = Environment.GetEnvironmentVariable("PLUGIN_SERVICE_TYPE") ?? "ClusterIP",
                Url            = Environment.GetEnvironmentVariable("PLUGIN_URL"),
                Acme           = Environment.GetEnvironmentVariable("PLUGIN_ACME") == "true",
                K8SUrl         = Environment.GetEnvironmentVariable("PLUGIN_K8S_URL"),
                K8SToken       = Environment.GetEnvironmentVariable("PLUGIN_K8S_TOKEN"),
                RegistrySecret = Environment.GetEnvironmentVariable("PLUGIN_REGISTRY_SECRET") ?? "simcu",
                Debug          = Environment.GetEnvironmentVariable("PLUGIN_DEBUG") == "true",
                //New:
                EntryPoint  = Environment.GetEnvironmentVariable("PLUGIN_ENTRY_POINT"),
                Command     = Environment.GetEnvironmentVariable("PLUGIN_COMMAND"),
                Labels      = new Dictionary <string, string>(),
                Annotations = new Dictionary <string, string>()
            };

            if (config.Namespace == null)
            {
                config.Namespace = Environment.GetEnvironmentVariable("DRONE_REPO_NAMESPACE");
            }

            if (config.Name == null)
            {
                config.Name = Environment.GetEnvironmentVariable("DRONE_REPO_NAME");
            }

            Log("Application Config:", "CONF");
            foreach (var prop in config.GetType().GetProperties())
            {
                if (new[] { "K8SUrl", "K8SToken" }.Contains(prop.Name))
                {
                    continue;
                }
                if (new[] { "Labels", "Annotations" }.Contains(prop.Name))
                {
                    Log($"{prop.Name} => {JsonSerializer.Serialize(prop.GetValue(config))}", "CONF");
                }
                else
                {
                    Log($"{prop.Name} => {prop.GetValue(config)}", "CONF");
                }
            }

            Log("Start Deploy ....", "INFO");

            var kubeOptions = new KubeClientOptions();

            if (config.K8SUrl == null)
            {
                if (Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST") != null &&
                    Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_PORT") != null)
                {
                    var k8s =
                        $"https://{Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST")}:{Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_PORT")}/";
                    kubeOptions = new KubeClientOptions
                    {
                        ApiEndPoint  = new Uri(k8s),
                        AuthStrategy = KubeAuthStrategy.BearerToken,
                        AccessToken  = File.ReadAllText("/var/run/secrets/kubernetes.io/serviceaccount/token"),
                        CertificationAuthorityCertificate =
                            new X509Certificate2(
                                File.ReadAllText("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt")
                                )
                    };
                }
                else
                {
                    Log("K8S Cluster Url isn't defined");
                }
            }
            else if (config.K8SToken == null)
            {
                Log("K8S Cluster Token isn't defined");
            }
            else
            {
                kubeOptions = new KubeClientOptions
                {
                    ApiEndPoint   = new Uri(config.K8SUrl),
                    AccessToken   = config.K8SToken,
                    AuthStrategy  = KubeAuthStrategy.BearerToken,
                    AllowInsecure = true
                };
            }

            if (config.Namespace == null)
            {
                Log("Namespace isn't defined");
            }

            if (config.Name == null)
            {
                Log("Name isn't defined");
            }

            if (config.Image == null)
            {
                Log("Image isn't defined");
            }

            if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PLUGIN_LABLES")))
            {
                foreach (var item in Environment.GetEnvironmentVariable("PLUGIN_LABLES").Split('|'))
                {
                    var itemArr = item.Split('=', 2);
                    if (itemArr.Length == 2)
                    {
                        config.Labels.Add(itemArr[0], itemArr[1]);
                    }
                }
            }

            if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PLUGIN_ANNOTATIONS")))
            {
                foreach (var item in Environment.GetEnvironmentVariable("PLUGIN_ANNOTATIONS").Split('|'))
                {
                    var itemArr = item.Split('=', 2);
                    if (itemArr.Length == 2)
                    {
                        config.Annotations.Add(itemArr[0], itemArr[1]);
                    }
                }
            }


            var kube = new Kubernetes(KubeApiClient.Create(kubeOptions));

            kube.CheckAndCreateNamespace(config.Namespace);
            kube.UpdateDeployment(config.Namespace, config.Name, config.Environment, config.Image, config.Cpu,
                                  config.Mem, config.Rsvp, config.Port, config.RegistrySecret, config);
            kube.UpdateService(config.Namespace, config.Name, config.Environment, config.ServiceType, config.Port);
            kube.UpdateIngress(config.Namespace, config.Name, config.Environment, config.Url, config.Port, config.Acme);
        }
        /// <summary>
        /// Add or Create a Kubernetes Secret as a Repository.
        /// </summary>
        /// <param name="builder">
        /// The <see cref="IDataProtectionBuilder"/> to Configure.
        /// </param>
        /// <param name="clientOptions">
        /// <see cref="KubeClientOptions"/> for the <see cref="KubeApiClient"/> used to communicate with the Kubernetes API.
        /// </param>
        /// <param name="secretName">
        ///  The name of the target Secret.
        /// </param>
        /// <param name="kubeNamespace">
        ///  The namespace of the target Secret.
        /// </param>
        /// <returns>
        /// The configured <see cref="IDataProtectionBuilder"/>.
        /// </returns>
        public static IDataProtectionBuilder PersistKeysToKubeSecret(this IDataProtectionBuilder builder, KubeClientOptions clientOptions, string secretName, string kubeNamespace = null)
        {
            KubeApiClient client = KubeApiClient.Create(clientOptions);

            return(PersistKeysToKubeSecretInternal(builder, client, secretName, kubeNamespace));
        }