class Chef::Provider::AptRepository

Constants

LIST_APT_KEY_FINGERPRINTS

Public Instance Methods

build_repo(uri, distribution, components, trusted, arch, add_src = false) click to toggle source

build complete repo text that will be written to the config @param [String] uri @param [Array] components @param [Boolean] trusted @param [String] arch @param [Boolean] add_src

@return [String] complete repo config text

# File lib/chef/provider/apt_repository.rb, line 323
def build_repo(uri, distribution, components, trusted, arch, add_src = false)
  uri = make_ppa_url(uri) if is_ppa_url?(uri)

  uri = '"' + uri + '"' unless uri.start_with?("'", '"')
  components = Array(components).join(" ")
  options = []
  options << "arch=#{arch}" if arch
  options << "trusted=yes" if trusted
  optstr = unless options.empty?
             "[" + options.join(" ") + "]"
           end
  info = [ optstr, uri, distribution, components ].compact.join(" ")
  repo =  "deb      #{info}\n"
  repo << "deb-src  #{info}\n" if add_src
  repo
end
cleanup_legacy_file!() click to toggle source

clean up a potentially legacy file from before we fixed the usage of new_resource.name vs. new_resource.repo_name. We might have the name.list file hanging around and need to clean it up.

@return [void]

# File lib/chef/provider/apt_repository.rb, line 345
def cleanup_legacy_file!
  legacy_path = "/etc/apt/sources.list.d/#{new_resource.name}.list"
  if new_resource.name != new_resource.repo_name && ::File.exist?(legacy_path)
    converge_by "Cleaning up legacy #{legacy_path} repo file" do
      declare_resource(:file, legacy_path) do
        action :delete
        # Not triggering an update since it isn't super likely to be needed.
      end
    end
  end
end
cookbook_name() click to toggle source

return the specified cookbook name or the cookbook containing the resource.

@return [String] name of the cookbook

# File lib/chef/provider/apt_repository.rb, line 152
def cookbook_name
  new_resource.cookbook || new_resource.cookbook_name
end
extract_fingerprints_from_cmd(*cmd) click to toggle source

run the specified command and extract the fingerprints from the output accepts a command so it can be used to extract both the current key's fingerprints and the fingerprint of the new key @param [Array<String>] cmd the command to run

@return [Array] an array of fingerprints

# File lib/chef/provider/apt_repository.rb, line 119
def extract_fingerprints_from_cmd(*cmd)
  so = shell_out(*cmd)
  so.stdout.split(/\n/).map do |t|
    if z = t.match(/^fpr:+([0-9A-F]+):/)
      z[1].split.join
    end
  end.compact
end
has_cookbook_file?(fn) click to toggle source

determine if a cookbook file is available in the run @param [String] fn the path to the cookbook file

@return [Boolean] cookbook file exists or doesn't

# File lib/chef/provider/apt_repository.rb, line 160
def has_cookbook_file?(fn)
  run_context.has_cookbook_file_in_cookbook?(cookbook_name, fn)
end
install_key_from_keyserver(key, keyserver = new_resource.keyserver) click to toggle source

@param [String] key @param [String] keyserver

@raise [RuntimeError] Invalid key which can't verify the apt repository

@return [void]

# File lib/chef/provider/apt_repository.rb, line 250
def install_key_from_keyserver(key, keyserver = new_resource.keyserver)
  declare_resource(:execute, "install-key #{key}") do
    command keyserver_install_cmd(key, keyserver)
    default_env true
    sensitive new_resource.sensitive
    not_if do
      present = extract_fingerprints_from_cmd(*LIST_APT_KEY_FINGERPRINTS).any? do |fp|
        fp.end_with? key.upcase
      end
      present && key_is_valid?(key.upcase)
    end
    notifies :run, "execute[apt-cache gencaches]", :immediately
  end

  raise "The key #{key} is invalid and cannot be used to verify an apt repository." unless key_is_valid?(key.upcase)
end
install_key_from_uri(key) click to toggle source

Fetch the key using either cookbook_file or remote_file, validate it, and install it with apt-key add @param [String] key the key to install

@raise [RuntimeError] Invalid key which can't verify the apt repository

@return [void]

# File lib/chef/provider/apt_repository.rb, line 201
def install_key_from_uri(key)
  key_name = key.gsub(/[^0-9A-Za-z\-]/, "_")
  cached_keyfile = ::File.join(Chef::Config[:file_cache_path], key_name)
  tmp_dir = Dir.mktmpdir(".gpg")
  at_exit { FileUtils.remove_entry(tmp_dir) }

  declare_resource(key_type(key), cached_keyfile) do
    source key
    mode "0644"
    sensitive new_resource.sensitive
    action :create
    verify "gpg --homedir #{tmp_dir} %{path}"
  end

  declare_resource(:execute, "apt-key add #{cached_keyfile}") do
    command [ "apt-key", "add", cached_keyfile ]
    default_env true
    sensitive new_resource.sensitive
    action :run
    not_if { no_new_keys?(cached_keyfile) }
    notifies :run, "execute[apt-cache gencaches]", :immediately
  end
