in src/library/Connect/KubernetesRemoteEnvironmentManager.cs [322:419]
private async Task<V1Pod> _DeployAgentOnlyPodAsync(string agentImage, string namespaceName, string podName, string containerName, IDictionary<int, string> tcpPorts, IDictionary<string, string> labels, CancellationToken cancellationToken)
{
using (var perfLogger = _log.StartPerformanceLogger(Events.KubernetesRemoteEnvironmentManager.AreaName, Events.KubernetesRemoteEnvironmentManager.Operations.DeployAgentOnlyPod))
{
// Generate pod spec
V1Pod podSpec = new V1Pod()
{
ApiVersion = "v1",
Kind = "Pod",
Metadata = new V1ObjectMeta
{
Name = podName
},
Spec = new V1PodSpec
{
TerminationGracePeriodSeconds = 0,
Containers = new List<V1Container>(),
NodeSelector = new Dictionary<string, string>() { { KubernetesConstants.Labels.OS, KubernetesConstants.Labels.Values.Linux } }
}
};
// Add devhostagent container to the pod
V1Container containerSpec = new V1Container
{
Image = agentImage,
ImagePullPolicy = "IfNotPresent",
Name = containerName
};
if (tcpPorts != null && tcpPorts.Count > 0)
{
containerSpec.Ports = new List<V1ContainerPort>();
foreach (var p in tcpPorts)
{
containerSpec.Ports.Add(new V1ContainerPort()
{
ContainerPort = p.Key,
Name = p.Value,
Protocol = "TCP"
});
}
}
podSpec.Spec.Containers.Add(containerSpec);
// Add a label to identify that the pod has been created for connect clone scenario
if (labels != null && labels.Count > 0)
{
podSpec.Metadata.Labels = labels;
}
if (podSpec.Metadata.Labels == null)
{
podSpec.Metadata.Labels = new Dictionary<string, string>();
}
podSpec.Metadata.Labels[Common.Constants.Labels.ConnectCloneLabel] = "true";
// Deploy the pod in the namespace and wait for it to get created
var deployedPod = await _kubernetesClient.CreateV1PodAsync(namespaceName, podSpec, cancellationToken);
if (deployedPod == null)
{
throw new UserVisibleException(_operationContext, Resources.FailedToDeployPodMessage);
}
var podDeployment = new PodDeployment(deployedPod);
try
{
// Deploy remote restore job
await _remoteRestoreJobDeployer.CreateRemoteRestoreJobAsync(deployedPod.Name(), namespaceName, podDeployment, cancellationToken);
// Save the pod to deployed pods context
_patchState.AddPodDeployment(podDeployment);
}
catch (Exception ex)
{
// Try to clean up deployed pod
this._ReportProgress(EventLevel.Warning, Resources.RestoringPodDeploymentMessage);
await _workloadRestorationService.Value.RemovePodDeploymentAsync(
podDeployment,
cancellationToken,
progressCallback: p => this._ReportProgress(p.Message, p.Level),
noThrow: true);
var errorMessage = $"Failed to deploy remote restore job for pod deployment {deployedPod.Namespace()}/{deployedPod.Name()}. {ex.Message}";
// In this case we cannot really do anything so logging it as warning
if (this.IsForbiddenHttpOperationException(ex))
{
_log.Warning(errorMessage);
}
else
{
_log.Error(errorMessage);
}
throw new UserVisibleException(_operationContext, ex, Resources.FailedToDeployRemoteRestoreJobFormat, deployedPod.Namespace(), deployedPod.Name(), ex.Message);
}
deployedPod = await _WaitForPodToRunAsync(deployedPod, TimeSpan.FromMinutes(5), cancellationToken);
this._ReportProgress(Resources.PodCreatedFormat, deployedPod.Metadata.NamespaceProperty, deployedPod.Metadata.Name);
perfLogger.SetSucceeded();
return deployedPod;
}
}