Пример #1
0
        /// <summary>
        ///     Update a Deployment, modifying its Command.
        /// </summary>
        /// <param name="client">
        ///     The Kubernetes API client.
        /// </param>
        /// <param name="existingDeployment">
        ///     A <see cref="DeploymentV1"/> representing the Deployment to update.
        /// </param>
        /// <returns>
        ///     A <see cref="DeploymentV1"/> representing the Deployment's current state.
        /// </returns>
        static async Task <DeploymentV1> UpdateDeployment(IKubeApiClient client, DeploymentV1 existingDeployment)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (existingDeployment == null)
            {
                throw new ArgumentNullException(nameof(existingDeployment));
            }

            DeploymentV1 updatedDeployment = await client.DeploymentsV1().Update(existingDeployment.Metadata.Name, kubeNamespace: existingDeployment.Metadata.Namespace, patchAction: patch =>
            {
                patch.Replace(
                    path: deployment => deployment.Spec.Template.Spec.Containers[0].Command,
                    value: new List <string> {
                    "/bin/sleep", "30m"
                }
                    );
            });

            // Re-fetch Deployment state so we pick up annotations added or updated by the controller.
            updatedDeployment = await client.DeploymentsV1().Get(updatedDeployment.Metadata.Name, updatedDeployment.Metadata.Namespace);

            return(updatedDeployment);
        }
Пример #2
0
        protected override async Task ProcessRecordAsync(CancellationToken cancellationToken)
        {
            await base.ProcessRecordAsync(cancellationToken);

            var _namespace = NamespaceObject?.Metadata.Name ?? Namespace;

            IEnumerable <DeploymentV1> deploymentList;

            if (String.IsNullOrEmpty(Name) || WildcardPattern.ContainsWildcardCharacters(Name))
            {
                deploymentList = await client.DeploymentsV1().List(
                    kubeNamespace: _namespace,
                    labelSelector: LabelSelector,
                    cancellationToken: cancellationToken
                    );
            }
            else
            {
                DeploymentV1 deployment = await client.DeploymentsV1().Get(
                    name: Name,
                    kubeNamespace: _namespace,
                    cancellationToken: cancellationToken
                    );

                deploymentList = new[] { deployment };
            }
            if (WildcardPattern.ContainsWildcardCharacters(Name))
            {
                var pattern = new WildcardPattern(Name);
                deploymentList = deploymentList.Where(deployment => pattern.IsMatch(deployment.Metadata.Name));
            }
            WriteObject(deploymentList, true);
        }
Пример #3
0
        public async Task<int> Scale(int numberOfPods, string k8sApiUri, string k8sBearerToken, string k8sDeploymentName)
        {
            try
            {
                KubeClientOptions options = new KubeClientOptions()
                {
                    ApiEndPoint = new Uri(k8sApiUri), //Change this for your own API URI
                    AuthStrategy = KubeAuthStrategy.BearerToken,
                    AccessToken = k8sBearerToken, //Change this for your own Bearer
                    AllowInsecure = true 
                };

                using (KubeApiClient client = KubeApiClient.Create(options))
                {
                    var deployments = await client.DeploymentsV1().List();
                    var currentDeployment = deployments.First(i=>i.Metadata.Name == k8sDeploymentName); //change first for the real one (rathena-openkore-cognitive)
                    DeploymentV1 updatedDeployment = await UpdateDeployment(client, currentDeployment, numberOfPods);
                }

                return ExitCodes.Success;
            }
            catch (Exception unexpectedError)
            {
                log.LogError("[ERROR] K8s unable to scale deployment: " + unexpectedError.ToString());
                return ExitCodes.UnexpectedError;
            }
        }
Пример #4
0
 public ServiceResources(DeploymentV1 deployment, StatefulSetV1 statefulSet, ServiceV1 service, IngressV1Beta1 ingress, SecretV1 secret)
 {
     Deployment  = deployment;
     StatefulSet = statefulSet;
     Service     = service;
     Ingress     = ingress;
     Secret      = secret;
 }
