From a86885a81b4eb406544f5fbf4e6ffbb8ffee0969 Mon Sep 17 00:00:00 2001 From: Jamie Magee <jamie.magee@gmail.com> Date: Thu, 6 Oct 2022 09:13:22 -0700 Subject: [PATCH] feat(datasource/kubernetes-api): add kubernetes-api datasource (#17420) --- data/kubernetes-api.json5 | 113 ++++++++++++++++++ lib/modules/datasource/api.ts | 2 + .../datasource/kubernetes-api/index.spec.ts | 35 ++++++ .../datasource/kubernetes-api/index.ts | 31 +++++ .../datasource/kubernetes-api/readme.md | 4 + .../manager/kubernetes/extract.spec.ts | 8 ++ lib/modules/manager/kubernetes/extract.ts | 4 + lib/modules/manager/kubernetes/index.ts | 6 +- 8 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 data/kubernetes-api.json5 create mode 100644 lib/modules/datasource/kubernetes-api/index.spec.ts create mode 100644 lib/modules/datasource/kubernetes-api/index.ts create mode 100644 lib/modules/datasource/kubernetes-api/readme.md diff --git a/data/kubernetes-api.json5 b/data/kubernetes-api.json5 new file mode 100644 index 0000000000..027ecda7c3 --- /dev/null +++ b/data/kubernetes-api.json5 @@ -0,0 +1,113 @@ +{ + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-16 + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#networkpolicy-v116 + NetworkPolicy: ['extensions/v1beta1', 'networking.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#daemonset-v116 + DaemonSet: ['extensions/v1beta1', 'apps/v1beta2', 'apps/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#deployment-v116 + Deployment: ['extensions/v1beta1', 'apps/v1beta1', 'apps/v1beta2', 'apps/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#statefulset-v116 + StatefulSet: ['apps/v1beta1', 'apps/v1beta2', 'apps/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#replicaset-v116 + ReplicaSet: ['extensions/v1beta1', 'apps/v1beta1', 'apps/v1beta2', 'apps/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#psp-v116 + PodSecurityPolicy: ['extensions/v1beta1', 'policy/v1beta1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22 + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#webhook-resources-v122 + MutatingWebhookConfiguration: [ + 'admissionregistration.k8s.io/v1beta1', + 'admissionregistration.k8s.io/v1', + ], + ValidatingWebhookConfiguration: [ + 'admissionregistration.k8s.io/v1beta1', + 'admissionregistration.k8s.io/v1', + ], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#customresourcedefinition-v122 + CustomResourceDefinition: [ + 'apiextensions.k8s.io/v1beta1', + 'apiextensions.k8s.io/v1', + ], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#apiservice-v122 + APIService: ['apiregistration.k8s.io/v1beta1', 'apiregistration.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#tokenreview-v122 + TokenReview: ['authentication.k8s.io/v1beta1', 'authentication.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#subjectaccessreview-resources-v122 + LocalSubjectAccessReview: [ + 'authorization.k8s.io/v1beta1', + 'authorization.k8s.io/v1', + ], + SelfSubjectAccessReview: [ + 'authorization.k8s.io/v1beta1', + 'authorization.k8s.io/v1', + ], + SubjectAccessReview: [ + 'authorization.k8s.io/v1beta1', + 'authorization.k8s.io/v1', + ], + SelfSubjectRulesReview: [ + 'authorization.k8s.io/v1beta1', + 'authorization.k8s.io/v1', + ], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#certificatesigningrequest-v122 + CertificateSigningRequest: [ + 'certificates.k8s.io/v1beta1', + 'certificates.k8s.io/v1', + ], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#lease-v122 + Lease: ['coordination.k8s.io/v1beta1', 'coordination.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#ingress-v122 + Ingress: [ + 'extensions/v1beta1', + 'networking.k8s.io/v1beta1', + 'networking.k8s.io/v1', + ], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#ingressclass-v122 + IngressClass: ['networking.k8s.io/v1beta1', 'networking.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#rbac-resources-v122 + ClusterRole: [ + 'rbac.authorization.k8s.io/v1beta1', + 'rbac.authorization.k8s.io/v1', + ], + ClusterRoleBinding: [ + 'rbac.authorization.k8s.io/v1beta1', + 'rbac.authorization.k8s.io/v1', + ], + Role: ['rbac.authorization.k8s.io/v1beta1', 'rbac.authorization.k8s.io/v1'], + RoleBinding: [ + 'rbac.authorization.k8s.io/v1beta1', + 'rbac.authorization.k8s.io/v1', + ], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#priorityclass-v122 + PriorityClass: ['scheduling.k8s.io/v1beta1', 'scheduling.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#storage-resources-v122 + CSIDriver: ['storage.k8s.io/v1beta1', 'storage.k8s.io/v1'], + CSINode: ['storage.k8s.io/v1beta1', 'storage.k8s.io/v1'], + StorageClass: ['storage.k8s.io/v1beta1', 'storage.k8s.io/v1'], + VolumeAttachment: ['storage.k8s.io/v1beta1', 'storage.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25 + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#cronjob-v125 + CronJob: ['batch/v1beta1', 'batch/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#endpointslice-v125 + EndpointSlice: ['discovery.k8s.io/v1beta1', 'discovery.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#event-v125 + Event: ['events.k8s.io/v1beta1', 'events.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#horizontalpodautoscaler-v125 + HorizontalPodAutoscaler: ['autoscaling/v2beta1', 'autoscaling/v2'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#poddisruptionbudget-v125 + PodDisruptionBudget: ['policy/v1beta1', 'policy/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#runtimeclass-v125 + RuntimeClass: ['node.k8s.io/v1beta1', 'node.k8s.io/v1'], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-26 + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#flowcontrol-resources-v126 + FlowSchema: [ + 'flowcontrol.apiserver.k8s.io/v1beta1', + 'flowcontrol.apiserver.k8s.io/v1beta2', + ], + PriorityLevelConfiguration: [ + 'flowcontrol.apiserver.k8s.io/v1beta1', + 'flowcontrol.apiserver.k8s.io/v1beta2', + ], + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-27 + // https://kubernetes.io/docs/reference/using-api/deprecation-guide/#csistoragecapacity-v127 + CSIStorageCapacity: ['storage.k8s.io/v1beta1', 'storage.k8s.io/v1'], +} diff --git a/lib/modules/datasource/api.ts b/lib/modules/datasource/api.ts index 130107de4c..9056d2fd62 100644 --- a/lib/modules/datasource/api.ts +++ b/lib/modules/datasource/api.ts @@ -29,6 +29,7 @@ import { HelmDatasource } from './helm'; import { HermitDatasource } from './hermit'; import { HexDatasource } from './hex'; import { JenkinsPluginsDatasource } from './jenkins-plugins'; +import { KubernetesApiDatasource } from './kubernetes-api'; import { MavenDatasource } from './maven'; import { NodeDatasource } from './node'; import { NpmDatasource } from './npm'; @@ -81,6 +82,7 @@ api.set(HelmDatasource.id, new HelmDatasource()); api.set(HermitDatasource.id, new HermitDatasource()); api.set(HexDatasource.id, new HexDatasource()); api.set(JenkinsPluginsDatasource.id, new JenkinsPluginsDatasource()); +api.set(KubernetesApiDatasource.id, new KubernetesApiDatasource()); api.set(MavenDatasource.id, new MavenDatasource()); api.set(NodeDatasource.id, new NodeDatasource()); api.set(NpmDatasource.id, new NpmDatasource()); diff --git a/lib/modules/datasource/kubernetes-api/index.spec.ts b/lib/modules/datasource/kubernetes-api/index.spec.ts new file mode 100644 index 0000000000..73542d86e2 --- /dev/null +++ b/lib/modules/datasource/kubernetes-api/index.spec.ts @@ -0,0 +1,35 @@ +import { getPkgReleases } from '../index'; +import { KubernetesApiDatasource } from '.'; + +const datasource = KubernetesApiDatasource.id; + +describe('modules/datasource/kubernetes-api/index', () => { + describe('getReleases', () => { + it('returns null for an unknown Kubernetes API type', async () => { + const res = await getPkgReleases({ datasource, depName: 'Unknown' }); + expect(res).toBeNull(); + }); + + it('returns for a known Kubernetes API type', async () => { + const res = await getPkgReleases({ + datasource, + depName: 'CSIStorageCapacity', + }); + expect(res).not.toBeNull(); + expect(res).toStrictEqual({ + releases: [ + { version: 'storage.k8s.io/v1beta1' }, + { version: 'storage.k8s.io/v1' }, + ], + }); + }); + + it('is case sensitive', async () => { + const res = await getPkgReleases({ + datasource, + depName: 'csistoragecapacity', + }); + expect(res).toBeNull(); + }); + }); +}); diff --git a/lib/modules/datasource/kubernetes-api/index.ts b/lib/modules/datasource/kubernetes-api/index.ts new file mode 100644 index 0000000000..044a5b88bf --- /dev/null +++ b/lib/modules/datasource/kubernetes-api/index.ts @@ -0,0 +1,31 @@ +import JSON5 from 'json5'; +import dataFiles from '../../../data-files.generated'; +import * as kubernetesApiVersioning from '../../versioning/kubernetes-api'; +import { Datasource } from '../datasource'; +import type { GetReleasesConfig, ReleaseResult } from '../types'; + +export class KubernetesApiDatasource extends Datasource { + static readonly id = 'kubernetes-api'; + private readonly kubernetesApiVersions: Record<string, string[]>; + + constructor() { + super(KubernetesApiDatasource.id); + this.kubernetesApiVersions = JSON5.parse( + dataFiles.get('data/kubernetes-api.json5')! + ); + } + + override defaultVersioning = kubernetesApiVersioning.id; + + getReleases({ + packageName, + }: GetReleasesConfig): Promise<ReleaseResult | null> { + const versions = this.kubernetesApiVersions[packageName]; + if (versions) { + const releases = versions.map((version) => ({ version })); + return Promise.resolve({ releases }); + } + + return Promise.resolve(null); + } +} diff --git a/lib/modules/datasource/kubernetes-api/readme.md b/lib/modules/datasource/kubernetes-api/readme.md new file mode 100644 index 0000000000..f9315b7e43 --- /dev/null +++ b/lib/modules/datasource/kubernetes-api/readme.md @@ -0,0 +1,4 @@ +Kubernetes API upgrade versions are manually transcribed from the [Kubernetes API deprecation guide][1]. +The Kubernetes API deprecation guide is updated regularly, so this list may be out of date. + +[1]: https://kubernetes.io/docs/reference/using-api/deprecation-guide/ diff --git a/lib/modules/manager/kubernetes/extract.spec.ts b/lib/modules/manager/kubernetes/extract.spec.ts index 897e8dab55..21b63f4bd3 100644 --- a/lib/modules/manager/kubernetes/extract.spec.ts +++ b/lib/modules/manager/kubernetes/extract.spec.ts @@ -18,7 +18,9 @@ describe('modules/manager/kubernetes/extract', () => { expect(res?.deps).toStrictEqual([ { currentValue: 'v1', + datasource: 'kubernetes-api', depName: 'ConfigMap', + versioning: 'kubernetes-api', }, ]); }); @@ -46,11 +48,15 @@ describe('modules/manager/kubernetes/extract', () => { }, { currentValue: 'apps/v1', + datasource: 'kubernetes-api', depName: 'Deployment', + versioning: 'kubernetes-api', }, { currentValue: 'extensions/v1beta1', + datasource: 'kubernetes-api', depName: 'DaemonSet', + versioning: 'kubernetes-api', }, ]); }); @@ -74,7 +80,9 @@ describe('modules/manager/kubernetes/extract', () => { }, { currentValue: 'apps/v1', + datasource: 'kubernetes-api', depName: 'DaemonSet', + versioning: 'kubernetes-api', }, ]); }); diff --git a/lib/modules/manager/kubernetes/extract.ts b/lib/modules/manager/kubernetes/extract.ts index 4c98462e26..e5813ea73c 100644 --- a/lib/modules/manager/kubernetes/extract.ts +++ b/lib/modules/manager/kubernetes/extract.ts @@ -2,6 +2,8 @@ import is from '@sindresorhus/is'; import { loadAll } from 'js-yaml'; import { logger } from '../../../logger'; import { newlineRegex, regEx } from '../../../util/regex'; +import { KubernetesApiDatasource } from '../../datasource/kubernetes-api'; +import * as kubernetesApiVersioning from '../../versioning/kubernetes-api'; import { getDep } from '../dockerfile/extract'; import type { ExtractConfig, PackageDependency, PackageFile } from '../types'; import type { KubernetesConfiguration } from './types'; @@ -74,5 +76,7 @@ function extractApis(content: string, fileName: string): PackageDependency[] { .map((configuration) => ({ depName: configuration.kind, currentValue: configuration.apiVersion, + datasource: KubernetesApiDatasource.id, + versioning: kubernetesApiVersioning.id, })); } diff --git a/lib/modules/manager/kubernetes/index.ts b/lib/modules/manager/kubernetes/index.ts index 7151ce782c..9e0bb7442e 100644 --- a/lib/modules/manager/kubernetes/index.ts +++ b/lib/modules/manager/kubernetes/index.ts @@ -1,5 +1,6 @@ import { ProgrammingLanguage } from '../../../constants'; import { DockerDatasource } from '../../datasource/docker'; +import { KubernetesApiDatasource } from '../../datasource/kubernetes-api'; export { extractPackageFile } from './extract'; @@ -9,4 +10,7 @@ export const defaultConfig = { fileMatch: [], }; -export const supportedDatasources = [DockerDatasource.id]; +export const supportedDatasources = [ + DockerDatasource.id, + KubernetesApiDatasource.id, +]; -- GitLab