Skip to content

Commit

Permalink
refactor(managers): yaml schema checks (#26811)
Browse files Browse the repository at this point in the history
  • Loading branch information
secustor committed Feb 25, 2024
1 parent 4084856 commit 14cba69
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 280 deletions.
34 changes: 12 additions & 22 deletions lib/modules/manager/argocd/extract.ts
Expand Up @@ -11,11 +11,11 @@ import type {
PackageDependency,
PackageFileContent,
} from '../types';
import type {
import {
ApplicationDefinition,
ApplicationSource,
ApplicationSpec,
} from './types';
type ApplicationSource,
type ApplicationSpec,
} from './schema';
import { fileTestRegex } from './util';

export function extractPackageFile(
Expand All @@ -33,27 +33,21 @@ export function extractPackageFile(

let definitions: ApplicationDefinition[];
try {
// TODO: use schema (#9610)
definitions = parseYaml(content);
definitions = parseYaml(content, null, {
customSchema: ApplicationDefinition,
failureBehaviour: 'filter',
});
} catch (err) {
logger.debug({ err, packageFile }, 'Failed to parse ArgoCD definition.');
return null;
}

const deps = definitions.filter(is.plainObject).flatMap(processAppSpec);
const deps = definitions.flatMap(processAppSpec);

return deps.length ? { deps } : null;
}

function processSource(source: ApplicationSource): PackageDependency | null {
if (
!source ||
!is.nonEmptyString(source.repoURL) ||
!is.nonEmptyString(source.targetRevision)
) {
return null;
}

// a chart variable is defined this is helm declaration
if (source.chart) {
// assume OCI helm chart if repoURL doesn't contain explicit protocol
Expand Down Expand Up @@ -89,14 +83,10 @@ function processSource(source: ApplicationSource): PackageDependency | null {
function processAppSpec(
definition: ApplicationDefinition,
): PackageDependency[] {
const spec: ApplicationSpec | null | undefined =
const spec: ApplicationSpec =
definition.kind === 'Application'
? definition?.spec
: definition?.spec?.template?.spec;

if (is.nullOrUndefined(spec)) {
return [];
}
? definition.spec
: definition.spec.template.spec;

const deps: (PackageDependency | null)[] = [];

Expand Down
35 changes: 35 additions & 0 deletions lib/modules/manager/argocd/schema.ts
@@ -0,0 +1,35 @@
import { z } from 'zod';

export const KubernetesResource = z.object({
apiVersion: z.string(),
});

export const ApplicationSource = z.object({
chart: z.string().optional(),
repoURL: z.string(),
targetRevision: z.string(),
});
export type ApplicationSource = z.infer<typeof ApplicationSource>;

export const ApplicationSpec = z.object({
source: ApplicationSource.optional(),
sources: z.array(ApplicationSource).optional(),
});
export type ApplicationSpec = z.infer<typeof ApplicationSpec>;

export const Application = KubernetesResource.extend({
kind: z.literal('Application'),
spec: ApplicationSpec,
});

export const ApplicationSet = KubernetesResource.extend({
kind: z.literal('ApplicationSet'),
spec: z.object({
template: z.object({
spec: ApplicationSpec,
}),
}),
});

export const ApplicationDefinition = Application.or(ApplicationSet);
export type ApplicationDefinition = z.infer<typeof ApplicationDefinition>;
30 changes: 0 additions & 30 deletions lib/modules/manager/argocd/types.ts

This file was deleted.

20 changes: 7 additions & 13 deletions lib/modules/manager/crossplane/extract.ts
Expand Up @@ -6,7 +6,7 @@ import type {
PackageDependency,
PackageFileContent,
} from '../types';
import { XPKGSchema } from './schema';
import { type XPKG, XPKGSchema } from './schema';

export function extractPackageFile(
content: string,
Expand All @@ -19,9 +19,12 @@ export function extractPackageFile(
return null;
}

let list = [];
let list: XPKG[] = [];
try {
list = parseYaml(content);
list = parseYaml(content, null, {
customSchema: XPKGSchema,
failureBehaviour: 'filter',
});
} catch (err) {
logger.debug(
{ err, packageFile },
Expand All @@ -31,16 +34,7 @@ export function extractPackageFile(
}

const deps: PackageDependency[] = [];
for (const item of list) {
const parsed = XPKGSchema.safeParse(item);
if (!parsed.success) {
logger.trace(
{ item, errors: parsed.error },
'Invalid Crossplane package',
);
continue;
}
const xpkg = parsed.data;
for (const xpkg of list) {
const dep = getDep(xpkg.spec.package, true, extractConfig?.registryAliases);
dep.depType = xpkg.kind.toLowerCase();
deps.push(dep);
Expand Down
24 changes: 6 additions & 18 deletions lib/modules/manager/docker-compose/extract.ts
Expand Up @@ -4,7 +4,7 @@ import { newlineRegex, regEx } from '../../../util/regex';
import { parseSingleYaml } from '../../../util/yaml';
import { getDep } from '../dockerfile/extract';
import type { ExtractConfig, PackageFileContent } from '../types';
import type { DockerComposeConfig } from './types';
import { DockerComposeFile } from './schema';

class LineMapper {
private imageLines: { line: string; lineNumber: number; used: boolean }[];
Expand Down Expand Up @@ -34,24 +34,12 @@ export function extractPackageFile(
extractConfig: ExtractConfig,
): PackageFileContent | null {
logger.debug(`docker-compose.extractPackageFile(${packageFile})`);
let config: DockerComposeConfig;
let config: DockerComposeFile;
try {
// TODO: use schema (#9610)
config = parseSingleYaml(content, { json: true });
if (!config) {
logger.debug(
{ packageFile },
'Null config when parsing Docker Compose content',
);
return null;
}
if (typeof config !== 'object') {
logger.debug(
{ packageFile, type: typeof config },
'Unexpected type for Docker Compose content',
);
return null;
}
config = parseSingleYaml(content, {
json: true,
customSchema: DockerComposeFile,
});
} catch (err) {
logger.debug(
{ err, packageFile },
Expand Down
23 changes: 23 additions & 0 deletions lib/modules/manager/docker-compose/schema.ts
@@ -0,0 +1,23 @@
import { z } from 'zod';

const DockerComposeService = z.object({
image: z.string().optional(),
build: z
.object({
context: z.string().optional(),
dockerfile: z.string().optional(),
})
.optional(),
});

const DockerComposeFileV1 = z.record(DockerComposeService);
const DockerComposeFileModern = z.object({
// compose does not use this strictly, so we shouldn't be either
// https://docs.docker.com/compose/compose-file/04-version-and-name/#version-top-level-element
version: z.string().optional(),
services: z.record(DockerComposeService),
});

export const DockerComposeFile =
DockerComposeFileModern.or(DockerComposeFileV1);
export type DockerComposeFile = z.infer<typeof DockerComposeFile>;
12 changes: 0 additions & 12 deletions lib/modules/manager/docker-compose/types.ts

This file was deleted.

20 changes: 9 additions & 11 deletions lib/modules/manager/fleet/extract.ts
Expand Up @@ -6,7 +6,7 @@ import { GitTagsDatasource } from '../../datasource/git-tags';
import { HelmDatasource } from '../../datasource/helm';
import { checkIfStringIsPath } from '../terraform/util';
import type { PackageDependency, PackageFileContent } from '../types';
import type { FleetFile, FleetHelmBlock, GitRepo } from './types';
import { FleetFile, type FleetHelmBlock, GitRepo } from './schema';

function extractGitRepo(doc: GitRepo): PackageDependency {
const dep: PackageDependency = {
Expand Down Expand Up @@ -119,23 +119,21 @@ export function extractPackageFile(

try {
if (regEx('fleet.ya?ml').test(packageFile)) {
// TODO: use schema (#9610)
const docs = parseYaml<FleetFile>(content, null, {
const docs = parseYaml(content, null, {
json: true,
customSchema: FleetFile,
failureBehaviour: 'filter',
});
const fleetDeps = docs
.filter((doc) => is.truthy(doc?.helm))
.flatMap((doc) => extractFleetFile(doc));
const fleetDeps = docs.flatMap(extractFleetFile);

deps.push(...fleetDeps);
} else {
// TODO: use schema (#9610)
const docs = parseYaml<GitRepo>(content, null, {
const docs = parseYaml(content, null, {
json: true,
customSchema: GitRepo,
failureBehaviour: 'filter',
});
const gitRepoDeps = docs
.filter((doc) => doc.kind === 'GitRepo') // ensure only GitRepo manifests are processed
.flatMap((doc) => extractGitRepo(doc));
const gitRepoDeps = docs.flatMap(extractGitRepo);
deps.push(...gitRepoDeps);
}
} catch (err) {
Expand Down
45 changes: 45 additions & 0 deletions lib/modules/manager/fleet/schema.ts
@@ -0,0 +1,45 @@
import { z } from 'zod';

const FleetHelmBlock = z.object({
chart: z.string().optional(),
repo: z.string().optional(),
version: z.string().optional(),
});
export type FleetHelmBlock = z.infer<typeof FleetHelmBlock>;

const FleetFileHelm = FleetHelmBlock.extend({
releaseName: z.string(),
});

/**
Represent a GitRepo Kubernetes manifest of Fleet.
@link https://fleet.rancher.io/gitrepo-add/#create-gitrepo-instance
*/
export const GitRepo = z.object({
metadata: z.object({
name: z.string(),
}),
kind: z.string(),
spec: z.object({
repo: z.string().optional(),
revision: z.string().optional(),
}),
});
export type GitRepo = z.infer<typeof GitRepo>;

/**
Represent a Bundle configuration of Fleet, which is located in `fleet.yaml` files.
@link https://fleet.rancher.io/gitrepo-structure/#fleetyaml
*/
export const FleetFile = z.object({
helm: FleetFileHelm,
targetCustomizations: z
.array(
z.object({
name: z.string(),
helm: FleetHelmBlock.partial().optional(),
}),
)
.optional(),
});
export type FleetFile = z.infer<typeof FleetFile>;
36 changes: 0 additions & 36 deletions lib/modules/manager/fleet/types.ts

This file was deleted.

0 comments on commit 14cba69

Please sign in to comment.