Class: Chef::Provider::AptRepository
- Inherits:
-
Chef::Provider
- Object
- Chef::Provider
- Chef::Provider::AptRepository
- Includes:
- Mixin::ShellOut
- Defined in:
- lib/chef/provider/apt_repository.rb
Constant Summary collapse
- LIST_APT_KEY_FINGERPRINTS =
%w{apt-key adv --list-public-keys --with-fingerprint --with-colons}.freeze
Instance Attribute Summary
Attributes inherited from Chef::Provider
#action, #current_resource, #logger, #new_resource, #recipe_name, #run_context
Instance Method Summary collapse
-
#build_repo(uri, distribution, components, trusted, arch, add_src = false) ⇒ String
build complete repo text that will be written to the config.
-
#cleanup_legacy_file! ⇒ void
clean up a potentially legacy file from before we fixed the usage of new_resource.name vs.
-
#cookbook_name ⇒ String
return the specified cookbook name or the cookbook containing the resource.
-
#extract_fingerprints_from_cmd(*cmd) ⇒ Array
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.
-
#has_cookbook_file?(fn) ⇒ Boolean
determine if a cookbook file is available in the run.
- #install_key_from_keyserver(key, keyserver = new_resource.keyserver) ⇒ void
-
#install_key_from_uri(key) ⇒ void
Fetch the key using either cookbook_file or remote_file, validate it, and install it with apt-key add.
- #install_ppa_key(owner, repo) ⇒ void
-
#is_key_id?(id) ⇒ Boolean
is the provided ID a key ID from a keyserver.
-
#is_ppa_url?(url) ⇒ Boolean
determine if the repository URL is a PPA.
-
#key_is_valid?(key) ⇒ Boolean
validate the key against the apt keystore to see if that version is expired.
-
#key_type(uri) ⇒ Symbol
Given the provided key URI determine what kind of chef resource we need to fetch the key.
-
#keyserver_install_cmd(key, keyserver) ⇒ String
build the apt-key command to install the keyserver.
- #load_current_resource ⇒ Object
-
#make_ppa_url(ppa) ⇒ String
given a PPA return a PPA URL in ppa.launchpad.net format.
-
#no_new_keys?(file) ⇒ Boolean
determine if there are any new keys by comparing the fingerprints of installed keys to those of the passed file.
-
#repo_components ⇒ String
determine the repository’s components: - “components” property if defined - “main” if “components” not defined and the repo is a PPA URL - otherwise nothing.
Methods included from Mixin::ShellOut
apply_default_env, maybe_add_timeout, #shell_out, #shell_out!
Methods inherited from Chef::Provider
action, #action_nothing, #check_resource_semantics!, #cleanup_after_converge, #compile_and_converge_action, #converge_by, #converge_if_changed, #define_resource_requirements, #description, #events, include_resource_dsl?, include_resource_dsl_module, #initialize, #introduced, #node, #process_resource_requirements, provides, provides?, #requirements, #resource_collection, #resource_updated?, #run_action, #set_updated_status, supports?, use_inline_resources, #whyrun_mode?, #whyrun_supported?
Methods included from Mixin::Provides
#provided_as, #provides, #provides?
Methods included from Mixin::DescendantsTracker
#descendants, descendants, direct_descendants, #direct_descendants, find_descendants_by_name, #find_descendants_by_name, #inherited, store_inherited
Methods included from Mixin::LazyModuleInclude
#descendants, #include, #included
Methods included from Mixin::PowershellOut
#powershell_out, #powershell_out!
Methods included from Mixin::WindowsArchitectureHelper
#assert_valid_windows_architecture!, #disable_wow64_file_redirection, #forced_32bit_override_required?, #is_i386_process_on_x86_64_windows?, #node_supports_windows_architecture?, #node_windows_architecture, #restore_wow64_file_redirection, #valid_windows_architecture?, #with_os_architecture, #wow64_architecture_override_required?, #wow64_directory
Methods included from Mixin::PowershellExec
Methods included from DSL::Powershell
Methods included from DSL::RegistryHelper
#registry_data_exists?, #registry_get_subkeys, #registry_get_values, #registry_has_subkeys?, #registry_key_exists?, #registry_value_exists?
Methods included from DSL::DataQuery
#data_bag, #data_bag_item, #search, #tagged?
Methods included from EncryptedDataBagItem::CheckEncrypted
Methods included from DSL::PlatformIntrospection
#older_than_win_2012_or_8?, #platform?, #platform_family?, #value_for_platform, #value_for_platform_family
Methods included from Mixin::NotifyingBlock
#notifying_block, #subcontext_block
Methods included from DSL::DeclareResource
#build_resource, #declare_resource, #delete_resource, #delete_resource!, #edit_resource, #edit_resource!, #find_resource, #find_resource!, #resources, #with_run_context
Constructor Details
This class inherits a constructor from Chef::Provider
Instance Method Details
#build_repo(uri, distribution, components, trusted, arch, add_src = false) ⇒ String
build complete repo text that will be written to the config
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/chef/provider/apt_repository.rb', line 322 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(" ") = [] << "arch=#{arch}" if arch << "trusted=yes" if trusted optstr = unless .empty? "[" + .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! ⇒ void
This method returns an undefined value.
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.
344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/chef/provider/apt_repository.rb', line 344 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 ⇒ String
return the specified cookbook name or the cookbook containing the resource.
151 152 153 |
# File 'lib/chef/provider/apt_repository.rb', line 151 def cookbook_name new_resource.cookbook || new_resource.cookbook_name end |
#extract_fingerprints_from_cmd(*cmd) ⇒ Array
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
118 119 120 121 122 123 124 125 |
# File 'lib/chef/provider/apt_repository.rb', line 118 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) ⇒ Boolean
determine if a cookbook file is available in the run
159 160 161 |
# File 'lib/chef/provider/apt_repository.rb', line 159 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) ⇒ void
This method returns an undefined value.
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/chef/provider/apt_repository.rb', line 249 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) ⇒ void
This method returns an undefined value.
Fetch the key using either cookbook_file or remote_file, validate it, and install it with apt-key add
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/chef/provider/apt_repository.rb', line 200 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) ⇒ void
This method returns an undefined value.
272 273 274 275 276 277 278 |
# File 'lib/chef/provider/apt_repository.rb', line 272 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.}" end |
#is_key_id?(id) ⇒ Boolean
is the provided ID a key ID from a keyserver. Looks at length and HEX only values
107 108 109 110 |
# File 'lib/chef/provider/apt_repository.rb', line 107 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) ⇒ Boolean
determine if the repository URL is a PPA
284 285 286 |
# File 'lib/chef/provider/apt_repository.rb', line 284 def is_ppa_url?(url) url.start_with?("ppa:") end |
#key_is_valid?(key) ⇒ Boolean
validate the key against the apt keystore to see if that version is expired
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/chef/provider/apt_repository.rb', line 131 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) ⇒ Symbol
Given the provided key URI determine what kind of chef resource we need to fetch the key
183 184 185 186 187 188 189 190 191 |
# File 'lib/chef/provider/apt_repository.rb', line 183 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) ⇒ String
build the apt-key command to install the keyserver
229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/chef/provider/apt_repository.rb', line 229 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 ⇒ Object
35 |
# File 'lib/chef/provider/apt_repository.rb', line 35 def load_current_resource; end |
#make_ppa_url(ppa) ⇒ String
given a PPA return a PPA URL in ppa.launchpad.net format
306 307 308 309 310 311 312 |
# File 'lib/chef/provider/apt_repository.rb', line 306 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) ⇒ Boolean
determine if there are any new keys by comparing the fingerprints of installed keys to those of the passed file
168 169 170 171 172 173 174 |
# File 'lib/chef/provider/apt_repository.rb', line 168 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 ⇒ String
determine the repository’s components:
- "components" property if defined
- "main" if "components" not defined and the repo is a PPA URL
- otherwise nothing
294 295 296 297 298 299 300 |
# File 'lib/chef/provider/apt_repository.rb', line 294 def repo_components if is_ppa_url?(new_resource.uri) && new_resource.components.empty? "main" else new_resource.components end end |