mystro-common 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mystro-common.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Shawn Catanzarite
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Mystro::Common
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'mystro-common'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install mystro-common
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ def changelog(last=nil, single=false)
4
+ command="git --no-pager log --format='%an::::%h::::%s'"
5
+
6
+ list = `git tag`
7
+
8
+ puts "# Changelog"
9
+ puts
10
+
11
+ ordered = list.lines.sort_by {|e| (a,b,c) = e.gsub(/^v/,"").split("."); "%3d%3d%3d" % [a, b, c]}
12
+
13
+ ordered.reject{|e| (a,b,c,d) = e.split("."); !d.nil?}.reverse_each do |t|
14
+ tag = t.chomp
15
+
16
+ if last
17
+ check = { }
18
+ out = []
19
+ log = `#{command} #{last}...#{tag}`
20
+ log.lines.each do |line|
21
+ (who, hash, msg) = line.split('::::')
22
+ unless check[msg]
23
+ unless msg =~ /^Merge branch/ || msg =~ /CHANGELOG/ || msg =~ /^(v|version|changes for|preparing|ready for release|ready to release|bump version)*\s*(v|version)*\d+\.\d+\.\d+/
24
+ msg.gsub(" *", "\n*").gsub(/^\*\*/, " *").lines.each do |l|
25
+ line = l =~ /^(\s+)*\*/ ? l : "* #{l}"
26
+ out << line
27
+ end
28
+ check[msg] = hash
29
+ end
30
+ end
31
+ end
32
+ puts "## #{last}:"
33
+ out.each { |e| puts e }
34
+ #puts log
35
+ puts
36
+ end
37
+
38
+ last = tag
39
+ exit if single
40
+ end
41
+ end
42
+
43
+ desc "generate changelog output"
44
+ task :changelog do
45
+ changelog
46
+ end
47
+
48
+ desc "show current changes (changelog output from HEAD to most recent tag)"
49
+ task :current do
50
+ changelog("HEAD",true)
51
+ end
@@ -0,0 +1,97 @@
1
+ module Mystro
2
+ class Account
3
+ class << self
4
+ #attr_reader :name
5
+ attr_reader :list
6
+ attr_reader :selected
7
+
8
+ def read
9
+ dir = Mystro.directory
10
+ @list = { }
11
+
12
+ Dir["#{dir}/accounts/*.y*ml"].each do |file|
13
+ name = file.gsub(/#{dir}\/accounts\//, "").gsub(/\.(\w+?)$/, "")
14
+ Mystro::Log.debug "loading account '#{name}' '#{file}'"
15
+ @list[name] = self.new(name, file)
16
+ end
17
+
18
+ @selected = default
19
+ end
20
+
21
+ def default
22
+ return ENV['MYSTRO_ACCOUNT'] if ENV['MYSTRO_ACCOUNT']
23
+ return Mystro.config.default_account if Mystro.config.default_account?
24
+ return "default" if list.keys.include?("default")
25
+ list.keys.first
26
+ end
27
+
28
+ def select(name)
29
+ @selected = name
30
+ end
31
+
32
+ #def data(name = get)
33
+ # if @name != name
34
+ # @data = nil
35
+ # @loaded = false
36
+ # @name = nil
37
+ # end
38
+ #
39
+ # @data ||= begin
40
+ # Mystro::Log.debug "loading account"
41
+ # a = Mystro::Model::Account.load(name)
42
+ # a[:name] = name
43
+ #
44
+ # Mystro::Log.debug "loading plugins from account"
45
+ # Mystro::Plugin.load(a[:plugins]) if a[:plugins]
46
+ #
47
+ # a
48
+ # end
49
+ # @name = @data[:name]
50
+ # @loaded = true
51
+ # @data
52
+ #end
53
+ #
54
+ #def get
55
+ # return ENV['RIG_ACCOUNT'] if ENV['RIG_ACCOUNT']
56
+ # return Mystro.config[:account] if Mystro.config[:account]
57
+ # return Mystro.config[:default_account] if Mystro.config[:default_account]
58
+ # return Mystro.config[:accounts].first if Mystro.config[:accounts] && Mystro.config[:accounts].count > 0
59
+ # "default"
60
+ #end
61
+ #
62
+ #def save
63
+ # name = Mystro.account[:name]
64
+ # Mystro::Model::Account.save(name, Mystro.account)
65
+ #end
66
+ end
67
+
68
+ attr_reader :data
69
+ attr_reader :file
70
+ attr_reader :name
71
+
72
+ def initialize(name, file)
73
+ cfg = Mystro.config.to_hash
74
+ account = File.exists?(file) ? YAML.load_file(file) : { }
75
+ @name = name
76
+ @file = file
77
+ @data = Hashie::Mash.new(cfg.deep_merge(account))
78
+ @data.name = name
79
+ end
80
+
81
+ def compute
82
+ @compute ||= Mystro::Connect::Compute.new(self) if @data.compute
83
+ end
84
+
85
+ def balancer
86
+ @balancer ||= Mystro::Connect::Balancer.new(self) if @data.balancer
87
+ end
88
+
89
+ def dns
90
+ @dns ||= Mystro::Connect::Dns.new(self) if @data.dns
91
+ end
92
+
93
+ def environment
94
+ @environment ||= Mystro::Connect::Environment.new(self)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,50 @@
1
+ ## integration with capistrano
2
+ ## have some helpers to use mystro to get the
3
+ ## host lists for capistrano to use.
4
+ #
5
+ #require 'mystro'
6
+ #require 'mystro/model'
7
+ #require 'capistrano'
8
+ #
9
+ #module Mystro
10
+ # module Capistrano
11
+ # def servers
12
+ # env = Mystro.config[:environment]
13
+ # role = Mystro.config[:role]
14
+ # servers = Mystro::Model::Environment.find(env).servers
15
+ # list = role == 'all' ? servers : servers.select { |s| (s.tags['Roles']||"").split(",").include?(role) }
16
+ #
17
+ # raise "Mystro could not find any servers matching environment=#{env} and role=#{role}" unless list && list.count > 0
18
+ # list.each do |s|
19
+ # server "#{s.tags['Name']}.#{Mystro.get_config(:dns_zone)}", :web, :app
20
+ # end
21
+ # rescue => e
22
+ # puts "*** servers not found: #{e.message}"
23
+ # end
24
+ # end
25
+ #end
26
+ #
27
+ #::Capistrano.plugin :mystro, Mystro::Capistrano
28
+ #
29
+ #configuration = Capistrano::Configuration.respond_to?(:instance) ?
30
+ # Capistrano::Configuration.instance(:must_exist) :
31
+ # Capistrano.configuration(:must_exist)
32
+ #
33
+ #configuration.load do
34
+ # puts " * reading mystro information..."
35
+ #
36
+ # Mystro.config[:environment] = ENV['ENVIRONMENT'] || ARGV[0]
37
+ # Mystro.config[:role] = ENV['ROLE'] || ARGV[1]
38
+ #
39
+ # # create dummy tasks for environment and role
40
+ # begin
41
+ # ARGV.first(2).each do |arg|
42
+ # task arg do
43
+ # nil
44
+ # end
45
+ # end
46
+ # rescue
47
+ # nil
48
+ # end
49
+ #
50
+ #end
@@ -0,0 +1,11 @@
1
+ module Mystro
2
+ module Common
3
+ module Version
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ TINY = 0
7
+ TAG = nil
8
+ STRING = [MAJOR, MINOR, TINY, TAG].compact.join(".")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ module Mystro
2
+ class Config
3
+ class << self
4
+ def instance
5
+ @instance ||= self.new
6
+ end
7
+ end
8
+
9
+ attr_reader :data
10
+
11
+ def initialize
12
+ f = "#{Mystro.directory}/config.yml"
13
+ d = File.exists?(f) ? YAML.load_file(f) : {}
14
+ c = Hashie::Mash.new(d)
15
+
16
+ if c.logging?
17
+ c.logging.each do |level, dest|
18
+ Mystro::Log.add(level.to_sym, dest)
19
+ end
20
+ end
21
+
22
+ Mystro::Log.debug "loading plugins from configuration"
23
+ Mystro::Plugin.load(c[:plugins]) if c[:plugins]
24
+ @raw = c
25
+ @data = Hashie::Mash.new(c)
26
+ @data.directory = Mystro.directory
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,91 @@
1
+
2
+ module Mystro
3
+ module Connect
4
+ class Balancer < Base
5
+ self.model = "Fog::Balancer"
6
+ self.collection = :load_balancers
7
+
8
+ def all
9
+ fog.send(collection).all
10
+ end
11
+
12
+ def create(model)
13
+ balancer = fog.send(collection).create(model.fog_options)
14
+ balancer.register_instances(model.computes.collect{|e| e.rid})
15
+ balancer.save
16
+ end
17
+
18
+ def find_by_environment(name)
19
+ all.select {|e| e.id =~ /^#{name}\-/}
20
+ end
21
+
22
+ def listener_find(id, from)
23
+ balancer = find(id)
24
+ (from_proto, from_port) = from.split(':')
25
+ match = balancer.listeners.select {|l| l.protocol == from_proto && l.lb_port == from_port.to_i }
26
+ raise "no listeners #{from} found" if match.count == 0
27
+ raise "more than one listener matched #{from}" if match.count > 1
28
+ match.first
29
+ end
30
+
31
+ def listener_create(id, model)
32
+ Mystro::Log.debug "balancer#add_listener #{id} #{model.inspect}"
33
+ lb = find(id)
34
+ opts = model.fog_options
35
+ ap opts
36
+ lb.listeners.create(opts)
37
+ end
38
+
39
+ def listener_destroy(id, from)
40
+ Mystro::Log.debug "balancer#rm_listener #{id} #{from}"
41
+ listener = listener_find(id, from)
42
+ listener.destroy
43
+ end
44
+
45
+ def health_check(id, health)
46
+ Mystro::Log.debug "balancer#health_check #{id} #{health.inspect}"
47
+ balancer = find(id)
48
+ raise "balancer #{id} not found" unless balancer
49
+
50
+ fog.configure_health_check(id, health.fog_options)
51
+ end
52
+
53
+ def sticky(id, type, expires_or_cookie, port, policy=nil)
54
+ balancer = find(id)
55
+ raise "balancer #{id} not found" unless balancer
56
+
57
+ policy ||= "sticky-#{id}-#{type}-#{port}"
58
+ policies = balancer.attributes["ListenerDescriptions"] ? balancer.attributes["ListenerDescriptions"].inject([]) {|a, e| a << e["PolicyNames"]}.flatten : []
59
+ policies << policy
60
+
61
+ case type.downcase.to_sym
62
+ when :app, :application
63
+ fog.create_app_cookie_stickiness_policy(id, policy, expires_or_cookie)
64
+ fog.set_load_balancer_policies_of_listener(id, port.to_i, policies)
65
+ when :lb, :loadbalancer, :load_balancer
66
+ fog.create_lb_cookie_stickiness_policy(id, policy, expires_or_cookie.to_i)
67
+ fog.set_load_balancer_policies_of_listener(id, port.to_i, policies)
68
+ else
69
+ raise "unknown sticky type #{type}"
70
+ end
71
+ end
72
+
73
+ def unsticky(id, type, port, policy=nil)
74
+ balancer = find(id)
75
+ raise "balancer #{id} not found" unless balancer
76
+
77
+ policy ||= "sticky-#{id}-#{type}-#{port}"
78
+ policies = balancer.attributes["ListenerDescriptions"] ? balancer.attributes["ListenerDescriptions"].inject([]) {|a, e| a << e["PolicyNames"]}.flatten : []
79
+ policies.delete(policy)
80
+
81
+ case type.downcase.to_sym
82
+ when :app, :application, :lb, :loadbalancer, :load_balancer
83
+ fog.set_load_balancer_policies_of_listener(id, port.to_i, policies)
84
+ fog.delete_load_balancer_policy(id, policy)
85
+ else
86
+ raise "unknown sticky type #{type}"
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,100 @@
1
+ module Mystro
2
+ module Connect
3
+ class Compute < Base
4
+ self.model = "Fog::Compute"
5
+ self.collection = :servers
6
+
7
+ def all(filters={ })
8
+ fog.send(collection).all(filters)
9
+ end
10
+
11
+ def running
12
+ all({ "instance-state-name" => "running" })
13
+ end
14
+
15
+ def find(id_or_name)
16
+ if id_or_name =~ /^i-/
17
+ super(id_or_name)
18
+ else
19
+ find_by_name(id_or_name)
20
+ end
21
+ end
22
+
23
+ def find_by_name(name)
24
+ list = find_by_tags(name: name)
25
+ return list if list.count > 0
26
+ find_by_nick(name)
27
+ end
28
+
29
+ def find_by_nick(nick)
30
+ Mystro::Log.debug "compute#find_by_nick #{nick}"
31
+ (name, env) = nick.match(/(\w+)\.(\w+)/)[1..2]
32
+ find_by_environment(env).select { |s| s.tags['Name'] =~ /^#{name}/ }
33
+ rescue => e
34
+ Mystro::Log.error "error finding server by nickname #{nick}. name should be of the form: role#.environment"
35
+ []
36
+ end
37
+
38
+ def find_by_environment(environment)
39
+ find_by_tags(environment: environment)
40
+ end
41
+
42
+ def find_by_environment_and_role(environment, role)
43
+ list = find_by_tags(environment: environment)
44
+ list.select { |s| r = s.tags['Roles'] || s.tags['Role']; r.split(",").include?(role) }
45
+ end
46
+
47
+ def find_by_tags(tags)
48
+ Mystro::Log.debug "compute#find_by_tags #{tags.inspect}"
49
+ filters = tags.inject({ }) { |h, e| (k, v) = e; h["tag:#{k.to_s.capitalize}"] = v; h }
50
+ all(filters)
51
+ end
52
+
53
+ def create(model)
54
+ Mystro::Log.debug "#{cname}#create #{model.inspect}"
55
+ e = fog.send(collection).create(model.fog_options)
56
+ fog.create_tags(e.id, model.fog_tags)
57
+ Mystro::Plugin.run "compute:create", e, model
58
+ e
59
+ end
60
+
61
+ #after :create do |compute, model|
62
+ # Mystro::Log.debug "compute#after_create #{compute.id} #{model.fog_tags}"
63
+ # Mystro::Log.debug "#{cname}#create #{fog.inspect}"
64
+ # sleep 3
65
+ #end
66
+
67
+ def destroy(models)
68
+ list = [*models].flatten
69
+ list.each do |m|
70
+ Mystro::Log.debug "#{cname}#destroy #{m.rid}"
71
+ e = find(m.rid)
72
+ Mystro::Plugin.run "compute:destroy", e, m
73
+ e.destroy
74
+ tags = e.tags.keys.inject({ }) { |h, e| h[e] = nil; h }
75
+ fog.create_tags([e.id], tags)
76
+ end
77
+ end
78
+
79
+ #def destroy(list)
80
+ # list = [*list]
81
+ # if list.count > 0
82
+ # ids = list.map {|e| e.kind_of?(String) ? e : e.id}
83
+ # Mystro::Log.debug "compute#destroy: #{ids.inspect}"
84
+ # list.each do |e|
85
+ # Mystro::Plugin.run "compute:destroy", e
86
+ # e.destroy
87
+ # tags = e.tags.keys.inject({}) {|h, e| h[e] = nil; h }
88
+ # fog.create_tags(ids, tags)
89
+ # end
90
+ # end
91
+ #end
92
+
93
+ #after :destroy do |compute, tags|
94
+ # Mystro::Log.debug "#{cname}#after_destroy"
95
+ # tags = compute.tags.keys.inject({ }) { |h, e| h[e] = nil; h }
96
+ # fog.create_tags([compute.id], tags) if tags.count > 0
97
+ #end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,38 @@
1
+
2
+ module Mystro
3
+ module Connect
4
+ class Dns < Base
5
+ self.model = "Fog::DNS"
6
+ self.collection = :records
7
+
8
+ def find_by_endpoint(dns)
9
+ all.select {|e| [*e.value].flatten.include?(dns)}
10
+ end
11
+
12
+ # customize the connect function, because we are defaulting
13
+ # to the zone from the configuration
14
+ def connect
15
+ @fog ||= begin
16
+ raise "could not connect to DNS; #{opt.to_hash.inspect}; #{cfg.to_hash.inspect}" unless opt && cfg.zone
17
+ dns = Fog::DNS.new(opt)
18
+ dns.zones.find(cfg.zone).first
19
+ end
20
+ rescue => e
21
+ Mystro::Log.error "DNS connect failed: #{e.message} at #{e.backtrace.first}"
22
+ Mystro::Log.error e
23
+ end
24
+
25
+ def zones
26
+ Fog::DNS.new(opt).zones.all
27
+ end
28
+
29
+ def zone(name)
30
+ Fog::DNS.new(opt).zones.find(name).first
31
+ end
32
+
33
+ def create_zone(model)
34
+ Fog::DNS.new(opt).zones.create(model.fog_options)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module Mystro
2
+ module Connect
3
+ class Environment < Base
4
+ def connect
5
+
6
+ end
7
+
8
+ def connected?
9
+ true
10
+ end
11
+
12
+ def find(name)
13
+ Mystro::Model::Environment.load(name)
14
+ end
15
+
16
+ def all
17
+ list = Mystro.compute.all
18
+ list.inject([]) { |a, e| a << e.tags["Environment"] }.compact.uniq.sort
19
+ end
20
+
21
+ def create(model)
22
+ Mystro::Log.info "model: #{model}"
23
+ list = model.template_to_models
24
+ end
25
+
26
+ def destroy
27
+
28
+ end
29
+ end
30
+ end
31
+ end