knife-mysql 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d0fd4b00f5a88f5f7e90d28913295b5444c2f30
4
+ data.tar.gz: 8221333cd8c91b41284735c3f16393a501a814d0
5
+ SHA512:
6
+ metadata.gz: b2ed1854d37e405da44073d38343341dfe0545d07bf7467068ac4d780add123d14f27bb2a72c6c30a02f4c7a8ce0f1a3fff37a8f15498ab79a61c95b896f84e1
7
+ data.tar.gz: d8a08ba68afc29b699120dede4213fe5a72c17dd561e7b27d4b7609258e9b3dcb49bdac06541c1ba721740ca4231d4b790994bf2f9b6cd3cc9e2727be1f8633d
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.sw[a-z]
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in knife-mysql.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # knife-mysql
2
+
3
+ `knife-mysql` is a [knife](http://docs.getchef.com/knife.html) plugin for working with MySQL databases on servers managed by [Chef](http://getchef.com/).
4
+
5
+ ## Installation
6
+
7
+ `gem install knife-mysql`
8
+
9
+ ## Usage
10
+
11
+ ### scp
12
+
13
+ knife mysql scp SOURCE DESTINATION (options)
14
+
15
+ The `knife mysql scp` subcommand makes it easy to copy databases between nodes.
16
+
17
+ This task will `ssh` to each node matching the [source query](http://docs.getchef.com/essentials_search.html) and `mysqldump` the requested databases to a SQL file,
18
+ then download the resulting file to your local machine, and then upload it to each node matching the destination query, finally importing the data to `mysql` and
19
+ cleaning up all the files.
20
+
21
+ #### Examples
22
+
23
+ Copying a single database from one node to another.
24
+
25
+ $ knife mysql scp name:db1.example.com name:db2.example.com --databases db_example
26
+
27
+ Copying everything from your `production` database servers into a `staging` environment.
28
+
29
+ $ knife mysql scp "role:database AND chef_environment:production" "role:database AND chef_environment:staging"
30
+
31
+ See `knife mysql scp --help` for the full list of options.
32
+
33
+ ## Contributing
34
+
35
+ Please do! Open a pull request with your changes and I'll be happy to review it.
36
+
37
+ ## License
38
+
39
+ Copyright (c) 2014 Logan Koester. Released under the MIT license. See LICENSE-MIT for details.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "knife-mysql/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'knife-mysql'
7
+ s.version = Knife::Mysql::VERSION
8
+ s.authors = ['Logan Koester']
9
+ s.email = ['logan@logankoester.com']
10
+ s.homepage = 'http://github.com/logankoester/knife-mysql'
11
+ s.summary = %q{Knife plugin to interact with MySQL on your nodes}
12
+ s.description = %q{`knife-mysql` contains a utilities working with your database nodes, such as copying databases from one node or environment to another.}
13
+
14
+ s.rubyforge_project = 'knife-mysql'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ['lib']
20
+
21
+ s.add_runtime_dependency 'chef'
22
+ s.add_runtime_dependency 'net-scp'
23
+ s.add_runtime_dependency 'ruby-progressbar'
24
+ end
@@ -0,0 +1,182 @@
1
+ require 'chef/knife'
2
+
3
+ module Knife
4
+ class MysqlScp < Chef::Knife
5
+
6
+ deps do
7
+ require 'etc'
8
+ require 'net/ssh'
9
+ require 'net/scp'
10
+ require 'ruby-progressbar'
11
+ require 'securerandom'
12
+ require 'fileutils'
13
+ require 'chef/search/query'
14
+ end
15
+
16
+ banner 'knife mysql scp SOURCE DESTINATION (options)'
17
+
18
+ option :databases,
19
+ :short => "-B database_1,database_2",
20
+ :long => "--databases database_1,database_2",
21
+ :description => "SCP several databases",
22
+ :proc => Proc.new { |names| names.split(',') },
23
+ :default => false
24
+
25
+ option :all_databases,
26
+ :short => '-A',
27
+ :long => '--all-databases',
28
+ :description => 'SCP all databases',
29
+ :boolean => true,
30
+ :default => true
31
+
32
+ option :ssh_user,
33
+ :short => '-x USERNAME',
34
+ :long => '--ssh-user USERNAME',
35
+ :description => 'The ssh username',
36
+ :default => Etc.getlogin
37
+
38
+ option :user,
39
+ :short => '-u USERNAME',
40
+ :long => '--user USERNAME',
41
+ :description => 'The MySQL username',
42
+ :default => 'root'
43
+
44
+ def run
45
+ ensure_required_args
46
+ source_query = Chef::Search::Query.new
47
+ source_query.search 'node', name_args[0] do |source_node|
48
+ dumpfile = mysqldump source_node, config
49
+ download source_node, dumpfile
50
+
51
+ destination_query = Chef::Search::Query.new
52
+ destination_query.search 'node', name_args[1] do |destination_node|
53
+ puts destination_node
54
+ upload destination_node, dumpfile
55
+ mysqlrestore destination_node, dumpfile, config
56
+ delete_remote destination_node, dumpfile
57
+ end
58
+
59
+ delete dumpfile
60
+ end
61
+ end
62
+
63
+ private
64
+ def ensure_required_args
65
+ unless name_args.size == 2
66
+ puts 'Both a source query and a destination query are required.'
67
+ show_usage
68
+ exit 1
69
+ end
70
+ end
71
+
72
+ private
73
+ def mysqldump(node, config)
74
+ Net::SSH.start node.fqdn, config[:ssh_user] do |ssh|
75
+ source_password = ui.ask_question "[#{node.name}] MySQL password (#{config[:user]}): "
76
+ dumpfile = "knife-mysql-scp-#{SecureRandom.uuid}.sql"
77
+ command = "mysqldump --single-transaction -u #{config[:user]} -p#{source_password} #{databases_to_arg(config)} > #{dumpfile}"
78
+ ssh.open_channel do |channel|
79
+ ui.info "[#{node.name}] Running #{command}"
80
+ channel.exec command do |ch, success|
81
+ unless success
82
+ ui.error "[#{node.name}] [mysqldump] An error occurred."
83
+ abort
84
+ end
85
+
86
+ channel.on_request 'exit-status' do |ch, data|
87
+ ui.info "[#{node.name}] [mysqldump] Finished!"
88
+ end
89
+ end
90
+ end
91
+ ssh.loop
92
+ return dumpfile
93
+ end
94
+ end
95
+
96
+ private
97
+ def mysqlrestore(node, dumpfile, config)
98
+ Net::SSH.start node.fqdn, config[:ssh_user] do |ssh|
99
+ source_password = ui.ask_question "MySQL password (#{config[:user]})"
100
+ command = "mysql -u #{config[:user]} -p#{source_password} < #{dumpfile}"
101
+ ssh.open_channel do |channel|
102
+ ui.info "[#{node.name}] Running #{command}"
103
+ channel.exec command do |ch, success|
104
+ unless success
105
+ ui.error "[#{node.name}] [mysql] An error occurred."
106
+ abort
107
+ end
108
+
109
+ channel.on_request 'exit-status' do |ch, data|
110
+ ui.info "[#{node.name}] [mysql] Finished!"
111
+ end
112
+ end
113
+ end
114
+ ssh.loop
115
+ end
116
+ end
117
+
118
+ private
119
+ def download(node, filename)
120
+ Net::SCP.start node.fqdn, config[:ssh_user] do |scp|
121
+ ui.info "[#{node.name}] [scp] Downloading #{filename}..."
122
+ progress = ProgressBar.create :title => filename, :format => '%a %B %p%% %t'
123
+ scp.download! filename, filename do |ch, name, sent, total|
124
+ progress.total = total
125
+ progress.progress = sent
126
+ end
127
+ progress.finish
128
+ ui.info "[#{node.name}] [scp] Finished!"
129
+ end
130
+ end
131
+
132
+ def upload(node, filename)
133
+ Net::SCP.start node.fqdn, config[:ssh_user] do |scp|
134
+ ui.info "[#{node.name}] [scp] Uploading #{filename}..."
135
+ progress = ProgressBar.create :title => filename, :format => '%a %B %p%% %t'
136
+ scp.upload! filename, filename do |ch, name, sent, total|
137
+ progress.total = total
138
+ progress.progress = sent
139
+ end
140
+ progress.finish
141
+ ui.info "[#{node.name}] [scp] Finished!"
142
+ end
143
+ end
144
+
145
+ def delete(filename)
146
+ ui.info "Deleting #{filename}..."
147
+ FileUtils.rm(filename)
148
+ ui.info "Finished!"
149
+ end
150
+
151
+ def delete_remote(node, filename)
152
+ Net::SSH.start node.fqdn, config[:ssh_user] do |ssh|
153
+ ui.info "[#{node.name}] Deleting #{filename}..."
154
+ command = "rm #{filename}"
155
+ ssh.open_channel do |channel|
156
+ channel.exec command do |ch, success|
157
+ unless success
158
+ ui.info "[#{node.name}] Failed to delete #{filename}!"
159
+ abort
160
+ end
161
+
162
+ channel.on_request 'exit-status' do |ch, data|
163
+ ui.info "[#{node.name}] Finished!"
164
+ end
165
+ end
166
+ end
167
+ ssh.loop
168
+ end
169
+ end
170
+
171
+ private
172
+ def databases_to_arg(config)
173
+ databases = config[:databases] || []
174
+ if config[:all_databases]
175
+ '--all-databases'
176
+ else
177
+ "--databases #{databases.join(' ')}"
178
+ end
179
+ end
180
+
181
+ end
182
+ end
@@ -0,0 +1,5 @@
1
+ module Knife
2
+ module Mysql
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-mysql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Logan Koester
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-scp
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-progressbar
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: "`knife-mysql` contains a utilities working with your database nodes,
56
+ such as copying databases from one node or environment to another."
57
+ email:
58
+ - logan@logankoester.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - README.md
66
+ - Rakefile
67
+ - knife-mysql.gemspec
68
+ - lib/chef/knife/mysql_scp.rb
69
+ - lib/knife-mysql/version.rb
70
+ homepage: http://github.com/logankoester/knife-mysql
71
+ licenses: []
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project: knife-mysql
89
+ rubygems_version: 2.2.2
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: Knife plugin to interact with MySQL on your nodes
93
+ test_files: []