Пример #5
0
        static async Task<DeploymentV1> UpdateDeployment(IKubeApiClient client, DeploymentV1 existingDeployment, int numberOfPods)
        {
            DeploymentV1 updatedDeployment = await client.DeploymentsV1().Update(existingDeployment.Metadata.Name, kubeNamespace: existingDeployment.Metadata.Namespace, patchAction: patch =>
            {
                patch.Replace(
                    path: deployment => deployment.Spec.Replicas,
                    value: numberOfPods
                );
            });

            updatedDeployment = await client.DeploymentsV1().Get(updatedDeployment.Metadata.Name, updatedDeployment.Metadata.Namespace);
            return updatedDeployment;
        }
Пример #6
0
        /// <summary>
        ///     Roll back a Deployment to the revision represented by the specified ReplicaSet.
        /// </summary>
        /// <param name="client">
        ///     The Kubernetes API client.
        /// </param>
        /// <param name="existingDeployment">
        ///     The target Deployment.
        /// </param>
        /// <param name="targetRevisionReplicaSet">
        ///     The ReplicaSet that represents the target revision.
        /// </param>
        /// <returns>
        ///     A <see cref="DeploymentV1"/> representing the Deployment's current state.
        /// </returns>
        static async Task <DeploymentV1> RollbackDeployment(IKubeApiClient client, DeploymentV1 existingDeployment, ReplicaSetV1 targetRevisionReplicaSet)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (existingDeployment == null)
            {
                throw new ArgumentNullException(nameof(existingDeployment));
            }

            if (targetRevisionReplicaSet == null)
            {
                throw new ArgumentNullException(nameof(targetRevisionReplicaSet));
            }

            if (!DoesDeploymentOwnReplicaSet(existingDeployment, targetRevisionReplicaSet))
            {
                throw new InvalidOperationException($"ReplicaSet '{targetRevisionReplicaSet.Metadata.Name}' in namespace '{targetRevisionReplicaSet.Metadata.Namespace}' is not owned by Deployment '{existingDeployment.Metadata.Name}'.");
            }

            int?targetRevision = targetRevisionReplicaSet.GetRevision();

            if (targetRevision == null)
            {
                throw new InvalidOperationException($"Cannot determine Deployment revision represented by ReplicaSet '{targetRevisionReplicaSet.Metadata.Name}' in namespace '{targetRevisionReplicaSet.Metadata.Namespace}'.");
            }

            DeploymentV1 rolledBackDeployment = await client.DeploymentsV1().Update(existingDeployment.Metadata.Name, kubeNamespace: existingDeployment.Metadata.Namespace, patchAction: patch =>
            {
                patch.Replace(deployment =>
                              deployment.Spec.Template.Spec,
                              value: targetRevisionReplicaSet.Spec.Template.Spec
                              );

                // Since the Rollback API is now obsolete, we have to update the Deployment's revision by hand.
                Dictionary <string, string> annotationsWithModifiedRevision         = existingDeployment.Metadata.Annotations;
                annotationsWithModifiedRevision[K8sAnnotations.Deployment.Revision] = targetRevision.Value.ToString();
                patch.Replace(deployment =>
                              deployment.Metadata.Annotations,
                              value: annotationsWithModifiedRevision
                              );
            });

            // Re-fetch Deployment state so we pick up annotations added or updated by the controller.
            rolledBackDeployment = await client.DeploymentsV1().Get(rolledBackDeployment.Metadata.Name, rolledBackDeployment.Metadata.Namespace);

            return(rolledBackDeployment);
        }
