Files
Johannes Feichtner b438e57bfc feat(manager/kubernetes): extract image volume references from manifests (#42038)
* feat(manager/kubernetes): extract image volume references from manifests

* fix coverage

* review comments

* simplify
2026-03-28 18:26:19 +00:00

112 lines
3.4 KiB
TypeScript

import { logger } from '../../../logger/index.ts';
import { newlineRegex, regEx } from '../../../util/regex.ts';
import { withDebugMessage } from '../../../util/schema-utils/index.ts';
import {
KubernetesApiDatasource,
supportedApis,
} from '../../datasource/kubernetes-api/index.ts';
import * as kubernetesApiVersioning from '../../versioning/kubernetes-api/index.ts';
import { getDep } from '../dockerfile/extract.ts';
import type {
ExtractConfig,
PackageDependency,
PackageFileContent,
} from '../types.ts';
import { type KubernetesManifest, KubernetesManifests } from './schema.ts';
export function extractPackageFile(
content: string,
packageFile: string,
config: ExtractConfig,
): PackageFileContent | null {
logger.trace('kubernetes.extractPackageFile()');
const isKubernetesManifest =
regEx(/\s*apiVersion\s*:/).test(content) &&
regEx(/\s*kind\s*:/).test(content);
if (!isKubernetesManifest) {
return null;
}
const manifests = KubernetesManifests.catch(
withDebugMessage([], `${packageFile} does not match Kubernetes schema`),
).parse(content);
const deps: PackageDependency[] = [
...extractImages(content, config),
...extractImageVolumes(manifests, config),
...extractApis(manifests),
];
return deps.length ? { deps } : null;
}
// Comes from https://github.com/distribution/reference/blob/v0.6.0/regexp.go
// Extracted & converted with https://go.dev/play/p/KQQAONGp__2
const dockerImageRegexPattern = `((?:(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])(?:\\.(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))*|\\[(?:[a-fA-F0-9:]+)\\])(?::[0-9]+)?/)?[a-z0-9]+(?:(?:[._]|__|[-]+)[a-z0-9]+)*(?:/[a-z0-9]+(?:(?:[._]|__|[-]+)[a-z0-9]+)*)*)(?::([A-Za-z0-9_][A-Za-z0-9_.-]{0,127}))?(?:@([A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][0-9a-fA-F]{32,}))?`;
const k8sImageRegex = regEx(
`^\\s*-?\\s*image:\\s*['"]?(${dockerImageRegexPattern})['"]?\\s*`,
);
function extractImages(
content: string,
config: ExtractConfig,
): PackageDependency[] {
const deps: PackageDependency[] = [];
for (const line of content.split(newlineRegex)) {
const match = k8sImageRegex.exec(line);
if (match) {
const currentFrom = match[1];
const dep = getDep(currentFrom, true, config.registryAliases);
logger.debug(
{
depName: dep.depName,
currentValue: dep.currentValue,
currentDigest: dep.currentDigest,
},
'Kubernetes image',
);
deps.push(dep);
}
}
return deps;
}
function extractImageVolumes(
manifests: KubernetesManifest[],
config: ExtractConfig,
): PackageDependency[] {
const deps: PackageDependency[] = [];
for (const manifest of manifests) {
for (const currentFrom of manifest.imageVolumeReferences) {
const dep = getDep(currentFrom, true, config.registryAliases);
logger.debug(
{
depName: dep.depName,
currentValue: dep.currentValue,
currentDigest: dep.currentDigest,
},
'Kubernetes image volume',
);
deps.push(dep);
}
}
return deps;
}
function extractApis(manifests: KubernetesManifest[]): PackageDependency[] {
return manifests
.filter((m) => supportedApis.has(m.kind))
.map((configuration) => ({
depName: configuration.kind,
currentValue: configuration.apiVersion,
datasource: KubernetesApiDatasource.id,
versioning: kubernetesApiVersioning.id,
}));
}