diff --git a/lib/puppet/provider/grafana_dashboard_permission/grafana.rb b/lib/puppet/provider/grafana_dashboard_permission/grafana.rb index 463ab55..0e1be74 100644 --- a/lib/puppet/provider/grafana_dashboard_permission/grafana.rb +++ b/lib/puppet/provider/grafana_dashboard_permission/grafana.rb @@ -1,249 +1,249 @@ # frozen_string_literal: true require 'json' require File.expand_path(File.join(File.dirname(__FILE__), '..', 'grafana')) Puppet::Type.type(:grafana_dashboard_permission).provide(:grafana, parent: Puppet::Provider::Grafana) do desc 'Support for Grafana dashboard permissions' defaultfor kernel: 'Linux' def grafana_api_path resource[:grafana_api_path] end def set_current_organization response = send_request 'POST', format('%s/user/using/%s', grafana_api_path, organization[:id]) return if response.code == '200' raise format('Failed to switch to org %s (HTTP response: %s/%s)', organization[:id], response.code, response.body) end def raise_on_error(code, message) raise message if code != '200' end def parse_response(data) JSON.parse(data) rescue JSON::ParserError raise format('Fail to parse response: %s', response.body) end def map_organizations(ids) ids.map do |id| response = send_request 'GET', format('%s/orgs/%s', grafana_api_path, id) raise_on_error(response.code, format('Failed to retrieve organization %d (HTTP response: %s/%s)', id, response.code, response.body)) organization = parse_response(response.body) { id: organization['id'], name: organization['name'] } end end def organizations response = send_request('GET', format('%s/orgs', grafana_api_path)) raise_on_error(response.code, format('Fail to retrieve organizations (HTTP response: %s/%s)', response.code, response.body)) organizations = JSON.parse(response.body) map_organizations(organizations.map { |x| x['id'] }) end def organization return @organization if @organization org = resource[:organization] key = org.is_a?(Numeric) || org.match(%r{/^[0-9]*$/}) ? :id : :name @organization = organizations.find { |x| x[key] == org } end def map_teams(teams) teams['teams'].map do |team| { id: team['id'], name: team['name'], organization: team['orgId'], membercount: team['membercount'], permission: team['permission'], email: team['email'] } end end def teams raise(format('Unknown Organization: %s', resource[:organization])) unless organization set_current_organization response = send_request('GET', format('%s/teams/search', grafana_api_path)) raise_on_error(response.code, format('Fail to retrieve teams (HTTP response: %s/%s)', response.code, response.body)) teams = parse_response(response.body) map_teams(teams) end def team @team ||= teams.find { |x| x[:name] == resource[:team] } end def send_users_request raise(format('Unknown Organization: %s', resource[:organization])) unless organization set_current_organization response = send_request('GET', format('%s/org/users', grafana_api_path)) raise_on_error(response.code, format('Fail to retrieve users (HTTP response: %s/%s)', response.code, response.body)) response.body end def users users = parse_response(send_users_request) users.map do |user| { id: user['userId'], name: user['login'], organization: user['orgId'], role: user['role'] } end end def user @user ||= users.find { |x| x[:name] == resource[:user] } end def dashboards set_current_organization search_path = { query: resource[:dashboard], type: 'dash-db' } response = send_request('GET', format('%s/search', grafana_api_path), nil, search_path) raise_on_error(response.code, format('Fail to retrieve dashboards (HTTP response: %s/%s)', response.code, response.body)) dashboards = parse_response(response.body) dashboards.map do |dashboard| { id: dashboard['id'], name: dashboard['title'] } end end def dashboard @dashboard ||= dashboards.find { |x| x[:name] == resource[:dashboard] } end def permissions return @permissions if @permissions raise(format('Unknown dashboard: %s', resource[:dashboard])) unless dashboard response = send_request('GET', format('%s/dashboards/id/%s/permissions', grafana_api_path, dashboard[:id])) raise_on_error(response.code, format('Failed to retrieve permissions on dashboard (HTTP response: %s/%s)', response.code, response.body)) permissions = parse_response(response.body) @permissions = permissions.map do |permission| { dashboardId: permission['dashboardId'], userId: permission['userId'], user: permission['userLogin'], teamId: permission['teamId'], team: permission['team'], permissionId: permission['permission'], permission: permission['permissionName'], dashboard: permission['title'], isFolder: permission['isFolder'], inherited: permission['inherited'] } end end def team_permission raise(format('Unknown team: %s for organaization: %s', resource[:team], resource[:organization])) unless team @team_permission ||= permissions.find { |x| x[:teamId] == team[:id] } end def user_permission raise(format('Unknown user: %s for organaization: %s', resource[:user], resource[:organization])) unless user @user_permission ||= permissions.find { |x| x[:userId] == user[:id] } end def permission resource[:user] ? user_permission[:permission] : team_permission[:permission] end def permission=(value) case value when 'View' resource[:permission] = 1 when 'Edit' resource[:permission] = 2 when 'Admin' resource[:permission] = 4 end save_permission end def new_permission key = resource[:user] ? :userId : :teamId subject_id = resource[:user] ? user[:id] : team[:id] permission = case resource[:permission] when :View 1 when :Edit 2 when :Admin 4 end raise(format('User or Team must exist')) unless subject_id { key => subject_id, 'permission' => permission } end def remove_unneeded_permissions(obj) obj.delete_if { |k| k['dashboardId'] == -1 } new_target = resource[:user] || resource[:team] new_type = resource[:user] ? :user : :team obj.delete_if { |k| k[new_type] == new_target } obj.delete_if { |k| k[:teamId].zero? && k[:userId].zero? } end def existing_permissions perms = remove_unneeded_permissions(permissions) perms.map do |perm| target = perm[:userId].zero? ? perm[:teamId] : perm[:userId] type = perm[:userId].zero? ? :teamId : :userId { type => target, :permission => perm[:permissionId] } end end - def permission_data(destroy=false) + def permission_data(destroy = false) raise format('Unknown dashboard: %s', resource[:dashboard]) unless dashboard endpoint = format('%s/dashboards/id/%s/permissions', grafana_api_path, dashboard[:id]) final_permissions = destroy ? { items: existing_permissions } : { items: existing_permissions + [new_permission] } ['POST', endpoint, final_permissions] end def save_permission response = send_request(*permission_data) raise_on_error(response.code, format('Failed to update membership %s, (HTTP response: %s/%s)', resource, response.code, response.body)) end def create save_permission end def destroy response = send_request(*permission_data(true)) raise_on_error(response.code, format('Failed to update membership %s, (HTTP response: %s/%s)', resource, response.code, response.body)) end def exists? raise('user or team parameter must be present') unless resource[:user] || resource[:team] resource[:user] ? user_permission : team_permission end end diff --git a/lib/puppet/provider/grafana_membership/grafana.rb b/lib/puppet/provider/grafana_membership/grafana.rb index a64886d..c76b71c 100644 --- a/lib/puppet/provider/grafana_membership/grafana.rb +++ b/lib/puppet/provider/grafana_membership/grafana.rb @@ -1,252 +1,241 @@ # frozen_string_literal: true require 'json' require File.expand_path(File.join(File.dirname(__FILE__), '..', 'grafana')) Puppet::Type.type(:grafana_membership).provide(:grafana, parent: Puppet::Provider::Grafana) do desc 'Support for Grafana memberships' defaultfor kernel: 'Linux' def map_organizations(ids) ids.map do |id| response = send_request 'GET', format('%s/orgs/%s', resource[:grafana_api_path], id) raise_on_error(response.code, format('Failed to retrieve organization %d (HTTP response: %s/%s)', id, response.code, response.body)) organization = parse_response(response.body) { id: organization['id'], name: organization['name'] } end end def organizations response = send_request('GET', format('%s/orgs', resource[:grafana_api_path])) raise_on_error(response.code, format('Fail to retrieve organizations (HTTP response: %s/%s)', response.code, response.body)) organizations = JSON.parse(response.body) map_organizations(organizations.map { |x| x['id'] }) end def organization return @organization if @organization org = resource[:membership_type] == :organization ? resource[:target_name] : resource[:organization] key = org.is_a?(Numeric) || org.match(%r{/^[0-9]*$/}) ? :id : :name @organization = organizations.find { |x| x[key] == org } - # return @organization if @organization - - # raise format('Unknown organization: %s', org) end def map_teams(teams) teams['teams'].map do |team| { id: team['id'], name: team['name'], organization: team['orgId'], membercount: team['membercount'], permission: team['permission'], email: team['email'] } end end def teams return {} unless organization set_current_organization response = send_request('GET', format('%s/teams/search', resource[:grafana_api_path])) raise_on_error(response.code, format('Fail to retrieve teams (HTTP response: %s/%s)', response.code, response.body)) teams = parse_response(response.body) map_teams(teams) end def team - # return @team if @team - @team ||= teams.find { |x| x[:name] == resource[:target_name] } - # return @team if @team - - # raise format('Unknown team: %s', resource[:target_name]) end def map_team_members(members) members.map do |member| { id: member['userId'], target_name: member['teamId'], organization: member['orgId'] } end end def team_members response = send_request('GET', format('%s/teams/%s/members', resource[:grafana_api_path], @team[:id])) raise_on_error(response.code, format('Fail to retrieve teams (HTTP response: %s/%s)', response.code, response.body)) members = parse_response(response.body) members ? map_team_members(members) : [] end def team_member @team_member ||= team_members.find { |x| x[:id] == @user[:id] } end def raise_on_error(code, message) raise message if code != '200' end def grafana_api_path resource[:grafana_api_path] end def parse_response(data) JSON.parse(data) rescue JSON::ParserError raise format('Fail to parse response: %s', response.body) end def send_users_request return '[]' unless organization set_current_organization response = send_request('GET', format('%s/org/users', grafana_api_path)) raise_on_error(response.code, format('Fail to retrieve users (HTTP response: %s/%s)', response.code, response.body)) response.body end def map_users(users) users.map do |user| { id: user['userId'], name: user['login'], organization: user['orgId'], role: user['role'] } end end def users users = parse_response(send_users_request) map_users(users) end def user @user ||= users.find { |x| x[:name] == resource[:user_name] } - # return @user if @user - - # raise format('Unknown user: %s', resource[:user_name]) end def set_current_organization response = send_request 'POST', format('%s/user/using/%s', resource[:grafana_api_path], organization[:id]) return if response.code == '200' raise format('Failed to switch to org %s (HTTP response: %s/%s)', organization[:id], response.code, response.body) end def role user unless @user return @user[:role] if @user nil end def role=(value) resource[:role] = value save_membership end def save_membership send(format('save_membership_%s', resource[:membership_type])) end def check_org_team_and_user_exist raise(format('Unknown organization: %s', resource[:organization])) unless organization set_current_organization raise('Unknown team or user') unless team && user end def save_membership_team check_org_team_and_user_exist endpoint = format('%s/teams/%s/members', resource[:grafana_api_path], @team[:id]) response = send_request('POST', endpoint, userId: @user[:id]) raise_on_error(response.code, format('Failed to update membership %s, (HTTP response: %s/%s)', resource, response.code, response.body)) end def setup_save_mem_org_data verb = 'POST' endpoint = format('%s/org/users', resource[:grafana_api_path]) request_data = { role: resource[:role], loginOrEmail: resource[:user_name] } if exists? verb = 'PATCH' endpoint = format('%s/org/users/%s', resource[:grafana_api_path], @user[:id]) end [verb, endpoint, request_data] end def save_membership_organization set_current_organization response = send_request(*setup_save_mem_org_data) raise_on_error(response.code, format('Failed to update membership %s, (HTTP response: %s/%s)', resource, response.code, response.body)) end def create save_membership end def setup_destroy_data if resource[:membership_type] == :organization endpoint = format('%s/org/users/%s', resource[:grafana_api_path], @user[:id]) else # team team unless @team endpoint = format('%s/teams/%s/members/%s', resource[:grafana_api_path], @team[:id], @user[:id]) end ['DELETE', endpoint] end def destroy_team_membership return unless user && organization && team endpoint = format('%s/teams/%s/members/%s', resource[:grafana_api_path], @team[:id], @user[:id]) response = send_request('DELETE', endpoint) raise_on_error(response.code, format('Failed to delete team membership (HTTP response: %s/%s)', response.code, response.body)) end def destroy_organization_membership return unless user && organization endpoint = format('%s/org/users/%s', resource[:grafana_api_path], @user[:id]) response = send_request('DELETE', endpoint) raise_on_error(response.code, format('Failed to delete organization membership (HTTP response: %s/%s)', response.code, response.body)) end def destroy set_current_organization resource[:membership_type] == :organization ? destroy_organization_membership : destroy_team_membership end def user_in_organization? organization return true if @user && @organization && @user[:organization] == @organization[:id] false end def user_in_team? team_member if team return true if @user && @team && @team_member false end def exists? user resource[:membership_type] == :organization ? user_in_organization? : user_in_team? end end diff --git a/lib/puppet/provider/grafana_team/grafana.rb b/lib/puppet/provider/grafana_team/grafana.rb index 07abd32..3421a4e 100644 --- a/lib/puppet/provider/grafana_team/grafana.rb +++ b/lib/puppet/provider/grafana_team/grafana.rb @@ -1,238 +1,235 @@ # frozen_string_literal: true require 'json' require File.expand_path(File.join(File.dirname(__FILE__), '..', 'grafana')) Puppet::Type.type(:grafana_team).provide(:grafana, parent: Puppet::Provider::Grafana) do desc 'Support for Grafana permissions' defaultfor kernel: 'Linux' def raise_on_error(code, message) raise message if code != '200' end def parse_response(data) JSON.parse(data) rescue JSON::ParserError raise format('Fail to parse response: %s', response.body) end def map_organizations(ids) ids.map do |id| response = send_request 'GET', format('%s/orgs/%s', resource[:grafana_api_path], id) raise_on_error(response.code, format('Failed to retrieve organization %d (HTTP response: %s/%s)', id, response.code, response.body)) organization = parse_response(response.body) { id: organization['id'], name: organization['name'] } end end def organizations response = send_request('GET', format('%s/orgs', resource[:grafana_api_path])) raise_on_error(response.code, format('Fail to retrieve organizations (HTTP response: %s/%s)', response.code, response.body)) organizations = JSON.parse(response.body) map_organizations(organizations.map { |x| x['id'] }) end def organization return @organization if @organization org = resource[:organization] || resource[:target_name] key = org.is_a?(Numeric) || org.match(%r{/^[0-9]*$/}) ? :id : :name @organization = organizations.find { |x| x[key] == org } - # return @organization if @organization - - # raise format('Unknown organization: %s', org) end def map_teams(teams) teams['teams'].map do |team| { id: team['id'], name: team['name'], organization: team['orgId'], membercount: team['membercount'], permission: team['permission'], email: team['email'] } end end def teams return [] unless organization set_current_organization response = send_request('GET', format('%s/teams/search', resource[:grafana_api_path])) raise_on_error(response.code, format('Fail to retrieve teams (HTTP response: %s/%s)', response.code, response.body)) teams = parse_response(response.body) map_teams(teams) end def team @team ||= teams.find { |x| x[:name] == resource[:name] } end def map_preferences(preferences) { theme: preferences['theme'], home_dashboard: preferences['homeDashboardId'], timezone: preferences['timezone'] } end def preferences team unless @team return if @preferences response = send_request('GET', format('%s/teams/%s/preferences', resource[:grafana_api_path], @team[:id])) raise_on_error(response.code, format('Fail to retrieve teams (HTTP response: %s/%s)', response.code, response.body)) preferences = parse_response(response.body) @preferences = map_preferences(preferences) end def setup_save_preferences_data endpoint = format('%s/teams/%s/preferences', resource[:grafana_api_path], @team[:id]) dash = get_dashboard(resource[:home_dashboard]) request_data = { theme: resource[:theme], homeDashboardId: dash[:id], timezone: resource[:timezone] } ['PUT', endpoint, request_data] end def save_preferences team unless @team set_current_organization setup_save_preferences_data response = send_request(*setup_save_preferences_data) # TODO: Raise on error? return if response.code == '200' || response.code == '412' raise format('Failed to update team %s, (HTTP response: %s/%s)', resource, response.code, response.body) end def set_current_organization response = send_request 'POST', format('%s/user/using/%s', resource[:grafana_api_path], organization[:id]) return if response.code == '200' raise format('Failed to switch to org %s (HTTP response: %s/%s)', organization[:id], response.code, response.body) end def home_dashboard preferences unless @preferences dash = get_dashboard(@preferences[:home_dashboard]) return dash[:name] if dash nil end def home_dashboard=(value) resource[:home_dashboard] = value save_preferences end def setup_search_path(ident) if ident.is_a?(Numeric) || ident.match(%r{/^[0-9]*$/}) { dashboardIds: ident, type: 'dash-db' } else { query: ident, type: 'dash-db' } end end def get_dashboard(ident) set_current_organization return { id: 0, name: 'Default' } if ident == 0 # rubocop:disable Style/NumericPredicate search_path = setup_search_path(ident) response = send_request('GET', format('%s/search', resource[:grafana_api_path]), nil, search_path) raise_on_error(response.code, format('Fail to retrieve dashboars (HTTP response: %s/%s)', response.code, response.body)) dashboard = parse_response(response.body) format_dashboard(dashboard) end def format_dashboard(dashboard) return { id: 0, name: 'Default' } unless dashboard.first { id: dashboard.first['id'], name: dashboard.first['title'] } end def theme preferences unless @preferences return @preferences[:theme] if @preferences nil end def theme=(value) resource[:theme] = value save_preferences end def timezone preferences unless @preferences return @preferences[:timezone] if @preferences nil end def timezone=(value) resource[:timezone] = value save_preferences end def setup_save_team_data verb = 'POST' endpoint = format('%s/teams', resource[:grafana_api_path]) request_data = { name: resource[:name], email: resource[:email] } if exists? verb = 'PUT' endpoint = format('%s/teams/%s', resource[:grafana_api_path], @team[:id]) end [verb, endpoint, request_data] end def save_team set_current_organization response = send_request(*setup_save_team_data) raise_on_error(response.code, format('Failed to update team %s, (HTTP response: %s/%s)', resource, response.code, response.body)) end def create save_team save_preferences end def destroy return unless team response = send_request('DELETE', format('%s/teams/%s', resource[:grafana_api_path], @team[:id])) return unless response.code != '200' raise Puppet::Error, format('Failed to delete team %s (HTTP response: %s/%s)', resource, response.code, response.body) end def exists? team return true if @team && @team[:name] == resource[:name] false end end