Пример #7
0
        /// <summary>
        ///     Roll back a Deployment to the revision represented by the specified ReplicaSet.
        /// </summary>
        /// <param name="client">
        ///     The Kubernetes API client.
        /// </param>
        /// <param name="existingDeployment">
        ///     The target Deployment.
        /// </param>
        /// <param name="targetRevisionReplicaSet">
        ///     The ReplicaSet that represents the target revision.
        /// </param>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the request.
        /// </param>
        /// <returns>
        ///     A <see cref="DeploymentV1"/> representing the Deployment's current state.
        /// </returns>
        public static async Task <DeploymentV1> RollbackDeployment(IKubeApiClient client, DeploymentV1 existingDeployment, ReplicaSetV1 targetRevisionReplicaSet, CancellationToken cancellationToken = default)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (existingDeployment == null)
            {
                throw new ArgumentNullException(nameof(existingDeployment));
            }

            if (targetRevisionReplicaSet == null)
            {
                throw new ArgumentNullException(nameof(targetRevisionReplicaSet));
            }

            int?targetRevision = targetRevisionReplicaSet.GetRevision();

            if (targetRevision == null)
            {
                throw new InvalidOperationException($"Cannot determine Deployment revision represented by ReplicaSet '{targetRevisionReplicaSet.Metadata.Name}' in namespace '{targetRevisionReplicaSet.Metadata.Namespace}'.");
            }

            DeploymentV1 rolledBackDeployment = await client.DeploymentsV1().Update(existingDeployment.Metadata.Name, kubeNamespace: existingDeployment.Metadata.Namespace, cancellationToken: cancellationToken, patchAction: patch =>
            {
                // Restore Deployment's Pod-template specification to the one used by the target ReplicaSet.
                patch.Replace(deployment =>
                              deployment.Spec.Template.Spec,
                              value: targetRevisionReplicaSet.Spec.Template.Spec
                              );

                // Since the old Rollback API is obsolete (as of v1beta2), we have to update the Deployment's revision by hand.
                patch.Replace(deployment =>
                              deployment.Metadata.Annotations, // Due to JSON-PATCH limitations in the K8s API, we have to replace the entire Annotations property, not attempt to update individual items within the dictionary.
                              value: new Dictionary <string, string>(existingDeployment.Metadata.Annotations)
                {
                    [K8sAnnotations.Deployment.Revision] = targetRevision.Value.ToString()
                }
                              );
            });

            // Re-fetch Deployment state so we pick up annotations added or updated by the controller.
            rolledBackDeployment = await client.DeploymentsV1().Get(rolledBackDeployment.Metadata.Name, rolledBackDeployment.Metadata.Namespace, cancellationToken);

            return(rolledBackDeployment);
        }
        /// <summary>
        ///     Request creation of a <see cref="DeploymentV1"/>.
        /// </summary>
        /// <param name="newDeployment">
        ///     A <see cref="DeploymentV1"/> representing the Deployment to create.
        /// </param>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the request.
        /// </param>
        /// <returns>
        ///     A <see cref="DeploymentV1"/> representing the current state for the newly-created Deployment.
        /// </returns>
        public async Task <DeploymentV1> Create(DeploymentV1 newDeployment, CancellationToken cancellationToken = default)
        {
            if (newDeployment == null)
            {
                throw new ArgumentNullException(nameof(newDeployment));
            }

            return(await Http
                   .PostAsJsonAsync(
                       Requests.Collection.WithTemplateParameters(new
            {
                Namespace = newDeployment?.Metadata?.Namespace ?? KubeClient.DefaultNamespace
            }),
                       postBody : newDeployment,
                       cancellationToken : cancellationToken
                       )
                   .ReadContentAsObjectV1Async <DeploymentV1>("create v1/Deployment resource"));
        }
