class Chef::Provider::AptRepository
Constants
- LIST_APT_KEY_FINGERPRINTS
Public Instance Methods
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
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
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
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
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
@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
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
@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 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
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
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
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
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
# File lib/chef/provider/apt_repository.rb, line 35 def load_current_resource end
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
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
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