Skip to content

Commit

Permalink
fix(terraform): Reduce constraints changes in lockfiles (#25430)
Browse files Browse the repository at this point in the history
Co-authored-by: Rhys Arkins <rhys@arkins.net>
  • Loading branch information
zharinov and rarkins committed Nov 2, 2023
1 parent 0db0bf0 commit a7faacc
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 3 deletions.
179 changes: 179 additions & 0 deletions lib/modules/manager/terraform/lockfile/index.spec.ts
Expand Up @@ -910,4 +910,183 @@ describe('modules/manager/terraform/lockfile/index', () => {
});
expect(result).toBeNull();
});

it('preserves constraints when current value and new value are same', async () => {
fs.readLocalFile.mockResolvedValueOnce(codeBlock`
provider "registry.terraform.io/hashicorp/aws" {
version = "3.0.0"
constraints = "~> 3.0.0"
hashes = [
"aaa",
"bbb",
"ccc",
]
}
`);
fs.findLocalSiblingOrParent.mockResolvedValueOnce('.terraform.lock.hcl');

mockHash.mockResolvedValueOnce([
'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=',
'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=',
]);

const result = await updateArtifacts({
packageFileName: 'main.tf',
updatedDeps: [
{
depName: 'aws',
depType: 'provider',
packageName: 'hashicorp/aws',
registryUrls: ['https://registry.example.com'],
newVersion: '3.36.1',
currentValue: '~> 3.36',
newValue: '~> 3.36',
},
],
newPackageFileContent: '',
config,
});

expect(result).toEqual([
{
file: {
contents: codeBlock`
provider "registry.terraform.io/hashicorp/aws" {
version = "3.36.1"
constraints = "~> 3.0.0"
hashes = [
"h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=",
"h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=",
]
}
`,
path: '.terraform.lock.hcl',
type: 'addition',
},
},
]);

expect(mockHash.mock.calls).toEqual([
['https://registry.example.com', 'hashicorp/aws', '3.36.1'],
]);
});

it('replaces current value to new version within a constraint', async () => {
fs.readLocalFile.mockResolvedValueOnce(codeBlock`
provider "registry.terraform.io/hashicorp/aws" {
version = "3.0.0"
constraints = "~> 3.0.0"
hashes = [
"aaa",
"bbb",
"ccc",
]
}
`);
fs.findLocalSiblingOrParent.mockResolvedValueOnce('.terraform.lock.hcl');

mockHash.mockResolvedValueOnce([
'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=',
'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=',
]);

const result = await updateArtifacts({
packageFileName: 'main.tf',
updatedDeps: [
{
depName: 'aws',
depType: 'provider',
packageName: 'hashicorp/aws',
registryUrls: ['https://registry.example.com'],
newVersion: '3.37.0',
currentValue: '~> 3.0.0',
newValue: '~> 3.37.0',
},
],
newPackageFileContent: '',
config,
});

expect(result).toEqual([
{
file: {
contents: codeBlock`
provider "registry.terraform.io/hashicorp/aws" {
version = "3.37.0"
constraints = "~> 3.37.0"
hashes = [
"h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=",
"h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=",
]
}
`,
path: '.terraform.lock.hcl',
type: 'addition',
},
},
]);

expect(mockHash.mock.calls).toEqual([
['https://registry.example.com', 'hashicorp/aws', '3.37.0'],
]);
});