end
install_ppa_key(owner, repo) click to toggle source

@param [String] owner @param [String] repo

@raise [RuntimeError] Could not access the Launchpad PPA API

@return [void]

# File lib/chef/provider/apt_repository.rb, line 273
def install_ppa_key(owner, repo)
  url = "https://launchpad.net/api/1.0/~#{owner}/+archive/#{repo}"
  key_id = Chef::HTTP::Simple.new(url).get("signing_key_fingerprint").delete('"')
  install_key_from_keyserver(key_id, "keyserver.ubuntu.com")
rescue Net::HTTPClientException => e
  raise "Could not access Launchpad ppa API: #{e.message}"
end
is_key_id?(id) click to toggle source

is the provided ID a key ID from a keyserver. Looks at length and HEX only values @param [String] id the key value passed by the user that may be an ID

# File lib/chef/provider/apt_repository.rb, line 108
def is_key_id?(id)
  id = id[2..-1] if id.start_with?("0x")
  id =~ /^\h+$/ && [8, 16, 40].include?(id.length)
end
is_ppa_url?(url) click to toggle source

determine if the repository URL is a PPA @param [String] url the url of the repository

@return [Boolean] is the repo URL a PPA

# File lib/chef/provider/apt_repository.rb, line 285
def is_ppa_url?(url)
  url.start_with?("ppa:")
end
key_is_valid?(key) click to toggle source

validate the key against the apt keystore to see if that version is expired @param [String] key

@return [Boolean] is the key valid or not

# File lib/chef/provider/apt_repository.rb, line 132
def key_is_valid?(key)
  valid = true

  so = shell_out("apt-key", "list")
  so.stdout.split(/\n/).map do |t|
    if t =~ %r{^\/#{key}.*\[expired: .*\]$}
      logger.debug "Found expired key: #{t}"
      valid = false
      break
    end
  end

  logger.debug "key #{key} #{valid ? "is valid" : "is not valid"}"
  valid
end
key_type(uri) click to toggle source

Given the provided key URI determine what kind of chef resource we need to fetch the key @param [String] uri the uri of the gpg key (local path or http URL)

@raise [Chef::Exceptions::FileNotFound] Key isn't remote or found in the current run

@return [Symbol] :remote_file or :cookbook_file

# File lib/chef/provider/apt_repository.rb, line 184
def key_type(uri)
  if uri.start_with?("http")
    :remote_file
  elsif has_cookbook_file?(uri)
    :cookbook_file
  else
    raise Chef::Exceptions::FileNotFound, "Cannot locate key file: #{uri}"
  end
end
keyserver_install_cmd(key, keyserver) click to toggle source

build the apt-key command to install the keyserver @param [String] key the key to install @param [String] keyserver the key server to use

@return [String] the full apt-key command to run

# File lib/chef/provider/apt_repository.rb, line 230
def keyserver_install_cmd(key, keyserver)
  cmd = "apt-key adv --no-tty --recv"
  cmd << " --keyserver-options http-proxy=#{new_resource.key_proxy}" if new_resource.key_proxy
  cmd << " --keyserver "
  cmd << if keyserver.start_with?("hkp://")
           keyserver
         else
           "hkp://#{keyserver}:80"
         end

  cmd << " #{key}"
  cmd
end
load_current_resource() click to toggle source
# File lib/chef/provider/apt_repository.rb, line 35
def load_current_resource
end
make_ppa_url(ppa) click to toggle source

given a PPA return a PPA URL in ppa.launchpad.net format @param [String] ppa the ppa URL

@return [String] full PPA URL

# File lib/chef/provider/apt_repository.rb, line 307
def make_ppa_url(ppa)
  owner, repo = ppa[4..-1].split("/")
  repo ||= "ppa"

  install_ppa_key(owner, repo)
  "http://ppa.launchpad.net/#{owner}/#{repo}/ubuntu"
end
no_new_keys?(file) click to toggle source

determine if there are any new keys by comparing the fingerprints of installed keys to those of the passed file @param [String] file the keyfile of the new repository

@return [Boolean] true: no new keys in the file. false: there are new keys

# File lib/chef/provider/apt_repository.rb, line 169
def no_new_keys?(file)
  # Now we are using the option --with-colons that works across old os versions
  # as well as the latest (16.10). This for both `apt-key` and `gpg` commands
  installed_keys = extract_fingerprints_from_cmd(*LIST_APT_KEY_FINGERPRINTS)
  proposed_keys = extract_fingerprints_from_cmd("gpg", "--with-fingerprint", "--with-colons", file)
  (installed_keys & proposed_keys).sort == proposed_keys.sort
end
repo_components() click to toggle source

determine the repository's components:

- "components" property if defined
- "main" if "components" not defined and the repo is a PPA URL
- otherwise nothing

@return [String] the repository component

# File lib/chef/provider/apt_repository.rb, line 295
def repo_components
  if is_ppa_url?(new_resource.uri) && new_resource.components.empty?
    "main"
  else
    new_resource.components
  end
end