Пример #9
0
        /// <summary>
        ///     Update (PATCH) an existing Deployment.
        /// </summary>
        /// <param name="deploymentClient">
        ///     The Kubernetes Deployment (v1) API client.
        /// </param>
        /// <param name="existingDeployment">
        ///     A <see cref="DeploymentV1"/> representing the Deployment to update.
        /// </param>
        /// <param name="patchAction">
        ///     A delegate that customises the patch operation.
        /// </param>
        /// <returns>
        ///     A <see cref="DeploymentV1"/> representing the Deployment's current state.
        /// </returns>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the request.
        /// </param>
        /// <remarks>
        ///     The state of <paramref name="existingDeployment"/> is not used for update; it is simply convenient shorthand for specifying the deployment's name and namespace.
        /// </remarks>
        public static async Task <DeploymentV1> UpdateDeployment(this IDeploymentClientV1 deploymentClient, DeploymentV1 existingDeployment, Action <JsonPatchDocument <DeploymentV1> > patchAction, CancellationToken cancellationToken = default)
        {
            if (deploymentClient == null)
            {
                throw new ArgumentNullException(nameof(deploymentClient));
            }

            if (existingDeployment == null)
            {
                throw new ArgumentNullException(nameof(existingDeployment));
            }

            DeploymentV1 updatedDeployment = await deploymentClient.Update(existingDeployment.Metadata.Name, patchAction, existingDeployment.Metadata.Namespace, cancellationToken);

            // Re-fetch Deployment state so we pick up annotations added or updated by the controller.
            updatedDeployment = await deploymentClient.Get(updatedDeployment.Metadata.Name, updatedDeployment.Metadata.Namespace, cancellationToken);

            return(updatedDeployment);
        }
        public async Task <IActionResult> Post([FromBody] DeploymentModel model, string project)
        {
            DeploymentV1 initialDeployment = await kubeApiClient.DeploymentsV1().Create(new DeploymentV1
            {
                Metadata = new ObjectMetaV1
                {
                    Name      = model.Name,
                    Namespace = project
                },
                Spec = new DeploymentSpecV1
                {
                    Selector = new LabelSelectorV1
                    {
                        MatchLabels =
                        {
                            ["app"] = model.Name
                        }
                    },
                    Replicas             = 2,
                    RevisionHistoryLimit = 3,
                    Template             = new PodTemplateSpecV1
                    {
                        Spec = new PodSpecV1
                        {
                            Containers =
                            {
                                new ContainerV1
                                {
                                    Name  = "wordpress",
                                    Image = $"wordpress:{model.WordpressVersion}-apache",
                                }
                            }
                        }
                    }
                }
            });

            return(Ok());
        }
Пример #11
0
        /// <summary>
        ///     Determine whether a Deployment owns a ReplicaSet.
        /// </summary>
        /// <param name="deployment">
        ///     The Deployment to examine.
        /// </param>
        /// <param name="replicaSet">
        ///     The ReplicaSet to examine.
        /// </param>
        /// <returns>
        ///     <c>true</c>, if the ReplicaSet has an owner-reference to the Deployment; otherwise, <c>false</c>.
        /// </returns>
        static bool DoesDeploymentOwnReplicaSet(DeploymentV1 deployment, ReplicaSetV1 replicaSet)
        {
            if (deployment == null)
            {
                throw new ArgumentNullException(nameof(deployment));
            }

            if (replicaSet == null)
            {
                throw new ArgumentNullException(nameof(replicaSet));
            }

            // Sanity-check: does the target ReplicaSet actually represent a revision of the existing Deployment?
            bool isReplicaSetForDeployment = replicaSet.Metadata.OwnerReferences.Any(ownerReference =>
                                                                                     ownerReference.Kind == deployment.Kind
                                                                                     &&
                                                                                     ownerReference.ApiVersion == deployment.ApiVersion
                                                                                     &&
                                                                                     ownerReference.Name == deployment.Metadata.Name
                                                                                     );

            return(isReplicaSetForDeployment);
        }
Пример #12
0
        /// <summary>
        ///     Find the ReplicaSet that corresponds to the specified revision of the specified Deployment.
        /// </summary>
        /// <param name="client">
        ///     The Kubernetes API client.
        /// </param>
        /// <param name="deployment">
        ///     The target Deployment.
        /// </param>
        /// <param name="targetRevision">
        ///     The target revision.
        /// </param>
        /// <returns>
        ///     A <see cref="ReplicaSetV1"/> representing the ReplicaSet's current state; <c>null</c>, if no corresponding ReplicaSet was found.
        /// </returns>
        static async Task <ReplicaSetV1> FindReplicaSetForRevision(IKubeApiClient client, DeploymentV1 deployment, int targetRevision)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (deployment == null)
            {
                throw new ArgumentNullException(nameof(deployment));
            }

            ReplicaSetListV1 replicaSets = await client.ReplicaSetsV1().List(
                labelSelector: $"app={deployment.Metadata.Name}",
                kubeNamespace: deployment.Metadata.Namespace
                );

            ReplicaSetV1 targetRevisionReplicaSet = replicaSets.Items.FirstOrDefault(
                replicaSet => replicaSet.GetRevision() == targetRevision
                );

            return(targetRevisionReplicaSet);
        }
