class TerraformWrapper::Tasks::Binary

Public Class Methods

new(binary:) { |self| ... } click to toggle source
# File lib/terraform-wrapper/tasks/binary.rb, line 31
def initialize(binary:)
  @binary = binary

  yield self if block_given?

  binary_task
end

Public Instance Methods

binary_task() click to toggle source
# File lib/terraform-wrapper/tasks/binary.rb, line 41
def binary_task
  desc "Downloads and extracts the expected version of the Terraform binary if it is not already present."
  task :binary do |t, args|
    logger.info("Checking Terraform binary for platform: #{@binary.platform}, version: #{@binary.version}")

    if not @binary.exists then
      logger.info("Terraform binary not found. Preparing binary...")

      logger.fatal("Failed to create binary directory: #{directory}") unless ::TerraformWrapper.create_directory(directory: @binary.directory, purpose: "binaries")

      archive_binary = "terraform"
      archive_file   = "terraform_#{@binary.version}_#{@binary.platform}_amd64.zip"
      archive_path   = File.join(@binary.directory, archive_file)
      archive_uri    = "https://releases.hashicorp.com/terraform/#{@binary.version}/#{archive_file}"

      sums_file = "terraform_#{@binary.version}_SHA256SUMS"
      sums_path = File.join(@binary.directory, sums_file)
      sums_uri  = "https://releases.hashicorp.com/terraform/#{@binary.version}/#{sums_file}"

      begin
        download(path: archive_path, uri: archive_uri) if not File.file?(archive_path)
        download(path: sums_path, uri: sums_uri) if not File.file?(sums_path)
        verify(file: archive_file, path: archive_path, sums: sums_path)
        extract(archive: archive_path, binary: archive_binary, destination: @binary.path)
      ensure
        clean(archive: archive_path, sums: sums_path)
      end
    end

    if not @binary.executable then
      logger.info("Terraform binary not executable. Setting permissions...")
      executable(path: @binary.path)
    end

    logger.fatal("Problem with checking the Terraform binary!") unless @binary.check
  end
end

Private Instance Methods

clean(archive:, sums:) click to toggle source
# File lib/terraform-wrapper/tasks/binary.rb, line 155
def clean(archive:, sums:)
  [archive, sums].each do |file|
    if File.file?(file)
      logger.info("Removing file: #{file}")

      begin
        File.delete(file)
      rescue
        logger.error("Failed to delete: #{file}, please remove manually.")
      end
    end
  end
end
download(path:, uri:) click to toggle source
# File lib/terraform-wrapper/tasks/binary.rb, line 85
def download(path:, uri:)
  logger.info("Downloading: #{uri}")

  response = Net::HTTP.get_response(URI(uri))

  logger.fatal("Download request did not return HTTP status 200!") if response.code != "200"
  logger.fatal("Download response body is not permitted!")         unless response.class.body_permitted?
  logger.fatal("Download response body is empty!")                 if response.body.nil?

  open(path, "wb") { |file|
    file.write(response.body)
  }

  logger.fatal("Download failed!") unless File.file?(path)
end
executable(path:) click to toggle source
# File lib/terraform-wrapper/tasks/binary.rb, line 147
def executable(path:)
  logger.info("Making executable: #{path}")
  FileUtils.chmod("+x", path)
  logger.fatal("Setting executable bit on file: #{path} has failed!") unless File.executable?(path)
end
extract(archive:, binary:, destination:) click to toggle source
# File lib/terraform-wrapper/tasks/binary.rb, line 133
def extract(archive:, binary:, destination:)
  logger.info("Extracting: #{archive}")

  Zip::File.open(archive) do |zip|
    zip.each do |file|
      zip.extract(file, destination) if file.name == binary
    end
  end

  logger.fatal("Extraction of Terraform binary: #{binary}, from archive: #{archive} has failed!") unless File.file?(destination)
end
verify(file:, path:, sums:) click to toggle source
# File lib/terraform-wrapper/tasks/binary.rb, line 103
def verify(file:, path:, sums:)
  logger.info("Checking SHA256 for: #{file}")

  result = false

  sha256 = Digest::SHA256.hexdigest File.read(path)

  File.readlines(sums).each do |line|
    begin
      fields     = line.match /^(?<sum>\S+)\s+(?<file>\S+)$/
      sum_file   = fields["file"]
      sum_sha256 = fields["sum"]
    rescue
      logger.warn("Unexpected data in sums file: #{sums}")
      next
    end

    if sum_file == file then
      logger.info("Expected SHA256 sum: #{sum_sha256}")
      logger.info("Actual SHA256 sum: #{sha256}")
      result = (sum_sha256 == sha256)
      break
    end
  end

  logger.fatal("Error whilst verifying the SHA256 sum of the downloaded Terraform archive!") unless result
end