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)); }
/// <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)); }