Пример #13
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);
            }
        }
Пример #14
0
        /// <summary>
        ///     Create the initial Deployment.
        /// </summary>
        /// <param name="client">
        ///     The Kubernetes API client.
        /// </param>
        /// <param name="deploymentName">
        ///     The name of the Deployment to create.
        /// </param>
        /// <param name="deploymentNamespace">
        ///     The name of the Kubernetes namespace in which the Deployment will be created.
        /// </param>
        /// <returns>
        ///     A <see cref="DeploymentV1"/> representing the Deployment's current state.
        /// </returns>
        static async Task <DeploymentV1> CreateInitialDeployment(IKubeApiClient client, string deploymentName, string deploymentNamespace)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (String.IsNullOrWhiteSpace(deploymentName))
            {
                throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'deploymentName'.", nameof(deploymentName));
            }

            if (String.IsNullOrWhiteSpace(deploymentNamespace))
            {
                throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'deploymentNamespace'.", nameof(deploymentNamespace));
            }

            DeploymentV1 initialDeployment = await client.DeploymentsV1().Create(new DeploymentV1
            {
                Metadata = new ObjectMetaV1
                {
                    Name      = deploymentName,
                    Namespace = deploymentNamespace
                },
                Spec = new DeploymentSpecV1
                {
                    Selector = new LabelSelectorV1
                    {
                        MatchLabels =
                        {
                            ["app"] = deploymentName
                        }
                    },
                    Replicas             = 2,
                    RevisionHistoryLimit = 3,
                    Template             = new PodTemplateSpecV1
                    {
                        Metadata = new ObjectMetaV1
                        {
                            Labels =
                            {
                                ["app"] = deploymentName
                            }
                        },
                        Spec = new PodSpecV1
                        {
                            Containers =
                            {
                                new ContainerV1
                                {
                                    Name    = "demo-container",
                                    Image   = "ubuntu:xenial",
                                    Command ={ "/bin/sleep",               "20m" }
                                }
                            }
                        }
                    }
                }
            });

            // Re-fetch Deployment state so we pick up annotations added by the controller.
            initialDeployment = await client.DeploymentsV1().Get(initialDeployment.Metadata.Name, initialDeployment.Metadata.Namespace);

            return(initialDeployment);
        }
