]> git.openstreetmap.org Git - chef.git/blob - cookbooks/mysql/libraries/mysql.rb
769df807e0eda0c37c6c71c60ad90cbd595df34b
[chef.git] / cookbooks / mysql / libraries / mysql.rb
1 require "chef/mixin/command"
2 require "rexml/document"
3
4 class Chef
5   class MySQL
6     include Chef::Mixin::Command
7
8     USER_PRIVILEGES = [
9       :select, :insert, :update, :delete, :create, :drop, :reload,
10       :shutdown, :process, :file, :grant, :references, :index, :alter,
11       :show_db, :super, :create_tmp_table, :lock_tables, :execute,
12       :repl_slave, :repl_client, :create_view, :show_view, :create_routine,
13       :alter_routine, :create_user, :event, :trigger, :create_tablespace
14     ]
15
16     DATABASE_PRIVILEGES = [
17       :select, :insert, :update, :delete, :create, :drop, :grant,
18       :references, :index, :alter, :create_tmp_table, :lock_tables,
19       :create_view, :show_view, :create_routine, :alter_routine,
20       :execute, :event, :trigger
21     ]
22
23     def execute(options)
24       # Create argument array
25       args = []
26
27       # Work out how to authenticate
28       if options[:user]
29         args.push("--username=#{options[:user]}")
30         args.push("--password=#{options[:password]}") if options[:password]
31       else
32         args.push("--defaults-file=/etc/mysql/debian.cnf")
33       end
34
35       # Build the other arguments
36       args.push("--execute=\"#{options[:command]}\"") if options[:command]
37
38       # Get the database to use
39       database = options[:database] || "mysql"
40
41       # Build the command to run
42       command = "/usr/bin/mysql #{args.join(' ')} #{database}"
43
44       # Escape backticks in the command
45       command.gsub!(/`/, "\\\\`")
46
47       # Run the command
48       run_command(:command => command, :user => "root", :group => "root")
49     end
50
51     def query(sql, options = {})
52       # Get the database to use
53       database = options[:database] || "mysql"
54
55       # Construct the command string
56       command = "/usr/bin/mysql --defaults-file=/etc/mysql/debian.cnf --xml --execute='#{sql}' #{database}"
57
58       # Run the query
59       status, stdout, stderr = output_of_command(command, :user => "root", :group => "root")
60       handle_command_failures(status, "STDOUT: #{stdout}\nSTDERR: #{stderr}", :output_on_failure => true)
61
62       # Parse the output
63       document = REXML::Document.new(stdout)
64
65       # Create
66       records = []
67
68       # Loop over the rows in the result set
69       document.root.each_element("/resultset/row") do |row|
70         # Create a record
71         record = {}
72
73         # Loop over the fields, adding them to the record
74         row.each_element("field") do |field|
75           name = field.attributes["name"].downcase
76           value = field.text
77
78           record[name.to_sym] = value
79         end
80
81         # Add the record to the record list
82         records << record
83       end
84
85       # Return the record list
86       records
87     end
88
89     def users
90       @users ||= query("SELECT * FROM user").each_with_object({}) do |user, users|
91         name = "'#{user[:user]}'@'#{user[:host]}'"
92
93         users[name] = USER_PRIVILEGES.each_with_object({}) do |privilege, privileges|
94           privileges[privilege] = user["#{privilege}_priv".to_sym] == "Y"
95         end
96       end
97     end
98
99     def databases
100       @databases ||= query("SHOW databases").each_with_object({}) do |database, databases|
101         databases[database[:database]] = {
102           :permissions => {}
103         }
104       end
105
106       query("SELECT * FROM db").each do |record|
107         next unless database = @databases[record[:db]]
108
109         user = "'#{record[:user]}'@'#{record[:host]}'"
110
111         database[:permissions][user] = DATABASE_PRIVILEGES.each_with_object([]) do |privilege, privileges|
112           privileges << privilege if record["#{privilege}_priv".to_sym] == "Y"
113         end
114       end
115
116       @databases
117     end
118
119     def canonicalise_user(user)
120       local, host = user.split("@")
121
122       host = "%" unless host
123
124       local = "'#{local}'" unless local =~ /^'.*'$/
125       host = "'#{host}'" unless host =~ /^'.*'$/
126
127       "#{local}@#{host}"
128     end
129
130     def privilege_name(privilege)
131       case privilege
132       when :grant
133         "GRANT OPTION"
134       when :create_tmp_table
135         "CREATE TEMPORARY TABLES"
136       else
137         privilege.to_s.upcase.tr("_", " ")
138       end
139     end
140   end
141 end