Skip to content

Commit

Permalink
Merge pull request bfraser#60 from atward/provider-user
Browse files Browse the repository at this point in the history
grafana_user custom resource
  • Loading branch information
wyardley authored Sep 20, 2017
2 parents b91eb2c + e064b4c commit b063e51
Show file tree
Hide file tree
Showing 4 changed files with 293 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,22 @@ completely missing. Example usage:
```puppet
grafana::plugin{'grafana-simple-json-datasource':}
```

##### `grafana::user`

Creates and manages a global grafana user via the API.

```puppet
grafana_user { 'username':
grafana_url => 'http://localhost:3000',
grafana_user => 'admin',
grafana_password => '5ecretPassw0rd',
full_name => 'John Doe',
password => 'Us3r5ecret',
email => '[email protected]',
}
```

## Limitations

This module has been tested on Ubuntu 14.04, using each of the 'archive', 'docker'
Expand Down
161 changes: 161 additions & 0 deletions lib/puppet/provider/grafana_user/grafana.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
require 'json'

require File.expand_path(File.join(File.dirname(__FILE__), '..', 'grafana'))

Puppet::Type.type(:grafana_user).provide(:grafana, parent: Puppet::Provider::Grafana) do
desc 'Support for Grafana users'

defaultfor kernel: 'Linux'

def users
response = send_request('GET', '/api/users')
if response.code != '200'
raise format('Fail to retrieve users (HTTP response: %s/%s)', response.code, response.body)
end

begin
users = JSON.parse(response.body)

users.map { |x| x['id'] }.map do |id|
response = send_request 'GET', format('/api/users/%s', id)
if response.code != '200'
raise format('Fail to retrieve user %d (HTTP response: %s/%s)', id, response.code, response.body)
end

user = JSON.parse(response.body)
{
id: id,
name: user['login'],
full_name: user['name'],
email: user['email'],
theme: user['theme'],
password: nil,
is_admin: user['isGrafanaAdmin'] ? :true : :false
}
end
rescue JSON::ParserError
raise format('Fail to parse response: %s', response.body)
end
end

def user
@user = users.find { |x| x[:name] == resource[:name] } unless @user
@user
end

attr_writer :user

def name
user[:name]
end

def name=(value)
resource[:name] = value
save_user
end

def full_name
user[:full_name]
end

def full_name=(value)
resource[:full_name] = value
save_user
end

def email
user[:email]
end

def email=(value)
resource[:email] = value
save_user
end

def theme
user[:theme]
end

def theme=(value)
resource[:theme] = value
save_user
end

def password
user[:password]
end

def password=(value)
resource[:password] = value
save_user
end

# rubocop:disable Style/PredicateName
def is_admin
user[:is_admin]
end

def is_admin=(value)
resource[:is_admin] = value
save_user
end
# rubocop:enable Style/PredicateName

def save_user
data = {
login: resource[:name],
name: resource[:full_name],
email: resource[:email],
password: resource[:password],
theme: resource[:theme],
isGrafanaAdmin: (resource[:is_admin] == :true)
}

if user.nil?
response = send_request('POST', '/api/admin/users', data)
else
data[:id] = user[:id]
send_request 'PUT', format('/api/admin/users/%s/password', user[:id]), password: data.delete(:password)
send_request 'PUT', format('/api/admin/users/%s/permissions', user[:id]), isGrafanaAdmin: data.delete(:isGrafanaAdmin)
response = send_request 'PUT', format('/api/users/%s', user[:id]), data
end

if response.code != '200'
raise format('Failed to create user %s (HTTP response: %s/%s)', resource[:name], response.code, response.body)
end
self.user = nil
end

def delete_user
response = send_request 'DELETE', format('/api/admin/users/%s', user[:id])

if response.code != '200'
raise format('Failed to delete user %s (HTTP response: %s/%s', resource[:name], response.code, response.body)
end
self.user = nil
end

def create
save_user
end

def destroy
delete_user
end

def exists?
user
end
end
66 changes: 66 additions & 0 deletions lib/puppet/type/grafana_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
Puppet::Type.newtype(:grafana_user) do
@doc = 'Manage users in Grafana'

ensurable

newparam(:name, namevar: true) do
desc 'The username of the user.'
end

newparam(:grafana_url) do
desc 'The URL of the Grafana server'
defaultto ''

validate do |value|
unless value =~ %r{^https?://}
raise ArgumentError, format('%s is not a valid URL', value)
end
end
end

newparam(:grafana_user) do
desc 'The username for the Grafana server'
end

newparam(:grafana_password) do
desc 'The password for the Grafana server'
end

newparam(:full_name) do
desc 'The full name of the user.'
end

newproperty(:password) do
desc 'The password for the user'
end

newproperty(:email) do
desc 'The email for the user'
end

newproperty(:theme) do
desc 'The theme for the user'
end

newproperty(:is_admin) do
desc 'Whether the user is a grafana admin'
newvalues(:true, :false)
defaultto :false
end

autorequire(:service) do
'grafana-server'
end
end
50 changes: 50 additions & 0 deletions spec/unit/puppet/type/grafana_user_type_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'

describe Puppet::Type.type(:grafana_user) do
let(:guser) do
described_class.new name: 'test', full_name: 'Mr tester', password: 't3st', grafana_url: 'http://example.com/'
end

context 'when setting parameters' do
it "fails if grafana_url isn't HTTP-based" do
expect do
described_class.new name: 'test', grafana_url: 'example.com'
end.to raise_error(Puppet::Error, %r{not a valid URL})
end

# rubocop:disable RSpec/MultipleExpectations
it 'accepts valid parameters' do
expect(guser[:name]).to eq('test')
expect(guser[:full_name]).to eq('Mr tester')
expect(guser[:password]).to eq('t3st')
expect(guser[:grafana_url]).to eq('http://example.com/')
end
it 'autorequires the grafana-server for proper ordering' do
catalog = Puppet::Resource::Catalog.new
service = Puppet::Type.type(:service).new(name: 'grafana-server')
catalog.add_resource service
catalog.add_resource guser

relationship = guser.autorequire.find do |rel|
(rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == guser.to_s)
end
expect(relationship).to be_a Puppet::Relationship
end
it 'does not autorequire the service it is not managed' do
catalog = Puppet::Resource::Catalog.new
catalog.add_resource guser
expect(guser.autorequire).to be_empty
end
end
end

0 comments on commit b063e51

Please sign in to comment.