From 15caac0562239331bda4760dda66490b1a08ca7b Mon Sep 17 00:00:00 2001 From: William Yardley Date: Tue, 19 Sep 2017 23:17:11 -0700 Subject: [PATCH] Create grafana_plugin resource type, and create grafana::plugins passthrough --- README.md | 18 ++++-- .../provider/grafana_plugin/grafana_cli.rb | 56 +++++++++++++++++++ lib/puppet/type/grafana_plugin.rb | 31 ++++++++++ manifests/init.pp | 12 +++- manifests/plugin.pp | 10 ---- spec/acceptance/grafana_plugin_spec.rb | 38 +++++++++++++ spec/classes/grafana_spec.rb | 15 +++++ spec/defines/plugin_spec.rb | 20 ------- .../grafana_plugin/grafana_cli_spec.rb | 53 ++++++++++++++++++ spec/unit/puppet/type/grafana_plugin_spec.rb | 16 ++++++ 10 files changed, 232 insertions(+), 37 deletions(-) create mode 100644 lib/puppet/provider/grafana_plugin/grafana_cli.rb create mode 100644 lib/puppet/type/grafana_plugin.rb delete mode 100644 manifests/plugin.pp create mode 100644 spec/acceptance/grafana_plugin_spec.rb delete mode 100644 spec/defines/plugin_spec.rb create mode 100644 spec/unit/puppet/provider/grafana_plugin/grafana_cli_spec.rb create mode 100644 spec/unit/puppet/type/grafana_plugin_spec.rb diff --git a/README.md b/README.md index d979916b0..833099497 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,11 @@ Grafana repositories are enabled on your host. If true, the official Grafana repositories will be enabled. If false, the module assumes you are managing your own package repository and will not set one up for you. Defaults to true. +##### `plugins` + +Hash. This is a passthrough to call `create_resources()` on the +`grafana_plugin` resource type. + ##### `package_name` The name of the package managed with the 'package' install method. Defaults to @@ -333,7 +338,7 @@ Example: #### Custom Types and Providers -The module includes two custom types: `grafana_dashboard` and `grafana_datasource` +The module includes several custom types: ##### `grafana_dashboard` @@ -380,14 +385,15 @@ from the browser, or `proxy` to send requests via grafana. Authentication is optional, as is `database`; additional `json_data` can be provided to allow custom configuration options. -##### `grafana::plugin` +##### `grafana_plugin` -There exists a custom defined resource which wraps grafana-cli to install -plugins. Deinstallation isn't supported right now, also docker support -completely missing. Example usage: +An example is provided for convenience; for more details, please view the +puppet strings docs. ```puppet -grafana::plugin{'grafana-simple-json-datasource':} +grafana_plugin { 'grafana-simple-json-datasource': + ensure => present, +} ``` ##### `grafana::user` diff --git a/lib/puppet/provider/grafana_plugin/grafana_cli.rb b/lib/puppet/provider/grafana_plugin/grafana_cli.rb new file mode 100644 index 000000000..153e1b1ce --- /dev/null +++ b/lib/puppet/provider/grafana_plugin/grafana_cli.rb @@ -0,0 +1,56 @@ +Puppet::Type.type(:grafana_plugin).provide(:grafana_cli) do + has_command(:grafana_cli, 'grafana-cli') do + is_optional + end + + defaultfor feature: :posix + + mk_resource_methods + + def self.all_plugins + plugins = [] + grafana_cli('plugins', 'ls').split(%r{\n}).each do |line| + next unless line =~ %r{^(\S+)\s+@\s+((?:\d\.).+)\s*$} + name = Regexp.last_match(1) + version = Regexp.last_match(2) + Puppet.debug("Found grafana plugin #{name} #{version}") + plugins.push(name) + end + plugins.sort + end + + def self.instances + resources = [] + all_plugins.each do |name| + plugin = { + ensure: :present, + name: name + } + resources << new(plugin) if plugin[:name] + end + resources + end + + def self.prefetch(resources) + plugins = instances + resources.keys.each do |name| + if (provider = plugins.find { |plugin| plugin.name == name }) + resources[name].provider = provider + end + end + end + + def exists? + @property_hash[:ensure] == :present + end + + def create + grafana_cli('plugins', 'install', resource[:name]) + @property_hash[:ensure] = :present + end + + def destroy + grafana_cli('plugins', 'uninstall', resource[:name]) + @property_hash[:ensure] = :absent + end +end diff --git a/lib/puppet/type/grafana_plugin.rb b/lib/puppet/type/grafana_plugin.rb new file mode 100644 index 000000000..4f2d95572 --- /dev/null +++ b/lib/puppet/type/grafana_plugin.rb @@ -0,0 +1,31 @@ +Puppet::Type.newtype(:grafana_plugin) do + desc <<-DESC +manages grafana plugins + +@example Install a grafana plugin + grafana_plugin { 'grafana-simple-json-datasource': } + +@example Uninstall a grafana plugin + grafana_plugin { 'grafana-simple-json-datasource': + ensure => absent, + } + +@example Show resources + $ puppet resource grafana_plugin +DESC + + ensurable do + defaultto(:present) + newvalue(:present) do + provider.create + end + newvalue(:absent) do + provider.destroy + end + end + + newparam(:name, namevar: true) do + desc 'The name of the plugin to enable' + newvalues(%r{^\S+$}) + end +end diff --git a/manifests/init.pp b/manifests/init.pp index 467ffc5c5..c4f020343 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -62,6 +62,10 @@ # Set to 'testing' to install beta versions # Defaults to stable. # +# [*plugins*] +# A hash of plugins to be passed to `create_resources`, wraps around the +# `grafana_plugin` resource. +# # === Examples # # class { '::grafana': @@ -84,7 +88,8 @@ $repo_name = $::grafana::params::repo_name, $rpm_iteration = $::grafana::params::rpm_iteration, $service_name = $::grafana::params::service_name, - $version = $::grafana::params::version + $version = $::grafana::params::version, + $plugins = {} ) inherits grafana::params { # validate parameters here @@ -99,4 +104,9 @@ Class['grafana::install'] -> Class['grafana::config'] ~> Class['grafana::service'] + + create_resources(grafana_plugin, $plugins) + + Grafana_Plugin <| |> ~> Class['grafana::service'] + } diff --git a/manifests/plugin.pp b/manifests/plugin.pp deleted file mode 100644 index ff10a9079..000000000 --- a/manifests/plugin.pp +++ /dev/null @@ -1,10 +0,0 @@ -# defined resource to add plugins to grafana via CLI -# this won't work with the docker installation method -define grafana::plugin( - String $plugin = $title, -){ - exec{"install ${plugin}": - command => "/usr/bin/grafana-cli plugins install ${plugin}", - creates => "/var/lib/grafana/plugins/${plugin}", - } -} diff --git a/spec/acceptance/grafana_plugin_spec.rb b/spec/acceptance/grafana_plugin_spec.rb new file mode 100644 index 000000000..49bdde3d9 --- /dev/null +++ b/spec/acceptance/grafana_plugin_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper_acceptance' + +describe 'grafana_plugin' do + context 'create plugin resource' do + it 'runs successfully' do + pp = <<-EOS + class { 'grafana':} + grafana_plugin { 'grafana-simple-json-datasource': } + EOS + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + + it 'has the plugin' do + shell('grafana-cli plugins ls') do |r| + expect(r.stdout).to match(%r{grafana-simple-json-datasource}) + end + end + end + context 'destroy plugin resource' do + it 'runs successfully' do + pp = <<-EOS + class { 'grafana':} + grafana_plugin { 'grafana-simple-json-datasource': + ensure => absent, + } + EOS + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + + it 'does not have the plugin' do + shell('grafana-cli plugins ls') do |r| + expect(r.stdout).not_to match(%r{grafana-simple-json-datasource}) + end + end + end +end diff --git a/spec/classes/grafana_spec.rb b/spec/classes/grafana_spec.rb index c66c95879..46b870f93 100644 --- a/spec/classes/grafana_spec.rb +++ b/spec/classes/grafana_spec.rb @@ -69,6 +69,21 @@ end end + context 'with some plugins passed in' do + let(:params) do + { + plugins: + { + 'grafana-wizzle' => { 'ensure' => 'present' }, + 'grafana-woozle' => { 'ensure' => 'absent' } + } + } + end + + it { is_expected.to contain_grafana_plugin('grafana-wizzle').with(ensure: 'present') } + it { is_expected.to contain_grafana_plugin('grafana-woozle').with(ensure: 'absent').that_notifies('Class[grafana::service]') } + end + context 'with parameter install_method is set to repo' do let(:params) do { diff --git a/spec/defines/plugin_spec.rb b/spec/defines/plugin_spec.rb deleted file mode 100644 index 6b1fb9950..000000000 --- a/spec/defines/plugin_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe 'grafana::plugin', type: :define do - on_supported_os.each do |os, facts| - context "on #{os} " do - let :facts do - facts - end - - let :title do - 'grafana-simple-json-datasource' - end - - context 'with all defaults' do - it { is_expected.to compile.with_all_deps } - it { is_expected.to contain_exec("install #{title}") } - end - end - end -end diff --git a/spec/unit/puppet/provider/grafana_plugin/grafana_cli_spec.rb b/spec/unit/puppet/provider/grafana_plugin/grafana_cli_spec.rb new file mode 100644 index 000000000..3c6914a08 --- /dev/null +++ b/spec/unit/puppet/provider/grafana_plugin/grafana_cli_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +provider_class = Puppet::Type.type(:grafana_plugin).provider(:grafana_cli) +describe provider_class do + let(:resource) do + Puppet::Type::Grafana_plugin.new( + name: 'grafana-wizzle' + ) + end + let(:provider) { provider_class.new(resource) } + + describe '#instances' do + # rubocop:disable Layout/TrailingWhitespace + provider_class.expects(:grafana_cli).with('plugins', 'ls').returns <<-EOT +installed plugins: +grafana-simple-json-datasource @ 1.3.4 +jdbranham-diagram-panel @ 1.4.0 + +Restart grafana after installing plugins . + +EOT + # rubocop:enable Layout/TrailingWhitespace + instances = provider_class.instances + it 'has the right number of instances' do + expect(instances.size).to eq(2) + end + + it 'has the correct names' do + names = instances.map(&:name) + expect(names).to include('grafana-simple-json-datasource', 'jdbranham-diagram-panel') + end + + it 'does not match if there are no plugins' do + provider_class.expects(:grafana_cli).with('plugins', 'ls').returns <<-EOT + +Restart grafana after installing plugins . + +EOT + instances = provider_class.instances + expect(provider.exists?).to eq(false) + end + end + + it '#create' do + provider.expects(:grafana_cli).with('plugins', 'install', 'grafana-wizzle') + provider.create + end + + it '#destroy' do + provider.expects(:grafana_cli).with('plugins', 'uninstall', 'grafana-wizzle') + provider.destroy + end +end diff --git a/spec/unit/puppet/type/grafana_plugin_spec.rb b/spec/unit/puppet/type/grafana_plugin_spec.rb new file mode 100644 index 000000000..29e7ea50f --- /dev/null +++ b/spec/unit/puppet/type/grafana_plugin_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' +describe Puppet::Type.type(:grafana_plugin) do + let(:plugin) do + Puppet::Type.type(:grafana_plugin).new(name: 'grafana-whatsit') + end + + it 'accepts a plugin name' do + plugin[:name] = 'plugin-name' + expect(plugin[:name]).to eq('plugin-name') + end + it 'requires a name' do + expect do + Puppet::Type.type(:grafana_plugin).new({}) + end.to raise_error(Puppet::Error, 'Title or name must be provided') + end +end