mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-12 15:06:27 +00:00
feat: kustomize image digests (#11153)
Co-authored-by: Michael Kriese <michael.kriese@visualon.de> Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> Co-authored-by: Rhys Arkins <rhys@arkins.net>
This commit is contained in:
parent
cdc083f40f
commit
dc15dfd808
9 changed files with 289 additions and 65 deletions
18
lib/manager/kustomize/__fixtures__/digest.yaml
Normal file
18
lib/manager/kustomize/__fixtures__/digest.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: hasura
|
||||
images:
|
||||
- name: postgres
|
||||
digest: sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
||||
- name: postgres:11
|
||||
digest: sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
||||
# invalid - includes newTag and digest
|
||||
- name: postgres
|
||||
newTag: 11
|
||||
digest: sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
||||
# invalid - not a string
|
||||
- name: postgres
|
||||
digest: 02641143766
|
||||
# invalid - missing prefix
|
||||
- name: postgres
|
||||
digest: b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
10
lib/manager/kustomize/__fixtures__/newName.yaml
Normal file
10
lib/manager/kustomize/__fixtures__/newName.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: hasura
|
||||
images:
|
||||
- name: postgres
|
||||
newName: awesome/postgres:11@sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
||||
- name: postgres
|
||||
newName: awesome/postgres:11
|
||||
- name: postgres
|
||||
newName: awesome/postgres@sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
11
lib/manager/kustomize/__fixtures__/newTag.yaml
Normal file
11
lib/manager/kustomize/__fixtures__/newTag.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: hasura
|
||||
images:
|
||||
- name: postgres
|
||||
newTag: "11"
|
||||
- name: postgres
|
||||
newTag: 11@sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
||||
# invalid - renders as `postgres:sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c`
|
||||
- name: postgres
|
||||
newTag: sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
|
@ -1,17 +0,0 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: hasura
|
||||
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: hasura
|
||||
|
||||
bases:
|
||||
- ../base/
|
||||
|
||||
patchesStrategicMerge:
|
||||
- patches/deployment.yaml
|
||||
|
||||
images:
|
||||
- name: postgres
|
||||
newTag: sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c
|
|
@ -1,5 +1,68 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`manager/kustomize/extract extractPackageFile() extracts from digest 1`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentDigest": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"currentValue": undefined,
|
||||
"datasource": "docker",
|
||||
"depName": "postgres",
|
||||
"replaceString": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"currentValue": "11",
|
||||
"datasource": "docker",
|
||||
"depName": "postgres",
|
||||
"replaceString": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"currentValue": 11,
|
||||
"depName": "postgres",
|
||||
"skipReason": "invalid-dependency-specification",
|
||||
},
|
||||
Object {
|
||||
"currentValue": 2641143766,
|
||||
"depName": "postgres",
|
||||
"skipReason": "invalid-value",
|
||||
},
|
||||
Object {
|
||||
"currentValue": "b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"depName": "postgres",
|
||||
"skipReason": "invalid-value",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`manager/kustomize/extract extractPackageFile() extracts from newTag 1`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentDigest": undefined,
|
||||
"currentValue": "11",
|
||||
"datasource": "docker",
|
||||
"depName": "postgres",
|
||||
"replaceString": "11",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"currentValue": "11",
|
||||
"datasource": "docker",
|
||||
"depName": "postgres",
|
||||
"replaceString": "11@sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
},
|
||||
Object {
|
||||
"currentValue": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"depName": "postgres",
|
||||
"skipReason": "invalid-value",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`manager/kustomize/extract extractPackageFile() extracts http dependency 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
|
@ -32,16 +95,29 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`manager/kustomize/extract extractPackageFile() extracts sha256 instead of tag 1`] = `
|
||||
exports[`manager/kustomize/extract extractPackageFile() extracts newName 1`] = `
|
||||
Object {
|
||||
"deps": Array [
|
||||
Object {
|
||||
"currentDigest": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"currentValue": "11",
|
||||
"datasource": "docker",
|
||||
"depName": "awesome/postgres",
|
||||
"replaceString": "awesome/postgres:11@sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": undefined,
|
||||
"currentValue": "11",
|
||||
"datasource": "docker",
|
||||
"depName": "awesome/postgres",
|
||||
"replaceString": "awesome/postgres:11",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"currentValue": undefined,
|
||||
"datasource": "docker",
|
||||
"depName": "postgres",
|
||||
"replaceString": "sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
"versioning": "docker",
|
||||
"depName": "awesome/postgres",
|
||||
"replaceString": "awesome/postgres@sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -95,7 +171,6 @@ Array [
|
|||
"datasource": "docker",
|
||||
"depName": "node",
|
||||
"replaceString": "v0.1.0",
|
||||
"versioning": "docker",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": undefined,
|
||||
|
@ -103,7 +178,6 @@ Array [
|
|||
"datasource": "docker",
|
||||
"depName": "group/instance",
|
||||
"replaceString": "v0.0.1",
|
||||
"versioning": "docker",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": undefined,
|
||||
|
@ -111,7 +185,6 @@ Array [
|
|||
"datasource": "docker",
|
||||
"depName": "quay.io/test/repo",
|
||||
"replaceString": "v0.0.2",
|
||||
"versioning": "docker",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": undefined,
|
||||
|
@ -119,7 +192,6 @@ Array [
|
|||
"datasource": "docker",
|
||||
"depName": "gitlab.com/org/suborg/image",
|
||||
"replaceString": "v0.0.3",
|
||||
"versioning": "docker",
|
||||
},
|
||||
Object {
|
||||
"currentDigest": undefined,
|
||||
|
@ -127,7 +199,6 @@ Array [
|
|||
"datasource": "docker",
|
||||
"depName": "but.this.lives.on.local/private-registry",
|
||||
"replaceString": "v0.0.4",
|
||||
"versioning": "docker",
|
||||
},
|
||||
Object {
|
||||
"currentValue": 2.5,
|
||||
|
|
|
@ -3,7 +3,6 @@ import * as datasourceDocker from '../../datasource/docker';
|
|||
import * as datasourceGitTags from '../../datasource/git-tags';
|
||||
import * as datasourceGitHubTags from '../../datasource/github-tags';
|
||||
import { SkipReason } from '../../types';
|
||||
import * as dockerVersioning from '../../versioning/docker';
|
||||
import {
|
||||
extractBase,
|
||||
extractImage,
|
||||
|
@ -19,7 +18,9 @@ const kustomizeWithLocal = loadFixture('kustomizeWithLocal.yaml');
|
|||
const nonKustomize = loadFixture('service.yaml');
|
||||
const gitImages = loadFixture('gitImages.yaml');
|
||||
const kustomizeDepsInResources = loadFixture('depsInResources.yaml');
|
||||
const sha = loadFixture('sha.yaml');
|
||||
const newTag = loadFixture('newTag.yaml');
|
||||
const newName = loadFixture('newName.yaml');
|
||||
const digest = loadFixture('digest.yaml');
|
||||
|
||||
describe('manager/kustomize/extract', () => {
|
||||
it('should successfully parse a valid kustomize file', () => {
|
||||
|
@ -131,7 +132,6 @@ describe('manager/kustomize/extract', () => {
|
|||
currentValue: 'v1.0.0',
|
||||
datasource: datasourceDocker.id,
|
||||
replaceString: 'v1.0.0',
|
||||
versioning: dockerVersioning.id,
|
||||
depName: 'node',
|
||||
};
|
||||
const pkg = extractImage({
|
||||
|
@ -146,7 +146,6 @@ describe('manager/kustomize/extract', () => {
|
|||
currentValue: 'v1.0.0',
|
||||
datasource: datasourceDocker.id,
|
||||
replaceString: 'v1.0.0',
|
||||
versioning: dockerVersioning.id,
|
||||
depName: 'test/node',
|
||||
};
|
||||
const pkg = extractImage({
|
||||
|
@ -161,7 +160,6 @@ describe('manager/kustomize/extract', () => {
|
|||
currentValue: 'v1.0.0',
|
||||
datasource: datasourceDocker.id,
|
||||
replaceString: 'v1.0.0',
|
||||
versioning: dockerVersioning.id,
|
||||
depName: 'quay.io/repo/image',
|
||||
};
|
||||
const pkg = extractImage({
|
||||
|
@ -175,7 +173,6 @@ describe('manager/kustomize/extract', () => {
|
|||
currentDigest: undefined,
|
||||
currentValue: 'v1.0.0',
|
||||
datasource: datasourceDocker.id,
|
||||
versioning: dockerVersioning.id,
|
||||
replaceString: 'v1.0.0',
|
||||
depName: 'localhost:5000/repo/image',
|
||||
};
|
||||
|
@ -191,7 +188,6 @@ describe('manager/kustomize/extract', () => {
|
|||
currentValue: 'v1.0.0',
|
||||
replaceString: 'v1.0.0',
|
||||
datasource: datasourceDocker.id,
|
||||
versioning: dockerVersioning.id,
|
||||
depName: 'localhost:5000/repo/image/service',
|
||||
};
|
||||
const pkg = extractImage({
|
||||
|
@ -253,13 +249,76 @@ describe('manager/kustomize/extract', () => {
|
|||
expect(res.deps[1].depName).toEqual('fluxcd/flux');
|
||||
expect(res.deps[2].depName).toEqual('fluxcd/flux');
|
||||
});
|
||||
it('extracts sha256 instead of tag', () => {
|
||||
expect(extractPackageFile(sha)).toMatchSnapshot({
|
||||
|
||||
const postgresDigest =
|
||||
'sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c';
|
||||
|
||||
it('extracts from newTag', () => {
|
||||
expect(extractPackageFile(newTag)).toMatchSnapshot({
|
||||
deps: [
|
||||
{
|
||||
currentDigest:
|
||||
'sha256:b0cfe264cb1143c7c660ddfd5c482464997d62d6bc9f97f8fdf3deefce881a8c',
|
||||
currentDigest: undefined,
|
||||
currentValue: '11',
|
||||
replaceString: '11',
|
||||
},
|
||||
{
|
||||
currentDigest: postgresDigest,
|
||||
currentValue: '11',
|
||||
replaceString: `11@${postgresDigest}`,
|
||||
},
|
||||
{
|
||||
skipReason: SkipReason.InvalidValue,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('extracts from digest', () => {
|
||||
expect(extractPackageFile(digest)).toMatchSnapshot({
|
||||
deps: [
|
||||
{
|
||||
currentDigest: postgresDigest,
|
||||
currentValue: undefined,
|
||||
replaceString: postgresDigest,
|
||||
},
|
||||
{
|
||||
currentDigest: postgresDigest,
|
||||
currentValue: '11',
|
||||
replaceString: postgresDigest,
|
||||
},
|
||||
{
|
||||
skipReason: SkipReason.InvalidDependencySpecification,
|
||||
},
|
||||
{
|
||||
skipReason: SkipReason.InvalidValue,
|
||||
},
|
||||
{
|
||||
skipReason: SkipReason.InvalidValue,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('extracts newName', () => {
|
||||
expect(extractPackageFile(newName)).toMatchSnapshot({
|
||||
deps: [
|
||||
{
|
||||
depName: 'awesome/postgres',
|
||||
currentDigest: postgresDigest,
|
||||
currentValue: '11',
|
||||
replaceString: `awesome/postgres:11@${postgresDigest}`,
|
||||
},
|
||||
{
|
||||
depName: 'awesome/postgres',
|
||||
currentDigest: undefined,
|
||||
currentValue: '11',
|
||||
replaceString: 'awesome/postgres:11',
|
||||
},
|
||||
{
|
||||
depName: 'awesome/postgres',
|
||||
currentDigest: postgresDigest,
|
||||
currentValue: undefined,
|
||||
replaceString: `awesome/postgres@${postgresDigest}`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as datasourceGitTags from '../../datasource/git-tags';
|
|||
import * as datasourceGitHubTags from '../../datasource/github-tags';
|
||||
import { logger } from '../../logger';
|
||||
import { SkipReason } from '../../types';
|
||||
import * as dockerVersioning from '../../versioning/docker';
|
||||
import { splitImageParts } from '../dockerfile/extract';
|
||||
import type { PackageDependency, PackageFile } from '../types';
|
||||
import type { Image, Kustomize } from './types';
|
||||
|
||||
|
@ -21,7 +21,8 @@ export function extractBase(base: string): PackageDependency | null {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (match?.groups.path.startsWith('github.com')) {
|
||||
const { path } = match.groups;
|
||||
if (path.startsWith('github.com:') || path.startsWith('github.com/')) {
|
||||
return {
|
||||
currentValue: match.groups.currentValue,
|
||||
datasource: datasourceGitHubTags.id,
|
||||
|
@ -31,37 +32,72 @@ export function extractBase(base: string): PackageDependency | null {
|
|||
|
||||
return {
|
||||
datasource: datasourceGitTags.id,
|
||||
depName: match.groups.path.replace('.git', ''),
|
||||
depName: path.replace('.git', ''),
|
||||
lookupName: match.groups.url,
|
||||
currentValue: match.groups.currentValue,
|
||||
};
|
||||
}
|
||||
|
||||
export function extractImage(image: Image): PackageDependency | null {
|
||||
if (image?.name && image.newTag) {
|
||||
const replaceString = image.newTag;
|
||||
let currentValue: string | undefined;
|
||||
let currentDigest: string | undefined;
|
||||
if (!is.string(replaceString)) {
|
||||
if (!image.name) {
|
||||
return null;
|
||||
}
|
||||
const nameDep = splitImageParts(image.newName ?? image.name);
|
||||
const { depName } = nameDep;
|
||||
const { digest, newTag } = image;
|
||||
if (digest && newTag) {
|
||||
logger.warn(
|
||||
{ newTag, digest },
|
||||
'Kustomize ignores newTag when digest is provided. Pick one, or use `newTag: tag@digest`'
|
||||
);
|
||||
return {
|
||||
depName,
|
||||
currentValue: newTag,
|
||||
currentDigest: digest,
|
||||
skipReason: SkipReason.InvalidDependencySpecification,
|
||||
};
|
||||
}
|
||||
|
||||
if (digest) {
|
||||
if (!is.string(digest) || !digest.startsWith('sha256:')) {
|
||||
return {
|
||||
depName: image.newName ?? image.name,
|
||||
currentValue: replaceString,
|
||||
depName,
|
||||
currentValue: digest,
|
||||
skipReason: SkipReason.InvalidValue,
|
||||
};
|
||||
}
|
||||
if (replaceString.startsWith('sha256:')) {
|
||||
currentDigest = replaceString;
|
||||
currentValue = undefined;
|
||||
} else {
|
||||
currentValue = replaceString;
|
||||
}
|
||||
|
||||
return {
|
||||
datasource: datasourceDocker.id,
|
||||
versioning: dockerVersioning.id,
|
||||
depName: image.newName ?? image.name,
|
||||
currentValue,
|
||||
currentDigest,
|
||||
replaceString,
|
||||
depName,
|
||||
currentValue: nameDep.currentValue,
|
||||
currentDigest: digest,
|
||||
replaceString: digest,
|
||||
};
|
||||
}
|
||||
|
||||
if (newTag) {
|
||||
if (!is.string(newTag) || newTag.startsWith('sha256:')) {
|
||||
return {
|
||||
depName,
|
||||
currentValue: newTag,
|
||||
skipReason: SkipReason.InvalidValue,
|
||||
};
|
||||
}
|
||||
|
||||
const dep = splitImageParts(`${depName}:${newTag}`);
|
||||
return {
|
||||
...dep,
|
||||
datasource: datasourceDocker.id,
|
||||
replaceString: newTag,
|
||||
};
|
||||
}
|
||||
|
||||
if (image.newName) {
|
||||
return {
|
||||
...nameDep,
|
||||
datasource: datasourceDocker.id,
|
||||
replaceString: image.newName,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -15,16 +15,51 @@ This package will manage two parts of the `kustomization.yaml` file:
|
|||
|
||||
- Needs to have `kind: Kustomization` defined
|
||||
- Currently this hasn't been tested using HTTPS to fetch the repos
|
||||
- The image tags are limited to the following formats:
|
||||
- The keys for the image tags can be in any order
|
||||
|
||||
```
|
||||
```yaml
|
||||
- name: image/name
|
||||
newTag: v0.0.1
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
# or
|
||||
- newTag: v0.0.1
|
||||
name: image/name
|
||||
```
|
||||
|
||||
- Digests can be pinned in `newTag` or `digest`:
|
||||
|
||||
```yaml
|
||||
- name: image/name
|
||||
newTag: v0.0.1@sha256:3eeba3e2caa30d2aba0fd78a34c1bbeebaa1b96c7aa3c95ec9bac44163c5ca4f
|
||||
# without a version, digests are tracked as :latest
|
||||
- name: image/name
|
||||
digest: sha256:3eeba3e2caa30d2aba0fd78a34c1bbeebaa1b96c7aa3c95ec9bac44163c5ca4f
|
||||
```
|
||||
|
||||
- The image's repository can be changed with `newName`:
|
||||
|
||||
```yaml
|
||||
- name: image/name
|
||||
newName: custom-image/name:v0.0.1
|
||||
- name: image/name
|
||||
newName: custom-image/name:v0.0.1@sha256:3eeba3e2caa30d2aba0fd78a34c1bbeebaa1b96c7aa3c95ec9bac44163c5ca4f
|
||||
- name: image/name
|
||||
newName: custom-image/name@sha256:3eeba3e2caa30d2aba0fd78a34c1bbeebaa1b96c7aa3c95ec9bac44163c5ca4f
|
||||
- name: image/name
|
||||
newName: custom-image/name
|
||||
newTag: v0.0.1@sha256:3eeba3e2caa30d2aba0fd78a34c1bbeebaa1b96c7aa3c95ec9bac44163c5ca4f
|
||||
- name: image/name
|
||||
newName: custom-image/name
|
||||
digest: sha256:3eeba3e2caa30d2aba0fd78a34c1bbeebaa1b96c7aa3c95ec9bac44163c5ca4f
|
||||
```
|
||||
|
||||
- Images with values ignored by Kustomize will be skipped to avoid ambiguity:
|
||||
|
||||
```yaml
|
||||
# bad: skipped because newTag: is ignored when digest: is set
|
||||
- name: image/name
|
||||
newTag: v0.0.1
|
||||
digest: sha256:3eeba3e2caa30d2aba0fd78a34c1bbeebaa1b96c7aa3c95ec9bac44163c5ca4f
|
||||
# good:
|
||||
- name: image/name
|
||||
newTag: v0.0.1@sha256:3eeba3e2caa30d2aba0fd78a34c1bbeebaa1b96c7aa3c95ec9bac44163c5ca4f
|
||||
```
|
||||
|
|
|
@ -2,6 +2,7 @@ export interface Image {
|
|||
name: string;
|
||||
newTag: string;
|
||||
newName?: string;
|
||||
digest?: string;
|
||||
}
|
||||
export interface Kustomize {
|
||||
kind: string;
|
||||
|
|
Loading…
Reference in a new issue