it('replaces current version to new version within a constraint', async () => {
fs.readLocalFile.mockResolvedValueOnce(codeBlock`
provider "registry.terraform.io/hashicorp/aws" {
version = "3.0.0"
constraints = "~> 3.0.0"
hashes = [
"aaa",
"bbb",
"ccc",
]
}
`);
fs.findLocalSiblingOrParent.mockResolvedValueOnce('.terraform.lock.hcl');

mockHash.mockResolvedValueOnce([
'h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=',
'h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=',
]);

const result = await updateArtifacts({
packageFileName: 'main.tf',
updatedDeps: [
{
depName: 'aws',
depType: 'provider',
packageName: 'hashicorp/aws',
registryUrls: ['https://registry.example.com'],
newVersion: '3.37.0',
currentVersion: '3.0.0',
},
],
newPackageFileContent: '',
config,
});

expect(result).toEqual([
{
file: {
contents: codeBlock`
provider "registry.terraform.io/hashicorp/aws" {
version = "3.37.0"
constraints = "~> 3.37.0"
hashes = [
"h1:lDsKRxDRXPEzA4AxkK4t+lJd3IQIP2UoaplJGjQSp2s=",
"h1:6zB2hX7YIOW26OrKsLJn0uLMnjqbPNxcz9RhlWEuuSY=",
]
}
`,
path: '.terraform.lock.hcl',
type: 'addition',
},
},
]);

expect(mockHash.mock.calls).toEqual([
['https://registry.example.com', 'hashicorp/aws', '3.37.0'],
]);
});
});
59 changes: 56 additions & 3 deletions lib/modules/manager/terraform/lockfile/index.ts
Expand Up @@ -4,7 +4,11 @@ import * as p from '../../../../util/promises';
import { GetPkgReleasesConfig, getPkgReleases } from '../../../datasource';
import { TerraformProviderDatasource } from '../../../datasource/terraform-provider';
import { get as getVersioning } from '../../../versioning';
import type { UpdateArtifact, UpdateArtifactsResult } from '../../types';
import type {
UpdateArtifact,
UpdateArtifactsResult,
Upgrade,
} from '../../types';
import { massageProviderLookupName } from '../util';
import { TerraformProviderHash } from './hash';
import type { ProviderLock, ProviderLockUpdate } from './types';
Expand Down Expand Up @@ -61,6 +65,55 @@ async function updateAllLocks(
return updates.filter(is.truthy);
}

function getNewConstraint(
dep: Upgrade<Record<string, unknown>>,
oldConstraint: string | undefined
): string | undefined {
const { currentValue, currentVersion, newValue, newVersion, packageName } =
dep;

if (oldConstraint && currentValue && newValue && currentValue === newValue) {
logger.debug(
`Leaving constraints "${oldConstraint}" unchanged for "${packageName}" as current and new values are the same`
);
return oldConstraint;
}

if (
oldConstraint &&
currentValue &&
newValue &&
oldConstraint.includes(currentValue)
) {
logger.debug(
`Updating constraint "${oldConstraint}" to replace "${currentValue}" with "${newValue}" for "${packageName}"`
);
return oldConstraint.replace(currentValue, newValue);
}

if (
oldConstraint &&
currentVersion &&
newVersion &&
oldConstraint.includes(currentVersion)
) {
logger.debug(
`Updating constraint "${oldConstraint}" to replace "${currentVersion}" with "${newVersion}" for "${packageName}"`
);
return oldConstraint.replace(currentVersion, newVersion);
}

if (isPinnedVersion(newValue)) {
logger.debug(`Pinning constraint for "${packageName}" to "${newVersion}"`);
return newVersion;
}

logger.debug(
`Could not detect constraint to update for "${packageName}" so setting to newValue "${newValue}"`
);
return newValue;
}

export async function updateArtifacts({
packageFileName,
updatedDeps,
Expand Down Expand Up @@ -99,19 +152,19 @@ export async function updateArtifacts({
);
for (const dep of providerDeps) {
massageProviderLookupName(dep);
const { registryUrls, newVersion, newValue, packageName } = dep;
const { registryUrls, newVersion, packageName } = dep;

const registryUrl = registryUrls
? registryUrls[0]
: TerraformProviderDatasource.defaultRegistryUrls[0];
const newConstraint = isPinnedVersion(newValue) ? newVersion : newValue;
const updateLock = locks.find(
(value) => value.packageName === packageName
);
// istanbul ignore if: needs test
if (!updateLock) {
continue;
}
const newConstraint = getNewConstraint(dep, updateLock.constraints);
const update: ProviderLockUpdate = {
// TODO #22198
newVersion: newVersion!,
Expand Down

0 comments on commit a7faacc

Please sign in to comment.