Пример #15
0
        private async Task <ServiceResources> DeployService(DeployCommand.Types.Service command, string kubeNamespace)
        {
            DeploymentV1  deployment  = null;
            StatefulSetV1 statefulSet = null;

            var secret = await CreateSecret();

            if (string.IsNullOrWhiteSpace(command.PersistentStoragePath))
            {
                deployment = await CreateDeployment();
            }
            else
            {
                statefulSet = await CreateStatefulSet();
            }

            var service = await CreateService();

            var ingress = await CreateServiceIngress();

            return(new ServiceResources(deployment, statefulSet, service, ingress, secret));

            async Task <SecretV1> CreateSecret()
            {
                if (!command.Secrets.Any())
                {
                    return(null);
                }

                var existingSecret = await kubeApiClient.SecretsV1().Get(command.Name, kubeNamespace);

                var secretResource = new SecretV1
                {
                    Metadata = new ObjectMetaV1
                    {
                        Name      = command.Name,
                        Namespace = kubeNamespace,
                    },
                    Type = "Opaque",
                };

                if (existingSecret != null)
                {
                    foreach (var existingData in existingSecret.Data)
                    {
                        secretResource.Data[existingData.Key] = existingData.Value;
                    }
                }

                foreach (var secretCommand in command.Secrets.Where(s => s.Value != null))
                {
                    secretResource.Data[secretCommand.Name] = Convert.ToBase64String(Encoding.UTF8.GetBytes(secretCommand.Value));
                }

                foreach (var existingSecretNames in secretResource.Data.Keys)
                {
                    if (command.Secrets.All(secretCommand => secretCommand.Name != existingSecretNames))
                    {
                        secretResource.Data.Remove(existingSecretNames);
                    }
                }

                return(await kubeApiClient.Dynamic().Apply(secretResource, fieldManager: "clud", force: true));
            }

            async Task <DeploymentV1> CreateDeployment()
            {
                var deployment = new DeploymentV1
                {
                    Metadata = new ObjectMetaV1
                    {
                        Name      = command.Name,
                        Namespace = kubeNamespace,
                    },
                    Spec = new DeploymentSpecV1
                    {
                        Selector = new LabelSelectorV1
                        {
                            MatchLabels = { { KubeNaming.AppLabelKey, command.Name } },
                        },
                        Replicas = command.Replicas,
                        Template = new PodTemplateSpecV1
                        {
                            Metadata = new ObjectMetaV1
                            {
                                Name      = command.Name,
                                Namespace = kubeNamespace,
                                Labels    = { { KubeNaming.AppLabelKey, command.Name } }
                            },
                            Spec = new PodSpecV1
                            {
                                Containers =
                                {
                                    new ContainerV1
                                    {
                                        Name  = command.Name,
                                        Image = DockerImageName(),
                                    }
                                },
                            },
                        }
                    }
                };

                AddEnvironmentVariables(deployment.Spec.Template.Spec.Containers.Single().Env);

                return(await kubeApiClient.Dynamic().Apply(deployment, fieldManager: "clud", force: true));
            }

            async Task <StatefulSetV1> CreateStatefulSet()
            {
                var statefulSet = new StatefulSetV1
                {
                    Metadata = new ObjectMetaV1
                    {
                        Name      = command.Name,
                        Namespace = kubeNamespace,
                    },
                    Spec = new StatefulSetSpecV1
                    {
                        Selector = new LabelSelectorV1
                        {
                            MatchLabels = { { KubeNaming.AppLabelKey, command.Name } },
                        },
                        Template = new PodTemplateSpecV1
                        {
                            Metadata = new ObjectMetaV1
                            {
                                Name      = command.Name,
                                Namespace = kubeNamespace,
                                Labels    = { { KubeNaming.AppLabelKey, command.Name } }
                            },
                            Spec = new PodSpecV1
                            {
                                Containers =
                                {
                                    new ContainerV1
                                    {
                                        Name         = command.Name,
                                        Image        = DockerImageName(),
                                        VolumeMounts =   { new VolumeMountV1
                                                           {
                                                               Name      = command.Name,
                                                               MountPath = command.PersistentStoragePath,
                                                           } }
                                    },
                                },
                            },
                        },
                        VolumeClaimTemplates =
                        {
                            new PersistentVolumeClaimV1
                            {
                                Metadata = new ObjectMetaV1
                                {
                                    Name      = command.Name,
                                    Namespace = kubeNamespace,
                                },
                                Spec = new PersistentVolumeClaimSpecV1
                                {
                                    AccessModes ={ "ReadWriteOnce"                   },
                                    Resources   = new ResourceRequirementsV1
                                    {
                                        Requests ={ { "storage", "100Mi"              } },
                                    }
                                }
                            }
                        }
                    }
                };

                AddEnvironmentVariables(statefulSet.Spec.Template.Spec.Containers.Single().Env);

                return(await kubeApiClient.Dynamic().Apply(statefulSet, fieldManager: "clud", force: true));
            }

            string DockerImageName()
            {
                return(command.IsPublicDockerImage
                    ? command.DockerImage
                    : $"{KubeNaming.DockerRegistryLocation}/{command.DockerImage}");
            }

            void AddEnvironmentVariables(List <EnvVarV1> envVarV1s)
            {
                envVarV1s.AddRange(command.EnvironmentVariables.Select(env => new EnvVarV1
                {
                    Name  = env.Name,
                    Value = env.Value
                }));

                envVarV1s.AddRange(command.Secrets.Select(secret => new EnvVarV1
                {
                    Name      = secret.Name,
                    ValueFrom = new EnvVarSourceV1
                    {
                        SecretKeyRef = new SecretKeySelectorV1
                        {
                            Name     = command.Name,
                            Key      = secret.Name,
                            Optional = false,
                        }
                    }
                }));
            }

            async Task <ServiceV1> CreateService()
            {
                var service = new ServiceV1
                {
                    Metadata = new ObjectMetaV1
                    {
                        Name      = command.Name,
                        Namespace = kubeNamespace,
                    },
                    Spec = new ServiceSpecV1
                    {
                        Selector = { { KubeNaming.AppLabelKey, command.Name } },
                    },
                };

                if (command.HttpPort != null)
                {
                    service.Spec.Ports.Add(new ServicePortV1
                    {
                        Name     = KubeNaming.HttpPortName,
                        Protocol = "TCP",
                        Port     = command.HttpPort.Value
                    });
                }

                service.Spec.Ports.AddRange(command.TcpPorts.Select(port => new ServicePortV1
                {
                    Name     = $"tcp-{port}",
                    Protocol = "TCP",
                    Port     = port,
                }));

                service.Spec.Ports.AddRange(command.UdpPorts.Select(port => new ServicePortV1
                {
                    Name     = $"udp-{port}",
                    Protocol = "UDP",
                    Port     = port,
                }));

                return(await kubeApiClient.Dynamic().Apply(service, fieldManager: "clud", force: true));
            }

            async Task <IngressV1Beta1> CreateServiceIngress()
            {
                if (command.HttpPort == null)
                {
                    return(null);
                }

                var ingress = new IngressV1Beta1
                {
                    Metadata = new ObjectMetaV1
                    {
                        Name      = command.Name,
                        Namespace = kubeNamespace,
                    },
                    Spec = new IngressSpecV1Beta1
                    {
                        Rules =
                        {
                            new IngressRuleV1Beta1
                            {
                                Host = $"{command.Name}-{kubeNamespace}.{cludOptions.BaseHostname}",
                                Http = new HTTPIngressRuleValueV1Beta1
                                {
                                    Paths =
                                    {
                                        new HTTPIngressPathV1Beta1
                                        {
                                            Path    = "/",
                                            Backend = new IngressBackendV1Beta1
                                            {
                                                ServiceName = service.Metadata.Name,
                                                ServicePort = KubeNaming.HttpPortName,
                                            }
                                        }
                                    }
                                }
                            }
                        },
                    },
                };

                return(await kubeApiClient.Dynamic().Apply(ingress, fieldManager: "clud", force: true));
            }
        }
