Skip to content
Snippets Groups Projects
Unverified Commit a4205761 authored by Johannes Feichtner's avatar Johannes Feichtner Committed by GitHub
Browse files

feat(terraform): kubernetes image resources support (#16029)


* add tests

* add support for kubernetes image resources

* Update lib/modules/manager/terraform/extract.ts

Co-authored-by: default avatarSebastian Poxhofer <secustor@users.noreply.github.com>

* added unknown resource

* added Kubernetes to Readme

* missed one spot in the Readme

* Update lib/modules/manager/terraform/readme.md

Co-authored-by: default avatarSebastian Poxhofer <secustor@users.noreply.github.com>

* switch to toMatchObject() in tests

Co-authored-by: default avatarSebastian Poxhofer <secustor@users.noreply.github.com>
Co-authored-by: default avatarRhys Arkins <rhys@arkins.net>
Co-authored-by: default avatarMichael Kriese <michael.kriese@visualon.de>
parent 6a784366
No related branches found
No related tags found
No related merge requests found
# docker_image resources
# https://www.terraform.io/docs/providers/docker/r/image.html
# https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/image
resource "docker_image" "nginx" {
name = "nginx:1.7.8"
}
......@@ -14,7 +14,7 @@ resource "docker_image" "ignore_variable" {
# docker_container resources
# https://www.terraform.io/docs/providers/docker/r/container.html
# https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container
resource "docker_container" "foo" {
name = "foo"
image = "nginx:1.7.8"
......@@ -26,7 +26,7 @@ resource "docker_container" "invalid" {
# docker_service resources
# https://www.terraform.io/docs/providers/docker/r/service.html
# https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/service
resource "docker_service" "foo" {
name = "foo-service"
......
resource "kubernetes_cron_job_v1" "demo" {
metadata {}
spec {
job_template {
metadata {}
spec {
template {
metadata {}
spec {
container {
name = "kaniko"
image = "gcr.io/kaniko-project/executor:v1.7.0@sha256:8504bde9a9a8c9c4e9a4fe659703d265697a36ff13607b7669a4caa4407baa52"
}
}
}
}
}
schedule = ""
}
}
resource "kubernetes_cron_job" "demo" {
metadata {}
spec {
job_template {
metadata {}
spec {
template {
metadata {}
spec {
container {
name = "kaniko"
image = "gcr.io/kaniko-project/executor:v1.8.0@sha256:8504bde9a9a8c9c4e9a4fe659703d265697a36ff13607b7669a4caa4407baa52"
}
}
}
}
}
schedule = ""
}
}
resource "kubernetes_daemon_set_v1" "example" {
metadata {}
spec {
template {
metadata {}
spec {
container {
image = "nginx:1.21.1"
name = "example1"
}
}
}
}
}
resource "kubernetes_daemonset" "example" {
metadata {}
spec {
template {
metadata {}
spec {
container {
image = "nginx:1.21.2"
name = "example2"
}
}
}
}
}
resource "kubernetes_deployment" "example" {
metadata {}
spec {
template {
metadata {}
spec {
container {
image = "nginx:1.21.3"
name = "example3"
}
}
}
}
}
resource "kubernetes_deployment_v1" "example" {
metadata {}
spec {
template {
metadata {}
spec {
container {
image = "nginx:1.21.4"
name = "example4"
}
}
}
}
}
resource "kubernetes_job" "demo" {
metadata {}
spec {
template {
metadata {}
spec {
container {
name = "example5"
image = "nginx:1.21.5"
}
}
}
}
}
resource "kubernetes_job" "demo_invalid" {
metadata {}
spec {
template {
metadata {}
spec {
container {
name = "example5-invalid"
}
}
}
}
}
resource "kubernetes_job_invalid" "demo_invalid2" {
metadata {}
spec {
template {
metadata {}
spec {
container {
name = "example5"
image = "nginx:1.21.6"
}
}
}
}
}
resource "kubernetes_job_v1" "demo" {
metadata {}
spec {
template {
metadata {}
spec {
container {
name = "example6"
image = "nginx:1.21.6"
}
}
}
}
}
resource "kubernetes_pod" "test" {
metadata {}
spec {
container {
image = "nginx:1.21.7"
name = "example7"
}
}
}
resource "kubernetes_pod_v1" "test" {
metadata {}
spec {
container {
image = "nginx:1.21.8"
name = "example8"
}
}
}
resource "kubernetes_replication_controller" "example" {
metadata {}
spec {
selector = {}
template {
metadata {}
spec {
container {
image = "nginx:1.21.9"
name = "example9"
}
}
}
}
}
resource "kubernetes_replication_controller_v1" "example" {
metadata {}
spec {
selector = {}
template {
metadata {}
spec {
container {
image = "nginx:1.21.10"
name = "example10"
}
}
}
}
}
resource "kubernetes_stateful_set" "prometheus" {
metadata {}
spec {
template {
metadata {}
spec {
init_container {
name = "example11"
image = "nginx:1.21.11"
}
container {
name = "prometheus-server1"
image = "prom/prometheus:v2.2.1"
}
}
}
service_name = ""
selector {}
}
}
resource "kubernetes_stateful_set_v1" "prometheus" {
metadata {}
spec {
template {
metadata {}
spec {
init_container {
name = "example12"
image = "nginx:1.21.12"
}
container {
name = "prometheus-server2"
image = "prom/prometheus:v2.2.2"
}
}
}
service_name = ""
selector {}
}
}
......@@ -11,27 +11,33 @@ export enum TerraformDependencyTypes {
terraform_version = 'terraform_version',
}
// eslint-disable-next-line typescript-enum/no-enum
export enum TerraformResourceTypes {
unknown = 'unknown',
/**
* https://www.terraform.io/docs/providers/docker/r/container.html
*/
docker_container = 'docker_container',
/**
* https://www.terraform.io/docs/providers/docker/r/image.html
*/
docker_image = 'docker_image',
/**
* https://www.terraform.io/docs/providers/docker/r/service.html
*/
docker_service = 'docker_service',
/**
* https://www.terraform.io/docs/providers/helm/r/release.html
*/
helm_release = 'helm_release',
/**
* https://registry.terraform.io/providers/hashicorp/tfe/latest/docs/resources/workspace
*/
tfe_workspace = 'tfe_workspace',
}
export const TerraformResourceTypes: Record<string, string[]> = {
unknown: ['unknown'],
generic_image_resource: [
// Docker provider: https://registry.terraform.io/providers/kreuzwerker/docker
'docker_container',
'docker_service',
// Kubernetes provider: https://registry.terraform.io/providers/hashicorp/kubernetes
'kubernetes_cron_job',
'kubernetes_cron_job_v1',
'kubernetes_daemon_set',
'kubernetes_daemon_set_v1',
'kubernetes_daemonset',
'kubernetes_deployment',
'kubernetes_deployment_v1',
'kubernetes_job',
'kubernetes_job_v1',
'kubernetes_pod',
'kubernetes_pod_v1',
'kubernetes_replication_controller',
'kubernetes_replication_controller_v1',
'kubernetes_stateful_set',
'kubernetes_stateful_set_v1',
],
// https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/image
docker_image: ['docker_image'],
// https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release
helm_release: ['helm_release'],
// https://registry.terraform.io/providers/hashicorp/tfe/latest/docs/resources/workspace
tfe_workspace: ['tfe_workspace'],
};
......@@ -10,6 +10,7 @@ const bitbucketModules = Fixtures.get('bitbucketModules.tf');
const azureDevOpsModules = Fixtures.get('azureDevOpsModules.tf');
const providers = Fixtures.get('providers.tf');
const docker = Fixtures.get('docker.tf');
const kubernetes = Fixtures.get('kubernetes.tf');
const tf2 = `module "relative" {
source = "../fe"
......@@ -104,6 +105,90 @@ describe('modules/manager/terraform/extract', () => {
expect(res).toMatchSnapshot();
});
it('extracts kubernetes resources', async () => {
const res = await extractPackageFile(kubernetes, 'kubernetes.tf', {});
expect(res.deps).toHaveLength(16);
expect(res.deps.filter((dep) => dep.skipReason)).toHaveLength(2);
expect(res.deps).toMatchObject([
{
depName: 'gcr.io/kaniko-project/executor',
currentValue: 'v1.7.0',
currentDigest:
'sha256:8504bde9a9a8c9c4e9a4fe659703d265697a36ff13607b7669a4caa4407baa52',
depType: 'kubernetes_cron_job_v1',
},
{
depName: 'gcr.io/kaniko-project/executor',
currentValue: 'v1.8.0',
currentDigest:
'sha256:8504bde9a9a8c9c4e9a4fe659703d265697a36ff13607b7669a4caa4407baa52',
depType: 'kubernetes_cron_job',
},
{
depName: 'nginx',
currentValue: '1.21.1',
depType: 'kubernetes_daemon_set_v1',
},
{
depName: 'nginx',
currentValue: '1.21.2',
depType: 'kubernetes_daemonset',
},
{
depName: 'nginx',
currentValue: '1.21.3',
depType: 'kubernetes_deployment',
},
{
depName: 'nginx',
currentValue: '1.21.4',
depType: 'kubernetes_deployment_v1',
},
{
depName: 'nginx',
currentValue: '1.21.5',
depType: 'kubernetes_job',
},
{ skipReason: 'invalid-dependency-specification' },
{ skipReason: 'invalid-value' },
{
depName: 'nginx',
currentValue: '1.21.6',
depType: 'kubernetes_job_v1',
},
{
depName: 'nginx',
currentValue: '1.21.7',
depType: 'kubernetes_pod',
},
{
depName: 'nginx',
currentValue: '1.21.8',
depType: 'kubernetes_pod_v1',
},
{
depName: 'nginx',
currentValue: '1.21.9',
depType: 'kubernetes_replication_controller',
},
{
depName: 'nginx',
currentValue: '1.21.10',
depType: 'kubernetes_replication_controller_v1',
},
{
depName: 'prom/prometheus',
currentValue: 'v2.2.1',
depType: 'kubernetes_stateful_set',
},
{
depName: 'prom/prometheus',
currentValue: 'v2.2.2',
depType: 'kubernetes_stateful_set_v1',
},
]);
});
it('returns null if only local deps', async () => {
expect(await extractPackageFile(tf2, '2.tf', {})).toBeNull();
});
......
......@@ -34,6 +34,8 @@ const dependencyBlockExtractionRegex = regEx(
const contentCheckList = [
'module "',
'provider "',
'"docker_',
'"kubernetes_',
'required_providers ',
' "helm_release" ',
' "docker_image" ',
......
Currently, Terraform supports renovating the following dependencies, where sub points represent hosting options of the dependencies:
Currently, Terraform supports renovating the following dependencies, where sub-points represent hosting options of the dependencies:
- modules
- GitTags
......@@ -13,6 +13,8 @@ Currently, Terraform supports renovating the following dependencies, where sub p
- chart repository ( Public and Private )
- docker\_\*
- Docker registry ( Public and Private )
- kubernetes\_\*
- Docker registry ( Public and Private )
- [tfe_workspace](https://registry.terraform.io/providers/hashicorp/tfe/latest/docs/resources/workspace) ( `terraform_version` argument )
Terraform range constraints are supported:
......@@ -23,10 +25,10 @@ Terraform range constraints are supported:
- `~> 1.2`: any non-beta version >= 1.2.0 and < 2.0.0, e.g. 1.X.Y
- `>= 1.0.0, <= 2.0.0`: any version between 1.0.0 and 2.0.0 inclusive
For fine-grained control, e.g. to turn off only parts of this manager, you can use the following `depTypes`:
For fine-grained control, e.g., to turn off only parts of this manager, you can use the following `depTypes`:
| resource | depType | Notes |
| --------------------------- | :-----------------: | :------------------------------------------------------------------------: |
| ------------------------------------ | :------------------------------------: | :------------------------------------------------------------------------: |
| Terraform provider | `provider` | |
| required Terraform provider | `required_provider` | |
| required Terraform version | `required_version` | This handles the `required_version` in terraform blocks |
......@@ -36,5 +38,19 @@ For fine-grained control, e.g. to turn off only parts of this manager, you can u
| Docker container | `docker_container` | |
| Docker image | `docker_image` | |
| Docker service | `docker_service` | |
| Kubernetes CronJob | `kubernetes_cron_job` | |
| Kubernetes CronJob v1 | `kubernetes_cron_job_v1` | |
| Kubernetes DaemonSet | `kubernetes_daemon_set` | |
| Kubernetes DaemonSet v1 | `kubernetes_daemon_set_v1` | |
| Kubernetes Deployment | `kubernetes_deployment` | |
| Kubernetes Deployment v1 | `kubernetes_deployment_v1` | |
| Kubernetes Job | `kubernetes_job` | |
| Kubernetes Job v1 | `kubernetes_job_v1` | |
| Kubernetes Pod | `kubernetes_pod` | |
| Kubernetes Pod v1 | `kubernetes_pod_v1` | |
| Kubernetes Replication Controller | `kubernetes_replication_controller` | |
| Kubernetes Replication Controller v1 | `kubernetes_replication_controller_v1` | |
| Kubernetes StatefulSet | `kubernetes_stateful_set` | |
| Kubernetes StatefulSet v1 | `kubernetes_stateful_set_v1` | |
If you need to change the versioning format, read the [versioning](https://docs.renovatebot.com/modules/versioning/) documentation to learn more.
......@@ -37,10 +37,18 @@ export function extractTerraformResource(
const typeMatch = resourceTypeExtractionRegex.exec(line);
// Sets the resourceType, e.g. "helm_release" 'resource "helm_release" "test_release"'
managerData.resourceType =
TerraformResourceTypes[typeMatch?.groups?.type as TerraformResourceTypes] ??
TerraformResourceTypes.unknown;
// Sets the resourceType, e.g., 'resource "helm_release" "test_release"' -> helm_release
const resourceType = typeMatch?.groups?.type;
const isKnownType =
resourceType &&
Object.keys(TerraformResourceTypes).some((key) => {
return TerraformResourceTypes[key].includes(resourceType);
});
managerData.resourceType = isKnownType
? resourceType
: TerraformResourceTypes.unknown[0];
/**
* Iterates over all lines of the resource to extract the relevant key value pairs,
......@@ -96,21 +104,19 @@ export function extractTerraformResource(
export function analyseTerraformResource(
dep: PackageDependency<ResourceManagerData>
): void {
// istanbul ignore if: should tested?
if (!dep.managerData) {
return;
}
switch (dep.managerData.resourceType) {
case TerraformResourceTypes.docker_container:
switch (dep.managerData?.resourceType) {
case TerraformResourceTypes.generic_image_resource.find(
(key) => key === dep.managerData?.resourceType
):
if (dep.managerData.image) {
applyDockerDependency(dep, dep.managerData.image);
dep.depType = 'docker_container';
dep.depType = dep.managerData.resourceType;
} else {
dep.skipReason = 'invalid-dependency-specification';
}
break;
case TerraformResourceTypes.docker_image:
case TerraformResourceTypes.docker_image[0]:
if (dep.managerData.name) {
applyDockerDependency(dep, dep.managerData.name);
dep.depType = 'docker_image';
......@@ -119,16 +125,7 @@ export function analyseTerraformResource(
}
break;
case TerraformResourceTypes.docker_service:
if (dep.managerData.image) {
applyDockerDependency(dep, dep.managerData.image);
dep.depType = 'docker_service';
} else {
dep.skipReason = 'invalid-dependency-specification';
}
break;
case TerraformResourceTypes.helm_release:
case TerraformResourceTypes.helm_release[0]:
if (!dep.managerData.chart) {
dep.skipReason = 'invalid-name';
} else if (checkIfStringIsPath(dep.managerData.chart)) {
......@@ -141,7 +138,7 @@ export function analyseTerraformResource(
dep.datasource = HelmDatasource.id;
break;
case TerraformResourceTypes.tfe_workspace:
case TerraformResourceTypes.tfe_workspace[0]:
if (dep.currentValue) {
analyseTerraformVersion(dep);
dep.depType = 'tfe_workspace';
......
import type { PackageDependency } from '../types';
import type {
TerraformDependencyTypes,
TerraformResourceTypes,
} from './common';
import type { TerraformDependencyTypes } from './common';
export interface ExtractionResult {
lineNumber: number;
......@@ -17,7 +14,7 @@ export interface TerraformManagerData {
}
export interface ResourceManagerData extends TerraformManagerData {
resourceType?: TerraformResourceTypes;
resourceType?: string;
chart?: string;
image?: string;
name?: string;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment