Skip to content

Commit e7277ea

Browse files
authored
fix: manifest generation fixes for initContainers, fix config type deploy status (#68)
* fix: configuration type stuck in queued status * fix: init ctn manifest generation * fix: strategy when pvc to avoid muti attach errors
1 parent 0b5fd74 commit e7277ea

File tree

2 files changed

+208
-25
lines changed

2 files changed

+208
-25
lines changed

src/server/lib/kubernetes.ts

Lines changed: 204 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,39 +1702,186 @@ function generateSingleDeploymentManifest({
17021702
const nodeSelector = enableFullYaml ? deploy.deployable?.nodeSelector : deploy.service?.nodeSelector;
17031703

17041704
const envToUse = deploy.env || {};
1705-
const containers = [];
1706-
const volumes: VOLUME[] = [];
1707-
const volumeMounts = [];
1705+
const containers: Array<Record<string, any>> = [];
1706+
const initContainers: Array<Record<string, any>> = [];
1707+
1708+
const volumes: VOLUME[] = [
1709+
{
1710+
emptyDir: {},
1711+
name: 'config-volume',
1712+
},
1713+
];
1714+
const volumeMounts: Array<{ name: string; mountPath: string }> = [];
1715+
1716+
const datadogLabels = {
1717+
'tags.datadoghq.com/env': `lifecycle-${build.uuid}`,
1718+
'tags.datadoghq.com/service': serviceName || name,
1719+
'tags.datadoghq.com/version': build.uuid,
1720+
};
17081721

17091722
// Handle init container if present
17101723
if (deploy.initDockerImage) {
1711-
const initEnvObj = flattenObject(build.commentInitEnv);
1712-
const initEnvArray = Object.entries(initEnvObj).map(([key, value]) => ({
1713-
name: key,
1714-
value: String(value),
1715-
}));
1724+
const initEnvObj = _.merge(
1725+
{ __NAMESPACE__: 'lifecycle' },
1726+
deploy.initEnv || {},
1727+
flattenObject(build.commentInitEnv)
1728+
);
1729+
1730+
const initEnvArray: Array<Record<string, any>> = Object.entries(initEnvObj)
1731+
.filter(([, value]) => !_.isObject(value))
1732+
.map(([key, value]) => ({
1733+
name: key,
1734+
value: String(value),
1735+
}));
1736+
1737+
initEnvArray.push(
1738+
{
1739+
name: 'POD_IP',
1740+
valueFrom: {
1741+
fieldRef: {
1742+
fieldPath: 'status.podIP',
1743+
},
1744+
},
1745+
},
1746+
{
1747+
name: 'DD_AGENT_HOST',
1748+
valueFrom: {
1749+
fieldRef: {
1750+
fieldPath: 'status.hostIP',
1751+
},
1752+
},
1753+
}
1754+
);
17161755

1717-
const initContainer = {
1756+
const initContainer: Record<string, any> = {
17181757
name: `init-${serviceName || 'container'}`,
17191758
image: deploy.initDockerImage,
1720-
imagePullPolicy: 'Always',
1759+
imagePullPolicy: 'IfNotPresent',
17211760
env: initEnvArray,
1761+
volumeMounts: [
1762+
{
1763+
mountPath: '/config',
1764+
name: 'config-volume',
1765+
},
1766+
],
17221767
};
1723-
containers.push(initContainer);
1768+
1769+
if (serviceCPU || serviceMemory) {
1770+
initContainer.resources = {
1771+
limits: {},
1772+
requests: {},
1773+
};
1774+
if (serviceCPU) {
1775+
initContainer.resources.limits.cpu = serviceCPU;
1776+
initContainer.resources.requests.cpu = serviceCPU;
1777+
}
1778+
if (serviceMemory) {
1779+
initContainer.resources.limits.memory = serviceMemory;
1780+
initContainer.resources.requests.memory = serviceMemory;
1781+
}
1782+
}
1783+
1784+
if (servicePort) {
1785+
initContainer.ports = [];
1786+
for (const port of servicePort.split(',')) {
1787+
initContainer.ports.push({
1788+
name: `port-${port}`,
1789+
containerPort: Number(port),
1790+
});
1791+
}
1792+
}
1793+
1794+
if (enableFullYaml) {
1795+
if (deploy.deployable?.initCommand) {
1796+
initContainer.command = [deploy.deployable.initCommand];
1797+
}
1798+
if (deploy.deployable?.initArguments) {
1799+
initContainer.args = deploy.deployable.initArguments.split('%%SPLIT%%');
1800+
}
1801+
}
1802+
1803+
initContainers.push(initContainer);
17241804
}
17251805

17261806
// Handle main container
1727-
const mainEnvObj = flattenObject({ ...build.commentRuntimeEnv, ...envToUse });
1728-
const mainEnvArray = Object.entries(mainEnvObj).map(([key, value]) => ({
1729-
name: key,
1730-
value: String(value),
1731-
}));
1807+
const mainEnvObj = _.merge({ __NAMESPACE__: 'lifecycle' }, envToUse, flattenObject(build.commentRuntimeEnv));
1808+
const mainEnvArray: Array<Record<string, any>> = Object.entries(mainEnvObj)
1809+
.filter(([, value]) => !_.isObject(value))
1810+
.map(([key, value]) => ({
1811+
name: key,
1812+
value: String(value),
1813+
}));
1814+
1815+
// Add Kubernetes field references for pod metadata
1816+
mainEnvArray.push(
1817+
{
1818+
name: 'POD_IP',
1819+
valueFrom: {
1820+
fieldRef: {
1821+
fieldPath: 'status.podIP',
1822+
},
1823+
},
1824+
},
1825+
{
1826+
name: 'DD_AGENT_HOST',
1827+
valueFrom: {
1828+
fieldRef: {
1829+
fieldPath: 'status.hostIP',
1830+
},
1831+
},
1832+
}
1833+
);
1834+
1835+
// Add Datadog env vars from labels (only if not already set)
1836+
const existingEnvKeys = new Set(mainEnvArray.map((e) => e.name));
1837+
if (!existingEnvKeys.has('DD_ENV')) {
1838+
mainEnvArray.push({
1839+
name: 'DD_ENV',
1840+
valueFrom: {
1841+
fieldRef: {
1842+
fieldPath: "metadata.labels['tags.datadoghq.com/env']",
1843+
},
1844+
},
1845+
});
1846+
}
1847+
if (!existingEnvKeys.has('DD_SERVICE')) {
1848+
mainEnvArray.push({
1849+
name: 'DD_SERVICE',
1850+
valueFrom: {
1851+
fieldRef: {
1852+
fieldPath: "metadata.labels['tags.datadoghq.com/service']",
1853+
},
1854+
},
1855+
});
1856+
}
1857+
if (!existingEnvKeys.has('DD_VERSION')) {
1858+
mainEnvArray.push({
1859+
name: 'DD_VERSION',
1860+
valueFrom: {
1861+
fieldRef: {
1862+
fieldPath: "metadata.labels['tags.datadoghq.com/version']",
1863+
},
1864+
},
1865+
});
1866+
}
1867+
if (!existingEnvKeys.has('LC_UUID')) {
1868+
mainEnvArray.push({
1869+
name: 'LC_UUID',
1870+
value: build.uuid,
1871+
});
1872+
}
17321873

17331874
const mainContainer: any = {
17341875
name: serviceName || 'main',
17351876
image: deploy.dockerImage,
1736-
imagePullPolicy: 'Always',
1877+
imagePullPolicy: 'IfNotPresent',
17371878
env: mainEnvArray,
1879+
volumeMounts: [
1880+
{
1881+
mountPath: '/config',
1882+
name: 'config-volume',
1883+
},
1884+
],
17381885
};
17391886

17401887
// Only add resources if they are defined
@@ -1760,12 +1907,15 @@ function generateSingleDeploymentManifest({
17601907
mainContainer.ports = [];
17611908
for (const port of servicePort.split(',')) {
17621909
mainContainer.ports.push({
1910+
name: `port-${port}`,
17631911
containerPort: Number(port),
17641912
});
17651913
}
17661914
}
17671915

1768-
// Handle volumes
1916+
// Handle additional volumes (service disks)
1917+
let hasPersistentVolumeClaims = false;
1918+
17691919
if (enableFullYaml && deploy.deployable?.serviceDisksYaml) {
17701920
const serviceDisks: ServiceDiskConfig[] = JSON.parse(deploy.deployable.serviceDisksYaml);
17711921
serviceDisks.forEach((disk) => {
@@ -1775,6 +1925,8 @@ function generateSingleDeploymentManifest({
17751925
emptyDir: {},
17761926
});
17771927
} else {
1928+
// EBS or other persistent disk - requires Recreate strategy
1929+
hasPersistentVolumeClaims = true;
17781930
volumes.push({
17791931
name: disk.name,
17801932
persistentVolumeClaim: {
@@ -1795,6 +1947,8 @@ function generateSingleDeploymentManifest({
17951947
emptyDir: {},
17961948
});
17971949
} else {
1950+
// EBS or other persistent disk - requires Recreate strategy
1951+
hasPersistentVolumeClaims = true;
17981952
volumes.push({
17991953
name: disk.name,
18001954
persistentVolumeClaim: {
@@ -1809,8 +1963,9 @@ function generateSingleDeploymentManifest({
18091963
});
18101964
}
18111965

1966+
// Add additional volume mounts to main container
18121967
if (volumeMounts.length > 0) {
1813-
mainContainer.volumeMounts = volumeMounts;
1968+
mainContainer.volumeMounts = [...mainContainer.volumeMounts, ...volumeMounts];
18141969
}
18151970

18161971
// Add probes
@@ -1830,6 +1985,16 @@ function generateSingleDeploymentManifest({
18301985
}
18311986
}
18321987

1988+
// Add command/args if specified
1989+
if (enableFullYaml) {
1990+
if (deploy.deployable?.command) {
1991+
mainContainer.command = [deploy.deployable.command];
1992+
}
1993+
if (deploy.deployable?.arguments) {
1994+
mainContainer.args = deploy.deployable.arguments.split('%%SPLIT%%');
1995+
}
1996+
}
1997+
18331998
containers.push(mainContainer);
18341999

18352000
const deploymentSpec: any = {
@@ -1838,44 +2003,60 @@ function generateSingleDeploymentManifest({
18382003
metadata: {
18392004
namespace,
18402005
name,
2006+
annotations: {
2007+
'cluster-autoscaler.kubernetes.io/safe-to-evict': 'true',
2008+
},
18412009
labels: {
18422010
name,
18432011
lc_uuid: build.uuid,
18442012
deploy_uuid: deploy.uuid,
2013+
dd_name: `lifecycle-${build.uuid}`,
2014+
...datadogLabels,
18452015
},
18462016
},
18472017
spec: {
18482018
replicas: replicaCount,
2019+
revisionHistoryLimit: 5,
18492020
selector: {
18502021
matchLabels: {
18512022
name,
18522023
},
18532024
},
2025+
// Use Recreate strategy for deployments with PVCs (EBS volumes can only attach to one pod)
2026+
// Use RollingUpdate for all other deployments
2027+
strategy: hasPersistentVolumeClaims ? { type: 'Recreate' } : { rollingUpdate: { maxUnavailable: '0%' } },
18542028
template: {
18552029
metadata: {
2030+
annotations: {
2031+
'cluster-autoscaler.kubernetes.io/safe-to-evict': 'true',
2032+
},
18562033
labels: {
18572034
name,
18582035
lc_uuid: build.uuid,
18592036
deploy_uuid: deploy.uuid,
2037+
dd_name: `lifecycle-${build.uuid}`,
2038+
...datadogLabels,
18602039
},
18612040
},
18622041
spec: {
18632042
serviceAccountName,
18642043
affinity,
18652044
...(nodeSelector && { nodeSelector }),
2045+
securityContext: {
2046+
fsGroup: 2000,
2047+
},
2048+
...(initContainers.length > 0 && { initContainers }),
18662049
containers,
2050+
volumes,
18672051
...(build?.isStatic && {
18682052
tolerations: staticEnvTolerations,
18692053
}),
2054+
enableServiceLinks: false,
18702055
},
18712056
},
18722057
},
18732058
};
18742059

1875-
if (volumes.length > 0) {
1876-
deploymentSpec.spec.template.spec.volumes = volumes;
1877-
}
1878-
18792060
return yaml.dump(deploymentSpec, { lineWidth: -1 });
18802061
}
18812062

src/server/services/build.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,8 +1036,10 @@ export default class BuildService extends BaseService {
10361036

10371037
// Use DeploymentManager for all active deploys (both Helm and GitHub types)
10381038
if (activeDeploys.length > 0) {
1039-
// we should ignore Codefresh services here since we dont deploy anything
1040-
const managedDeploys = activeDeploys.filter((d) => d.deployable.type !== DeployTypes.CODEFRESH);
1039+
// we should ignore Codefresh and Configuration services here since we dont deploy anything
1040+
const managedDeploys = activeDeploys.filter(
1041+
(d) => d.deployable.type !== DeployTypes.CODEFRESH && d.deployable.type !== DeployTypes.CONFIGURATION
1042+
);
10411043
const deploymentManager = new DeploymentManager(managedDeploys);
10421044
await deploymentManager.deploy();
10431045
}

0 commit comments

Comments
 (0)