Пример #16
0
        /// <summary>
        ///     Find the ReplicaSet that corresponds to the specified revision of the specified Deployment.
        /// </summary>
        /// <param name="client">
        ///     The Kubernetes API client.
        /// </param>
        /// <param name="deployment">
        ///     The target Deployment.
        /// </param>
        /// <param name="targetRevision">
        ///     The target revision.
        /// </param>
        /// <param name="cancellationToken">
        ///     An optional <see cref="CancellationToken"/> that can be used to cancel the request.
        /// </param>
        /// <returns>
        ///     A <see cref="ReplicaSetV1"/> representing the ReplicaSet's current state; <c>null</c>, if no corresponding ReplicaSet was found.
        /// </returns>
        public static async Task <ReplicaSetV1> FindReplicaSetForRevision(IKubeApiClient client, DeploymentV1 deployment, int targetRevision, CancellationToken cancellationToken = default)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (deployment == null)
            {
                throw new ArgumentNullException(nameof(deployment));
            }

            string matchLabelSelector = deployment.GetLabelSelector();

            ReplicaSetListV1 replicaSets = await client.ReplicaSetsV1().List(matchLabelSelector, deployment.Metadata.Namespace, cancellationToken);

            ReplicaSetV1 targetRevisionReplicaSet = replicaSets.Items.FirstOrDefault(
                replicaSet => replicaSet.GetRevision() == targetRevision
                );

            return(targetRevisionReplicaSet);
        }