server_settings 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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +77 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +202 -0
- data/Rakefile +2 -0
- data/example/sample.rb +53 -0
- data/lib/capistrano/server_settings.rb +8 -0
- data/lib/server_settings.rb +113 -0
- data/lib/server_settings/capistrano.rb +23 -0
- data/lib/server_settings/database.rb +29 -0
- data/lib/server_settings/database_config.rb +46 -0
- data/lib/server_settings/host.rb +16 -0
- data/lib/server_settings/host_collection.rb +31 -0
- data/lib/server_settings/railtie.rb +10 -0
- data/lib/server_settings/role.rb +42 -0
- data/lib/server_settings/role_db.rb +59 -0
- data/lib/server_settings/tasks.rb +9 -0
- data/lib/server_settings/tasks/db.rake +98 -0
- data/lib/server_settings/version.rb +3 -0
- data/server_settings.gemspec +25 -0
- data/spec/lib/server_settings/database_config_spec.rb +97 -0
- data/spec/lib/servers_config_spec.rb +300 -0
- data/spec/spec_helper.rb +91 -0
- data/spec/test-yaml/role1.yml +3 -0
- data/spec/test-yaml/role2.yml +3 -0
- metadata +133 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'server_settings'
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module ServersGroup
|
5
|
+
def self.extend(configuration)
|
6
|
+
configuration.load do
|
7
|
+
Capistrano::Configuration.instance.load do
|
8
|
+
def load_servers(pattern)
|
9
|
+
ServerSettings.load_config_dir(pattern)
|
10
|
+
ServerSettings.each_role do |role, hosts|
|
11
|
+
role role.to_sym, *hosts.map(&:host)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if Capistrano::Configuration.instance
|
21
|
+
Capistrano::ServersGroup.extend(Capistrano::Configuration.instance)
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class ServerSettings
|
2
|
+
class Database
|
3
|
+
attr_accessor :name, :group, :master, :backup, :slaves, :settings
|
4
|
+
def initialize(name, group)
|
5
|
+
@name = name
|
6
|
+
@group = group
|
7
|
+
end
|
8
|
+
|
9
|
+
def config(role)
|
10
|
+
host = send(role)
|
11
|
+
case host
|
12
|
+
when Array
|
13
|
+
host.map { |h| settings.merge(:host => h) }
|
14
|
+
when String
|
15
|
+
settings.merge(:host => host)
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def host
|
22
|
+
@master
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_slave?
|
26
|
+
!! @slaves and not @slaves.empty?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class ServerSettings
|
2
|
+
class DatabaseConfig
|
3
|
+
def self.slave_name(name, idx)
|
4
|
+
name + "_slave#{idx}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.generate_database_config(db_role, with_slave:true)
|
8
|
+
configuration = {}
|
9
|
+
return configuration unless ServerSettings.databases
|
10
|
+
|
11
|
+
ServerSettings.databases.each do |db|
|
12
|
+
next unless db.config(db_role)
|
13
|
+
|
14
|
+
role_db_config = db.config(db_role)
|
15
|
+
stringify_keys!(role_db_config) if defined? Rails
|
16
|
+
|
17
|
+
if db.name == "default"
|
18
|
+
if defined? Rails
|
19
|
+
configuration[Rails.env] = role_db_config
|
20
|
+
else
|
21
|
+
configuration.merge!(role_db_config)
|
22
|
+
end
|
23
|
+
elsif db.group
|
24
|
+
configuration[db.group] ||= {}
|
25
|
+
configuration[db.group][db.name] = role_db_config
|
26
|
+
else
|
27
|
+
configuration[db.name] = role_db_config
|
28
|
+
end
|
29
|
+
|
30
|
+
if with_slave && db.has_slave?
|
31
|
+
db.config(:slaves).each.with_index(1) do |config, idx|
|
32
|
+
# スレーブのバックアップホストは、マスターのバックアップホストと同じにする
|
33
|
+
config[:host] = db.backup if db_role == :backup
|
34
|
+
stringify_keys!(config) if defined? Rails
|
35
|
+
configuration[slave_name(db.name, idx)] = config
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
configuration
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.stringify_keys!(config)
|
43
|
+
config.keys.each { |key| config[key.to_s] = config.delete(key) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class ServerSettings
|
2
|
+
|
3
|
+
class HostCollection < Array
|
4
|
+
def initialize(hosts, role_config)
|
5
|
+
@role_config = role_config
|
6
|
+
unless hosts.kind_of?(Array)
|
7
|
+
raise InvalidHosts, "hosts: #{hosts} is not array"
|
8
|
+
end
|
9
|
+
|
10
|
+
hosts.each do |host_exp|
|
11
|
+
self.push Host.parse(host_exp)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_format(format)
|
16
|
+
self.map do |host|
|
17
|
+
replacemap = @role_config
|
18
|
+
replacemap['%host'] = host.host
|
19
|
+
replacemap['%port'] = host.port if host.port
|
20
|
+
replacemap.inject(format) do |string, mapping|
|
21
|
+
string.gsub(*mapping)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Errors
|
27
|
+
class InvalidHosts < StandardError ; end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class ServerSettings
|
2
|
+
|
3
|
+
class Role
|
4
|
+
attr_reader :name, :config
|
5
|
+
|
6
|
+
def initialize(role, config)
|
7
|
+
@name = role
|
8
|
+
@config = load(config)
|
9
|
+
end
|
10
|
+
|
11
|
+
def load(config)
|
12
|
+
role_options = config.keys.select{|s| s != "hosts"}
|
13
|
+
@settings = Hash[*role_options.map do |option_name|
|
14
|
+
[ "%#{option_name}", config[option_name].to_s]
|
15
|
+
end.flatten]
|
16
|
+
if config.has_key?("hosts")
|
17
|
+
config["hosts"]= HostCollection.new(config["hosts"], @settings)
|
18
|
+
end
|
19
|
+
config
|
20
|
+
end
|
21
|
+
|
22
|
+
def hosts
|
23
|
+
@config["hosts"] if @config.has_key?("hosts")
|
24
|
+
end
|
25
|
+
|
26
|
+
def host
|
27
|
+
hosts.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(name, *args, &block)
|
31
|
+
key = name.to_s
|
32
|
+
return nil unless @config.has_key? key
|
33
|
+
@config[key]
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_format(format)
|
37
|
+
hosts.with_format(format)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class ServerSettings
|
2
|
+
|
3
|
+
class RoleDB < Role
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def databases
|
7
|
+
@config.keys.select { |a| a.kind_of?(String) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_db(name, config, group = nil)
|
11
|
+
db = Database.new(name, group)
|
12
|
+
db.master = config[:master]
|
13
|
+
db.backup = config[:backup]
|
14
|
+
db.slaves = config[:slaves]
|
15
|
+
db.settings = config_params(config)
|
16
|
+
return db
|
17
|
+
end
|
18
|
+
|
19
|
+
def find(db_name)
|
20
|
+
select { |s| s.name == db_name }.first
|
21
|
+
end
|
22
|
+
|
23
|
+
def each
|
24
|
+
default_db = build_db("default", @config)
|
25
|
+
yield default_db
|
26
|
+
|
27
|
+
databases.each do |db_name|
|
28
|
+
config = @config[db_name]
|
29
|
+
|
30
|
+
if config && config.has_key?(:master)
|
31
|
+
db = build_db(db_name, config)
|
32
|
+
db.settings = default_db.settings.merge(db.settings)
|
33
|
+
yield db
|
34
|
+
|
35
|
+
else # this is group section
|
36
|
+
group_name = db_name
|
37
|
+
config.map do |nest_db_name, nest_config|
|
38
|
+
db = build_db(nest_db_name, nest_config, group_name)
|
39
|
+
db.settings = default_db.settings.merge(db.settings)
|
40
|
+
yield db
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def hosts
|
47
|
+
find_all
|
48
|
+
end
|
49
|
+
|
50
|
+
def config_params(config)
|
51
|
+
exclude_settings = [ :master, :backup, :slaves ]
|
52
|
+
config_keys = config.keys.select do |s|
|
53
|
+
s.is_a?(Symbol) and not exclude_settings.include?(s)
|
54
|
+
end
|
55
|
+
Hash[*config_keys.map {|s| [s, config[s]] }.flatten]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
namespace :server_settings do
|
2
|
+
namespace :db do
|
3
|
+
namespace :create do
|
4
|
+
require "colorize"
|
5
|
+
|
6
|
+
def build_new_db_configs
|
7
|
+
ServerSettings::DatabaseConfig.generate_database_config(:master).each_with_object([]) do |(_, config), new_db_configs|
|
8
|
+
client = Mysql2::Client.new(username: config["username"], password: config["password"], host: config["host"])
|
9
|
+
if client.query("SHOW DATABASES LIKE '#{ config['database'] }'").to_a.blank?
|
10
|
+
new_db_configs << [config, client]
|
11
|
+
else
|
12
|
+
client.close
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def show_new_databases(new_db_configs)
|
18
|
+
if new_db_configs.blank?
|
19
|
+
puts "There is no new databases."
|
20
|
+
else
|
21
|
+
new_db_configs.each do |config, _|
|
22
|
+
puts format("* %-15s: new database '%s'", config["host"], config["database"]).colorize(:green)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def close_connections(new_db_configs)
|
28
|
+
new_db_configs.each { |_, client| client.close }
|
29
|
+
end
|
30
|
+
|
31
|
+
def confirm_and_execute(prompt, &block)
|
32
|
+
print "#{ prompt } (yes/N): "
|
33
|
+
if $stdin.gets.strip == "yes"
|
34
|
+
block.call
|
35
|
+
else
|
36
|
+
puts "Operation canceled."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def perform_show_status(new_db_configs)
|
41
|
+
show_new_databases(new_db_configs)
|
42
|
+
close_connections(new_db_configs)
|
43
|
+
end
|
44
|
+
|
45
|
+
def perform_create_databases(new_db_configs)
|
46
|
+
show_new_databases(new_db_configs)
|
47
|
+
confirm_and_execute "Are you sure you want to execute above?" do
|
48
|
+
new_db_configs.each do |config, client|
|
49
|
+
command = "CREATE DATABASE IF NOT EXISTS #{ config['database'] }"
|
50
|
+
puts "Executing '#{ command }' on #{ config['host'] }"
|
51
|
+
client.query(command)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
ensure
|
55
|
+
close_connections(new_db_configs)
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "Show status of new databases not created yet"
|
59
|
+
task :status => :environment do
|
60
|
+
new_db_configs = build_new_db_configs
|
61
|
+
perform_show_status(new_db_configs)
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "Confirm and execute CREATE DATABASE for each new database"
|
65
|
+
task :execute => :environment do
|
66
|
+
new_db_configs = build_new_db_configs
|
67
|
+
next if new_db_configs.blank?
|
68
|
+
perform_create_databases(new_db_configs)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Confirm and execute Drop DATABASE for all database"
|
73
|
+
task :drop => :environment do
|
74
|
+
all_db_configs = build_all_db_configs
|
75
|
+
next if all_db_configs.blank?
|
76
|
+
perform_drop_databases(all_db_configs)
|
77
|
+
end
|
78
|
+
|
79
|
+
def build_all_db_configs
|
80
|
+
ServerSettings::DatabaseConfig.generate_database_config(:master).each_with_object([]) do |(_, config), all_db_configs|
|
81
|
+
client = Mysql2::Client.new(username: config["username"], password: config["password"], host: config["host"])
|
82
|
+
all_db_configs << [config, client]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def perform_drop_databases(all_db_configs)
|
87
|
+
confirm_and_execute "Are you sure you want to execute above?" do
|
88
|
+
all_db_configs.each do |config, client|
|
89
|
+
command = "DROP DATABASE IF EXISTS #{ config['database'] }"
|
90
|
+
puts "Executing '#{ command }' on #{ config['host'] }"
|
91
|
+
client.query(command)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
ensure
|
95
|
+
close_connections(all_db_configs)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'server_settings/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "server_settings"
|
8
|
+
spec.version = ServerSettings::VERSION
|
9
|
+
spec.authors = ["Osamu MATSUMOTO", "Contributers"]
|
10
|
+
spec.email = ["osamu.matsumoto@gmail.com"]
|
11
|
+
spec.summary = %q{Server Configuration logic}
|
12
|
+
spec.description = %q{Server Configuration logic for any where}
|
13
|
+
spec.homepage = "https://github.com/monsterstrike/server_settings"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "colorize"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'server_settings'
|
3
|
+
|
4
|
+
describe "ServerSettings::DatabaseConfig" do
|
5
|
+
let (:config1) do
|
6
|
+
yaml_text = <<-EOF
|
7
|
+
---
|
8
|
+
databases: &default
|
9
|
+
:adapter: mysql2
|
10
|
+
:encoding: utf8
|
11
|
+
:reconnect: true
|
12
|
+
:database: server_settings
|
13
|
+
:pool: 5
|
14
|
+
:username: monsterstrike
|
15
|
+
:password: monsterstrike
|
16
|
+
:master: localhost
|
17
|
+
user:
|
18
|
+
<<: *default
|
19
|
+
:database: server_settings_user
|
20
|
+
has_slave:
|
21
|
+
<<: *default
|
22
|
+
:database: server_settings_has_slave
|
23
|
+
:slaves: [ localhost ]
|
24
|
+
archive:
|
25
|
+
archive_me:
|
26
|
+
<<: *default
|
27
|
+
has_backup:
|
28
|
+
<<: *default
|
29
|
+
:backup: 'backup.com'
|
30
|
+
no_backup:
|
31
|
+
<<: *default
|
32
|
+
EOF
|
33
|
+
end
|
34
|
+
before do
|
35
|
+
filepath = "config.yml"
|
36
|
+
allow(IO).to receive(:read).with(filepath).and_return(config1)
|
37
|
+
allow(File).to receive(:mtime).with(filepath).and_return(Time.now)
|
38
|
+
ServerSettings.load_config("config.yml")
|
39
|
+
end
|
40
|
+
after do
|
41
|
+
ServerSettings.destroy
|
42
|
+
end
|
43
|
+
describe ".slave_name" do
|
44
|
+
it { expect(ServerSettings::DatabaseConfig.slave_name("hoge", 1)).to eq "hoge_slave1" }
|
45
|
+
end
|
46
|
+
describe ".generate_database_config" do
|
47
|
+
context "check configuration" do
|
48
|
+
let(:database_config) { database_config = ServerSettings::DatabaseConfig.generate_database_config(:master) }
|
49
|
+
it "contain `user` key" do
|
50
|
+
expect(database_config["user"].class).to eq Hash
|
51
|
+
end
|
52
|
+
context "when has slave" do
|
53
|
+
it "contain slave key" do
|
54
|
+
expect(database_config["has_slave_slave1"].class).to eq Hash
|
55
|
+
end
|
56
|
+
end
|
57
|
+
context "when has group" do
|
58
|
+
it "create nest configuration" do
|
59
|
+
expect(database_config["archive"]["archive_me"].class).to eq Hash
|
60
|
+
end
|
61
|
+
end
|
62
|
+
context "when no slaves options" do
|
63
|
+
it "does not exist slave key" do
|
64
|
+
no_slave_config = ServerSettings::DatabaseConfig.generate_database_config(:master, with_slave: false)
|
65
|
+
expect(no_slave_config.has_key? "has_slave_slave1").to be false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
context "when use backup role" do
|
69
|
+
let(:backup_config) { ServerSettings::DatabaseConfig.generate_database_config :backup }
|
70
|
+
it "change host to backup" do
|
71
|
+
expect(backup_config["has_backup"][:host]).to eq "backup.com"
|
72
|
+
end
|
73
|
+
it "does not set key, when not set backup" do
|
74
|
+
expect(backup_config.has_key? "no_backup").to eq false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
context "when use rails" do
|
79
|
+
before do
|
80
|
+
module Rails
|
81
|
+
def self.env
|
82
|
+
end
|
83
|
+
end
|
84
|
+
allow(Rails).to receive(:env).and_return("test")
|
85
|
+
end
|
86
|
+
it "contain environment key" do
|
87
|
+
database_config = ServerSettings::DatabaseConfig.generate_database_config(:master)
|
88
|
+
expect(database_config["test"]).not_to eq nil
|
89
|
+
end
|
90
|
+
it "returns configs with string keys" do
|
91
|
+
database_config = ServerSettings::DatabaseConfig.generate_database_config(:master)
|
92
|
+
expect(database_config["user"].keys.all? { |k| k.is_a? String }).to be true
|
93
|
+
expect(database_config["has_slave_slave1"].keys.all? { |k| k.is_a? String }).to be true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|