/// <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); }
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); }
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; } }
public ServiceResources(DeploymentV1 deployment, StatefulSetV1 statefulSet, ServiceV1 service, IngressV1Beta1 ingress, SecretV1 secret) { Deployment = deployment; StatefulSet = statefulSet; Service = service; Ingress = ingress; Secret = secret; }
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; }
/// <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); }
/// <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")); }
/// <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()); }
/// <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); }
/// <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); }
/// <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); } }
/// <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); }
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)); } }
/// <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); }