From ff5ddd334cad2b4dcfc7b720296c0af097f19ebd Mon Sep 17 00:00:00 2001 From: David Schmitt Date: Mon, 11 Sep 2017 16:14:40 +0100 Subject: [PATCH] Update to use the puppet-resource_api gem --- Gemfile | 1 + lib/puppet/provider/apt_key2/apt_key2.rb | 14 +- lib/puppet/type/apt_key2.rb | 11 +- lib/puppet_x/apt_key/resource_api.rb | 309 ----------------------- 4 files changed, 16 insertions(+), 319 deletions(-) delete mode 100644 lib/puppet_x/apt_key/resource_api.rb diff --git a/Gemfile b/Gemfile index a9f0161..70a6af7 100644 --- a/Gemfile +++ b/Gemfile @@ -62,6 +62,7 @@ gem 'puppet', *location_for(ENV['PUPPET_GEM_VERSION']) gem 'facter', *location_for(ENV['FACTER_GEM_VERSION']) if ENV['FACTER_GEM_VERSION'] gem 'hiera', *location_for(ENV['HIERA_GEM_VERSION']) if ENV['HIERA_GEM_VERSION'] +gem 'puppet-resource_api', git: 'https://github.com/puppetlabs/puppet-resource_api', ref: 'master' # Evaluate Gemfile.local if it exists if File.exists? "#{__FILE__}.local" diff --git a/lib/puppet/provider/apt_key2/apt_key2.rb b/lib/puppet/provider/apt_key2/apt_key2.rb index aa21c6b..b5066f0 100644 --- a/lib/puppet/provider/apt_key2/apt_key2.rb +++ b/lib/puppet/provider/apt_key2/apt_key2.rb @@ -1,12 +1,14 @@ -require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x', 'apt_key', 'resource_api.rb')) - -require 'open-uri' require 'net/ftp' +require 'open-uri' +require 'puppet/resource_api' require 'tempfile' -register_provider('apt_key2') do - commands apt_key: 'apt-key' - commands gpg: '/usr/bin/gpg' +class Puppet::Provider::AptKey2::AptKey2 + + def initialize + @apt_key_cmd = Puppet::ResourceApi::Command.new 'apt-key' + @gpg_cmd = Puppet::ResourceApi::Command.new '/usr/bin/gpg' + end def canonicalize(resources) resources.each do |r| diff --git a/lib/puppet/type/apt_key2.rb b/lib/puppet/type/apt_key2.rb index eb67117..9ad4538 100644 --- a/lib/puppet/type/apt_key2.rb +++ b/lib/puppet/type/apt_key2.rb @@ -1,6 +1,7 @@ -require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'puppet_x', 'apt_key', 'resource_api.rb')) +require 'puppet/resource_api' -register_type(name: 'apt_key2', +Puppet::ResourceApi.register_type( + name: 'apt_key2', docs: <<-EOS, This type provides Puppet with the capabilities to manage GPG keys needed by apt to perform package validation. Apt has it's own GPG keyring that can @@ -36,7 +37,7 @@ register_type(name: 'apt_key2', server: { type: 'Pattern[/\A((hkp|http|https):\/\/)?([a-z\d])([a-z\d-]{0,61}\.)+[a-z\d]+(:\d{2,5})?$/]', docs: 'The key server to fetch the key from based on the ID. It can either be a domain name or url.', - behaviour: :read_only, + behaviour: :parameter, default: :'keyserver.ubuntu.com', }, options: { @@ -88,4 +89,6 @@ register_type(name: 'apt_key2', autorequires: { file: '$source', # will evaluate to the value of the `source` attribute package: 'apt', - }) + }, + features: ['canonicalize'], +) diff --git a/lib/puppet_x/apt_key/resource_api.rb b/lib/puppet_x/apt_key/resource_api.rb deleted file mode 100644 index 17fcc99..0000000 --- a/lib/puppet_x/apt_key/resource_api.rb +++ /dev/null @@ -1,309 +0,0 @@ -require 'pathname' - -module Puppet::SimpleResource - class TypeShim - attr_reader :values - - def initialize(title, resource_hash) - # internalize and protect - needs to go deeper - @values = resource_hash.dup - # "name" is a privileged key - @values[:name] = title - @values.freeze - end - - def to_resource - ResourceShim.new(@values) - end - - def name - values[:name] - end - end - - class ResourceShim - attr_reader :values - - def initialize(resource_hash) - @values = resource_hash.dup.freeze # whatevs - end - - def title - values[:name] - end - - def prune_parameters(*args) - # puts "not pruning #{args.inspect}" if args.length > 0 - self - end - - def to_manifest - (["apt_key { #{values[:name].inspect}: "] + values.keys.select { |k| k != :name }.collect { |k| " #{k} => #{values[k].inspect}," } + ['}']).join("\n") - end - end -end - -class Logger - def initialize(typename) - @typename = typename - end - - def format_titles(titles) - "#{@typename}[#{[titles].flatten.compact.join(", ")}]" - end - - def setup_context(titles, message = nil) - @context = format_titles(titles) - @context += ": #{message}: " if message - end - - [:debug, :info, :notice, :warning, :err, :critical, :alert, :emerg].each do |method| - define_method(method) do |*args| - if args.length == 1 - puts "#{method}: #{@context || @typename}: #{args.last}" - elsif args.length == 2 - resources = format_titles(args.first) - puts "#{method}: #{resources}: #{args.last}" - else - puts "#{method}: #{args.map(&inspect).join(", ")}" - end - end - end - - [:creating, :updating, :deleting, :failing].each do |method| - define_method(method) do |titles, message: method.to_s.capitalize, &block| - setup_context(titles, message) - begin - debug("Start") - yield - notice("Finished in x.yz seconds") - rescue - err("Failed after x.yz seconds") - raise - ensure - @context = nil - end - end - end - - def processing(titles, is, should, message: 'Processing', &block) - setup_context(titles, message) - begin - debug("Changing #{is.inspect} to #{should.inspect}") - yield - notice("Changed from #{is.inspect} to #{should.inspect} in x.yz seconds") - rescue - err("Failed changing #{is.inspect} to #{should.inspect} after x.yz seconds") - raise - ensure - @context = nil - end - end - - def attribute_changed(titles, is, should, message: nil) - setup_context(titles, message) - notice("Changed from #{is.inspect} to #{should.inspect}") - end -end - -def register_type(definition) - Puppet::Type.newtype(definition[:name].to_sym) do - @docs = definition[:docs] - has_namevar = false - namevar_name = nil - - def initialize(attributes) - $stderr.puts "A: #{attributes.inspect}" - attributes = attributes.to_hash if attributes.is_a? Puppet::Resource - $stderr.puts "B: #{attributes.inspect}" - attributes = self.class.canonicalize([attributes])[0] - $stderr.puts "C: #{attributes.inspect}" - super(attributes) - end - - definition[:attributes].each do |name, options| - # puts "#{name}: #{options.inspect}" - - # TODO: using newparam everywhere would suppress change reporting - # that would allow more fine-grained reporting through logger, - # but require more invest in hooking up the infrastructure to emulate existing data - param_or_property = if options[:behaviour] == :read_only || options[:behaviour] == :namevar - :newparam - else - :newproperty - end - send(param_or_property, name.to_sym) do - unless options[:type] - fail("#{definition[:name]}.#{name} has no type") - end - - if options[:docs] - desc "#{options[:docs]} (a #{options[:type]}" - else - warn("#{definition[:name]}.#{name} has no docs") - end - - if options[:behaviour] == :namevar - puts 'setting namevar' - isnamevar - has_namevar = true - namevar_name = name - end - - # read-only values do not need type checking, but can have default values - if options[:behaviour] != :read_only - # TODO: this should use Pops infrastructure to avoid hardcoding stuff, and enhance type fidelity - # validate do |v| - # type = Puppet::Pops::Types::TypeParser.singleton.parse(options[:type]).normalize - # if type.instance?(v) - # return true - # else - # inferred_type = Puppet::Pops::Types::TypeCalculator.infer_set(value) - # error_msg = Puppet::Pops::Types::TypeMismatchDescriber.new.describe_mismatch("#{DEFINITION[:name]}.#{name}", type, inferred_type) - # raise Puppet::ResourceError, error_msg - # end - # end - - if options.has_key? :default - defaultto options[:default] - end - - case options[:type] - when 'String' - # require any string value - newvalue // do - end - when 'Boolean' - ['true', 'false', :true, :false, true, false].each do |v| - newvalue v do - end - end - - munge do |v| - case v - when 'true', :true - true - when 'false', :false - false - else - v - end - end - when 'Integer' - newvalue /^\d+$/ do - end - munge do |v| - Puppet::Pops::Utils.to_n(v) - end - when 'Float', 'Numeric' - newvalue Puppet::Pops::Patterns::NUMERIC do - end - munge do |v| - Puppet::Pops::Utils.to_n(v) - end - when 'Enum[present, absent]' - newvalue :absent do - end - newvalue :present do - end - when 'Variant[Pattern[/\A(0x)?[0-9a-fA-F]{8}\Z/], Pattern[/\A(0x)?[0-9a-fA-F]{16}\Z/], Pattern[/\A(0x)?[0-9a-fA-F]{40}\Z/]]' - # the namevar needs to be a Parameter, which only has newvalue*s* - newvalues(/\A(0x)?[0-9a-fA-F]{8}\Z/, /\A(0x)?[0-9a-fA-F]{16}\Z/, /\A(0x)?[0-9a-fA-F]{40}\Z/) - when 'Optional[String]' - newvalue :undef do - end - newvalue // do - end - when 'Variant[Stdlib::Absolutepath, Pattern[/\A(https?|ftp):\/\//]]' - # TODO: this is wrong, but matches original implementation - [/^\//, /\A(https?|ftp):\/\//].each do |v| - newvalue v do - end - end - when 'Pattern[/\A((hkp|http|https):\/\/)?([a-z\d])([a-z\d-]{0,61}\.)+[a-z\d]+(:\d{2,5})?$/]' - newvalues(/\A((hkp|http|https):\/\/)?([a-z\d])([a-z\d-]{0,61}\.)+[a-z\d]+(:\d{2,5})?$/) - when /^(Enum|Optional|Variant)/ - fail("Datatype #{$1} is not yet supported in this prototype") - else - fail("Datatype #{options[:type]} is not yet supported in this prototype") - end - end - end - end - - define_singleton_method(:instances) do - puts 'instances' - # klass = Puppet::Type.type(:api) - # force autoloading of the provider - autoloaded_provider = provider(name) - get.collect do |resource_hash| - Puppet::SimpleResource::TypeShim.new(resource_hash[namevar_name], resource_hash) - end - end - - define_method(:retrieve) do - puts 'retrieve' - result = Puppet::Resource.new(self.class, title) - current_state = self.class.get.find { |h| h[namevar_name] == title } - - if current_state - current_state.each do |k, v| - result[k]=v - end - else - result[:ensure] = :absent - end - - puts 'retrieve done' - - @rapi_current_state = current_state - result - end - - def flush - puts 'flush' - target_state = self.class.canonicalize([Hash[@parameters.collect { |k, v| [k, v.value] }]]).first - if @rapi_current_state != target_state - self.class.set({title => @rapi_current_state}, {title => target_state}, false) - else - puts 'no changes' - end - end - - define_singleton_method(:logger) do - return Logger.new(definition[:name]) - end - - def self.commands(*args) - args.each do |command_group| - command_group.each do |command_name, command| - puts "registering command: #{command_name}, using #{command}" - define_singleton_method(command_name) do |*args| - puts "spawn([#{command.to_s}, #{command.to_s}], #{args.inspect})" - # TODO: capture output to debug stream - p = Process.spawn([command, command], *args) - Process.wait(p) - unless $?.exitstatus == 0 - raise Puppet::ResourceError, "#{command} failed with exit code #{$?.exitstatus}" - end - end - - define_singleton_method("#{command_name}_lines") do |*args| - puts "capture3([#{command.to_s}, #{command.to_s}], #{args.inspect})" - stdin_str, stderr_str, status = Open3.capture3([command, command], *args) - unless status.exitstatus == 0 - raise Puppet::ResourceError, "#{command} failed with exit code #{$?.exitstatus}" - end - stdin_str.split("\n") - end - end - end - end - end -end - -def register_provider(typename, &block) - type = Puppet::Type.type(typename.to_sym) - type.instance_eval &block - # require'pry';binding.pry -end -- 2.45.2