From 9e48dafda829d5dd6d787a4c404628d29cade874 Mon Sep 17 00:00:00 2001 From: Philip Harrison Date: Tue, 22 Jun 2021 16:53:35 +0100 Subject: [PATCH] Terraform: install modules when updating lockfile Terraform requires modules to be installed with `terraform init` when updating the lockfile. Opted to only run `terraform init` if the call to `terraform providers lock` bails out and retry. We could opt to always run `terraform init` if there are any modules defined but would mean parsing the dependency files and checking if any of the dependencies are modules as we only have access to the current dependency, which in this case is provider. --- .../lib/dependabot/terraform/file_updater.rb | 10 +++- .../dependabot/terraform/file_updater_spec.rb | 46 +++++++++++++++++++ .../lockfile_with_modules/.terraform.lock.hcl | 22 +++++++++ .../lockfile_with_modules/caf_module.tf | 4 ++ .../lockfile_with_modules/versions.tf | 9 ++++ 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 terraform/spec/fixtures/projects/lockfile_with_modules/.terraform.lock.hcl create mode 100644 terraform/spec/fixtures/projects/lockfile_with_modules/caf_module.tf create mode 100644 terraform/spec/fixtures/projects/lockfile_with_modules/versions.tf diff --git a/terraform/lib/dependabot/terraform/file_updater.rb b/terraform/lib/dependabot/terraform/file_updater.rb index 3d008b7c02..d55130accf 100644 --- a/terraform/lib/dependabot/terraform/file_updater.rb +++ b/terraform/lib/dependabot/terraform/file_updater.rb @@ -90,7 +90,7 @@ def update_registry_declaration(new_req, old_req, updated_content) end end - def update_lockfile_declaration + def update_lockfile_declaration # rubocop:disable Metrics/AbcSize return if lock_file.nil? new_req = dependency.requirements.first @@ -115,6 +115,14 @@ def update_lockfile_declaration content.scan(declaration_regex).first.scan(/^\s*version\s*=.*/) content.sub!(declaration_regex, updated_dependency) end + rescue SharedHelpers::HelperSubprocessFailed => e + raise if @retrying_lock || !e.message.include?("terraform init") + + # NOTE: Modules need to be installed before terraform can update the + # lockfile + @retrying_lock = true + SharedHelpers.run_shell_command("terraform init") + retry end content diff --git a/terraform/spec/dependabot/terraform/file_updater_spec.rb b/terraform/spec/dependabot/terraform/file_updater_spec.rb index 4c2e3431d5..08e2c1690d 100644 --- a/terraform/spec/dependabot/terraform/file_updater_spec.rb +++ b/terraform/spec/dependabot/terraform/file_updater_spec.rb @@ -838,5 +838,51 @@ module "github_terraform" { ) end end + + describe "with a lockfile and modules that need to be installed" do + let(:files) { project_dependency_files("lockfile_with_modules") } + let(:dependencies) do + [ + Dependabot::Dependency.new( + name: "integrations/github", + version: "4.12.0", + previous_version: "4.4.0", + requirements: [{ + requirement: "4.12.0", + groups: [], + file: "main.tf", + source: { + type: "registry", + registry_hostname: "registry.terraform.io", + module_identifier: "integrations/github" + } + }], + previous_requirements: [{ + requirement: "4.4.0", + groups: [], + file: "main.tf", + source: { + type: "registry", + registry_hostname: "registry.terraform.io", + module_identifier: "integrations/github" + } + }], + package_manager: "terraform" + ) + ] + end + + it "updates the version in the lockfile" do + lockfile = subject.find { |file| file.name == ".terraform.lock.hcl" } + + expect(lockfile.content).to include( + <<~DEP + provider "registry.terraform.io/integrations/github" { + version = "4.12.0" + constraints = "~> 4.4" + DEP + ) + end + end end end diff --git a/terraform/spec/fixtures/projects/lockfile_with_modules/.terraform.lock.hcl b/terraform/spec/fixtures/projects/lockfile_with_modules/.terraform.lock.hcl new file mode 100644 index 0000000000..29b0caf8ce --- /dev/null +++ b/terraform/spec/fixtures/projects/lockfile_with_modules/.terraform.lock.hcl @@ -0,0 +1,22 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/integrations/github" { + version = "4.4.0" + constraints = "~> 4.4" + hashes = [ + "h1:dgn+oL1cC8kz3ODIuT/PyHqgso00SpItPN089ZuUGt4=", + "h1:eKArqtLcYoYUFf4dgNzVemqu2GsoEf7K0ZLEXjSoPBo=", + "zh:0ebb07c4971ca7d60fce8614270d056328a121fd4ffbda4b29a06d4a1e90e939", + "zh:178b333f2f285c1a59b9335320f584bd01304179c2d6a1919366945b55cfb293", + "zh:2c9087e987a5e1af2aad803a79fa5bd847ac060d4c766b5a187b9aabb3f734a4", + "zh:419597d8d284616ed93a2c13b3833b129aaba7af7a057d2f48aeb7bc3610cefc", + "zh:61686d578880ad76cb8e9c2cc72ad14ef2896fde973cca18b8f7c8848781c71d", + "zh:662e125ac42a0c113d811afd2e7a0f81ba061f00cc62ba7435bd685f889290b9", + "zh:87fb3d97070cae7f0b623d1a9b59e8cfad0dfece4a27ee964c4c77f228592a80", + "zh:e9dcc85ef2f2e09d298f3bbbb9b0d673596d62c1d7d480b2999b4badb2f4aeff", + "zh:f052c377a0630a6881c183ac5de0dbef4e5627638a23434a6aa7fce8977b43de", + "zh:f7456ec2a6a31caa5d2c85f4da660689f8bac5541c70324803de0c26a14586e1", + "zh:fbf6bfddde6f209dc65052ca27e0c83e96bae5fde6940edf029ca42e7e4f1110", + ] +} diff --git a/terraform/spec/fixtures/projects/lockfile_with_modules/caf_module.tf b/terraform/spec/fixtures/projects/lockfile_with_modules/caf_module.tf new file mode 100644 index 0000000000..fb04b444b1 --- /dev/null +++ b/terraform/spec/fixtures/projects/lockfile_with_modules/caf_module.tf @@ -0,0 +1,4 @@ +module "caf" { + source = "aztfmod/caf/azurerm" + version = "5.1.0" +} diff --git a/terraform/spec/fixtures/projects/lockfile_with_modules/versions.tf b/terraform/spec/fixtures/projects/lockfile_with_modules/versions.tf new file mode 100644 index 0000000000..db7fa42821 --- /dev/null +++ b/terraform/spec/fixtures/projects/lockfile_with_modules/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + github = { + source = "integrations/github" + version = "~> 4.4" + } + } + required_version = ">= 0.14" +}