Add puppet/archive module, required for newer puppet/rabbitmq
[mirror/dsa-puppet.git] / 3rdparty / modules / archive / lib / puppet / provider / archive / ruby.rb
diff --git a/3rdparty/modules/archive/lib/puppet/provider/archive/ruby.rb b/3rdparty/modules/archive/lib/puppet/provider/archive/ruby.rb
new file mode 100644 (file)
index 0000000..53b065f
--- /dev/null
@@ -0,0 +1,246 @@
+begin
+  require 'puppet_x/bodeco/archive'
+  require 'puppet_x/bodeco/util'
+rescue LoadError
+  require 'pathname' # WORK_AROUND #14073 and #7788
+  archive = Puppet::Module.find('archive', Puppet[:environment].to_s)
+  raise(LoadError, "Unable to find archive module in modulepath #{Puppet[:basemodulepath] || Puppet[:modulepath]}") unless archive
+  require File.join archive.path, 'lib/puppet_x/bodeco/archive'
+  require File.join archive.path, 'lib/puppet_x/bodeco/util'
+end
+
+require 'securerandom'
+require 'tempfile'
+
+# This provider implements a simple state-machine. The following attempts to #
+# document it. In general, `def adjective?` implements a [state], while `def
+# verb` implements an {action}.
+# Some states are more complex, as they might depend on other states, or trigger
+# actions. Since this implements an ad-hoc state-machine, many actions or states
+# have to guard themselves against being called out of order.
+#
+# [exists?]
+#   |
+#   v
+# [extracted?] -> no -> [checksum?]
+#    |
+#    v
+#   yes
+#    |
+#    v
+# [path.exists?] -> no -> {cleanup}
+#    |                    |    |
+#    v                    v    v
+# [checksum?]            yes. [extracted?] && [cleanup?]
+#                              |
+#                              v
+#                            {destroy}
+#
+# Now, with [exists?] defined, we can define [ensure]
+# But that's just part of the standard puppet provider state-machine:
+#
+# [ensure] -> absent -> [exists?] -> no.
+#   |                     |
+#   v                     v
+#  present               yes
+#   |                     |
+#   v                     v
+# [exists?]            {destroy}
+#   |
+#   v
+# {create}
+#
+# Here's how we would extend archive for an `ensure => latest`:
+#
+#  [exists?] -> no -> {create}
+#    |
+#    v
+#   yes
+#    |
+#    v
+#  [ttl?] -> expired -> {destroy} -> {create}
+#    |
+#    v
+#  valid.
+#
+
+Puppet::Type.type(:archive).provide(:ruby) do
+  optional_commands aws: 'aws'
+  defaultfor feature: :microsoft_windows
+  attr_reader :archive_checksum
+
+  def exists?
+    return checksum? unless extracted?
+    return checksum? if File.exist? archive_filepath
+    cleanup
+    true
+  end
+
+  def create
+    transfer_download(archive_filepath) unless checksum?
+    extract
+    cleanup
+  end
+
+  def destroy
+    FileUtils.rm_f(archive_filepath) if File.exist?(archive_filepath)
+  end
+
+  def archive_filepath
+    resource[:path]
+  end
+
+  def tempfile_name
+    if resource[:checksum] == 'none'
+      "#{resource[:filename]}_#{SecureRandom.base64}"
+    else
+      "#{resource[:filename]}_#{resource[:checksum]}"
+    end
+  end
+
+  def creates
+    if resource[:extract] == :true
+      extracted? ? resource[:creates] : 'archive not extracted'
+    else
+      resource[:creates]
+    end
+  end
+
+  def creates=(_value)
+    extract
+  end
+
+  def checksum
+    resource[:checksum] || (resource[:checksum] = remote_checksum if resource[:checksum_url])
+  end
+
+  def remote_checksum
+    PuppetX::Bodeco::Util.content(
+      resource[:checksum_url],
+      username: resource[:username],
+      password: resource[:password],
+      cookie: resource[:cookie],
+      proxy_server: resource[:proxy_server],
+      proxy_type: resource[:proxy_type],
+      insecure: resource[:allow_insecure]
+    )[%r{\b[\da-f]{32,128}\b}i]
+  end
+
+  # Private: See if local archive checksum matches.
+  # returns boolean
+  def checksum?(store_checksum = true)
+    return false unless File.exist? archive_filepath
+    return true  if resource[:checksum_type] == :none
+
+    archive = PuppetX::Bodeco::Archive.new(archive_filepath)
+    archive_checksum = archive.checksum(resource[:checksum_type])
+    @archive_checksum = archive_checksum if store_checksum
+    checksum == archive_checksum
+  end
+
+  def cleanup
+    return unless extracted? && resource[:cleanup] == :true
+    Puppet.debug("Cleanup archive #{archive_filepath}")
+    destroy
+  end
+
+  def extract
+    return unless resource[:extract] == :true
+    raise(ArgumentError, 'missing archive extract_path') unless resource[:extract_path]
+    PuppetX::Bodeco::Archive.new(archive_filepath).extract(
+      resource[:extract_path],
+      custom_command: resource[:extract_command],
+      options: resource[:extract_flags],
+      uid: resource[:user],
+      gid: resource[:group]
+    )
+  end
+
+  def extracted?
+    resource[:creates] && File.exist?(resource[:creates])
+  end
+
+  def transfer_download(archive_filepath)
+    if resource[:temp_dir] && !File.directory?(resource[:temp_dir])
+      raise Puppet::Error, "Temporary directory #{resource[:temp_dir]} doesn't exist"
+    end
+    tempfile = Tempfile.new(tempfile_name, resource[:temp_dir])
+
+    temppath = tempfile.path
+    tempfile.close!
+
+    case resource[:source]
+    when %r{^(puppet)}
+      puppet_download(temppath)
+    when %r{^(http|ftp)}
+      download(temppath)
+    when %r{^file}
+      uri = URI(resource[:source])
+      FileUtils.copy(Puppet::Util.uri_to_path(uri), temppath)
+    when %r{^s3}
+      s3_download(temppath)
+    when nil
+      raise(Puppet::Error, 'Unable to fetch archive, the source parameter is nil.')
+    else
+      raise(Puppet::Error, "Source file: #{resource[:source]} does not exists.") unless File.exist?(resource[:source])
+      FileUtils.copy(resource[:source], temppath)
+    end
+
+    # conditionally verify checksum:
+    if resource[:checksum_verify] == :true && resource[:checksum_type] != :none
+      archive = PuppetX::Bodeco::Archive.new(temppath)
+      actual_checksum = archive.checksum(resource[:checksum_type])
+      if actual_checksum != checksum
+        raise(Puppet::Error, "Download file checksum mismatch (expected: #{checksum} actual: #{actual_checksum})")
+      end
+    end
+
+    move_file_in_place(temppath, archive_filepath)
+  end
+
+  def move_file_in_place(from, to)
+    # Ensure to directory exists.
+    FileUtils.mkdir_p(File.dirname(to))
+    FileUtils.mv(from, to)
+  end
+
+  def download(filepath)
+    PuppetX::Bodeco::Util.download(
+      resource[:source],
+      filepath,
+      username: resource[:username],
+      password: resource[:password],
+      cookie: resource[:cookie],
+      proxy_server: resource[:proxy_server],
+      proxy_type: resource[:proxy_type],
+      insecure: resource[:allow_insecure]
+    )
+  end
+
+  def puppet_download(filepath)
+    PuppetX::Bodeco::Util.puppet_download(
+      resource[:source],
+      filepath
+    )
+  end
+
+  def s3_download(path)
+    params = [
+      's3',
+      'cp',
+      resource[:source],
+      path
+    ]
+    params += resource[:download_options] if resource[:download_options]
+
+    aws(params)
+  end
+
+  def optional_switch(value, option)
+    if value
+      option.map { |flags| flags % value }
+    else
+      []
+    end
+  end
+end