cloudclone 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
+ environment_id="ruby-1.9.3-p0@cloudclone"
8
+
9
+ #
10
+ # Uncomment following line if you want options to be set only for given project.
11
+ #
12
+ # PROJECT_JRUBY_OPTS=( --1.9 )
13
+ #
14
+ # The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
15
+ #
16
+ # chmod +x ${rvm_path}/hooks/after_use_jruby_opts
17
+ #
18
+
19
+ #
20
+ # First we attempt to load the desired environment directly from the environment
21
+ # file. This is very fast and efficient compared to running through the entire
22
+ # CLI and selector. If you want feedback on which environment was used then
23
+ # insert the word 'use' after --create as this triggers verbose mode.
24
+ #
25
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
26
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
27
+ then
28
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
29
+
30
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
31
+ then
32
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
33
+ fi
34
+ else
35
+ # If the environment file has not yet been created, use the RVM CLI to select.
36
+ if ! rvm --create "$environment_id"
37
+ then
38
+ echo "Failed to create RVM environment '${environment_id}'."
39
+ return 1
40
+ fi
41
+ fi
42
+
43
+ #
44
+ # If you use an RVM gemset file to install a list of gems (*.gems), you can have
45
+ # it be automatically loaded. Uncomment the following and adjust the filename if
46
+ # necessary.
47
+ #
48
+ # filename=".gems"
49
+ # if [[ -s "$filename" ]]
50
+ # then
51
+ # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
52
+ # fi
53
+
54
+ # If you use bundler, this might be useful to you:
55
+ # if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
56
+ # then
57
+ # printf "The rubygem 'bundler' is not installed. Installing it now.\n"
58
+ # gem install bundler
59
+ # fi
60
+ # if [[ -s Gemfile ]] && command -v bundle
61
+ # then
62
+ # bundle install
63
+ # fi
64
+
65
+ if [[ $- == *i* ]] # check for interactive shells
66
+ then
67
+ echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
68
+ else
69
+ echo "Using: $GEM_HOME" # don't use colors in interactive shells
70
+ fi
71
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cloudclone.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2011 Nuttanart Pornprasitsakul.
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 NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "cloudclone/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "cloudclone"
7
+ s.version = Cloudclone::VERSION
8
+ s.authors = ["Nuttanart Pornprasitsakul"]
9
+ s.email = ["visibletrap@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Multi cloud service instances management}
12
+ s.description = %q{Cloudclone help you easily manage tons of cloud service instances}
13
+
14
+ s.rubyforge_project = "cloudclone"
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
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+
25
+ s.add_development_dependency "rspec"
26
+ s.add_development_dependency "webmock"
27
+ s.add_runtime_dependency "heroku"
28
+ end
data/lib/cloudclone.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "cloudclone/version"
2
+
3
+ module Cloudclone
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,65 @@
1
+ require 'cloudclone'
2
+ require 'heroku'
3
+ require 'cloudclone/group'
4
+
5
+ class Cloudclone::Client
6
+
7
+ attr_reader :heroku
8
+
9
+ def initialize(username, password)
10
+ @heroku = Heroku::Client.new(username, password)
11
+ end
12
+
13
+ def create(group_name, no_of_apps)
14
+ app_names = (1..no_of_apps).map { |n| "cc-#{group_name}-#{n}" }
15
+ app_names.each { |n| @heroku.create(n) }
16
+ deploy(app_names)
17
+ Cloudclone::Group.new(group_name, @heroku)
18
+ end
19
+
20
+ def list
21
+ @heroku.list.map { |e| e[0].match(/^cc-(.+)-1$/) }.compact.
22
+ map { |e| e.captures }.flatten
23
+ end
24
+
25
+ def group(group_name)
26
+ Cloudclone::Group.new(group_name, @heroku) if list.include?(group_name)
27
+ end
28
+
29
+ def destroy(group_name)
30
+ groups.select { |g| g.name == group_name }.each { |g| g.destroy }
31
+ end
32
+
33
+ def groups
34
+ list.map { |name| Cloudclone::Group.new(name, @heroku) }
35
+ end
36
+
37
+ private
38
+ def deploy(app_names)
39
+ clone_server
40
+ Dir.chdir('cloudclone-server')
41
+ begin
42
+ app_names.each do |n|
43
+ add_remote(n)
44
+ push(n)
45
+ end
46
+ ensure
47
+ Dir.chdir('..')
48
+ FileUtils.rm_rf('../cloudclone/cloudclone-server')
49
+ end
50
+ end
51
+
52
+ def clone_server
53
+ `git clone git://github.com/visibletrap/cloudclone-server.git`
54
+ end
55
+
56
+ def add_remote(app_name)
57
+ `git remote add #{app_name} git@heroku.com:#{app_name}.git`
58
+ end
59
+
60
+ def push(app_name)
61
+ `heroku accounts:set #{ENV['HEROKU_ACCOUNT']}` if ENV['HEROKU_ACCOUNT']
62
+ `git push #{app_name} master`
63
+ end
64
+
65
+ end
@@ -0,0 +1,31 @@
1
+ require 'cloudclone'
2
+
3
+ class Cloudclone::Group
4
+
5
+ def initialize(group_name, heroku)
6
+ @group_name = group_name
7
+ @heroku = heroku
8
+ end
9
+
10
+ def name
11
+ @group_name
12
+ end
13
+
14
+ def destroy
15
+ app_names.each{ |n| @heroku.destroy(n) }
16
+ end
17
+
18
+ def request(url)
19
+ app_names.each { |n| RestClient::get("http://#{n}.heroku.com/?url=#{url}") }
20
+ end
21
+
22
+ private
23
+ def app_names
24
+ all_app_names.select{ |n| n =~ /^cc-#{@group_name}-\d$/ }
25
+ end
26
+
27
+ def all_app_names
28
+ @heroku.list.map{ |e| e[0] }
29
+ end
30
+
31
+ end
@@ -0,0 +1,3 @@
1
+ module Cloudclone
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,67 @@
1
+ require 'cloudclone/client'
2
+
3
+ describe Cloudclone::Client do
4
+
5
+ subject { Cloudclone::Client.new('username', 'password') }
6
+
7
+ describe "#create" do
8
+ it "should create Heroku apps" do
9
+ subject.should_receive(:deploy).with((1..10).map{ |i|"cc-prefix-#{i}" })
10
+ (1..10).each do |n|
11
+ Heroku::Client.any_instance.should_receive(:create).
12
+ with("cc-prefix-#{n}")
13
+ end
14
+ subject.create('prefix', 10)
15
+ end
16
+ end
17
+
18
+ describe "#list" do
19
+ it "should list all Cloudclone groups" do
20
+ email = "not@interest.ing"
21
+ heroku_response = [["otherapps", email], ["cc-groupa-1", email],
22
+ ["cc-groupa-2", email], ["cc-groupb-1", email]]
23
+ Heroku::Client.any_instance.should_receive(:list).
24
+ and_return(heroku_response)
25
+ subject.list.should == ['groupa', 'groupb']
26
+ end
27
+ end
28
+
29
+ describe "#group" do
30
+ before { subject.stub(:list).and_return(["group_name"]) }
31
+ context "group existing" do
32
+ it "should create group object" do
33
+ Cloudclone::Group.should_receive(:new).with("group_name", kind_of(Heroku::Client))
34
+ subject.group("group_name")
35
+ end
36
+ end
37
+ context "group not existing" do
38
+ it "should do nothing" do
39
+ Cloudclone::Group.should_not_receive(:new)
40
+ subject.group("not_group_name")
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "#groups" do
46
+ it "should use result from list to return as group objects" do
47
+ names = ["groupa", "groupb"]
48
+ subject.should_receive(:list).and_return(names)
49
+ groups = subject.groups
50
+ groups.count.should == names.count
51
+ names.should include(groups[0].name)
52
+ names.should include(groups[1].name)
53
+ end
54
+ end
55
+
56
+ describe "#destroy" do
57
+ it "should call Group to destroy" do
58
+ group = mock(Cloudclone::Group)
59
+ group.stub(:name).and_return("group_name")
60
+ subject.stub(:groups).and_return([group])
61
+ group.should_receive(:destroy)
62
+
63
+ subject.destroy("group_name")
64
+ end
65
+ end
66
+
67
+ end
@@ -0,0 +1,44 @@
1
+ describe Cloudclone::Group do
2
+
3
+ let(:group_name) { "group-name" }
4
+ let(:heroku) { mock(Heroku::Client).as_null_object }
5
+ subject { Cloudclone::Group.new("group-name", heroku) }
6
+
7
+ describe "#new" do
8
+ it "should receive group name and Heroku object" do
9
+ Cloudclone::Group.new(group_name, instance_of(Heroku::Client)).should_not be_nil
10
+ end
11
+ end
12
+
13
+ describe "#name" do
14
+ it "should return group name" do
15
+ subject.name.should == group_name
16
+ end
17
+ end
18
+
19
+ describe "#destroy" do
20
+ it "should call Heroku to destroy every apps in group" do
21
+ email = "any@em.ail"
22
+ apps = [["cc-#{group_name}-1", email], ["cc-#{group_name}-2", email],
23
+ ["otherapp", email], ["cc-othergroup-1", email]]
24
+ heroku.should_receive(:list).and_return(apps)
25
+ heroku.should_receive(:destroy).with(apps[0][0])
26
+ heroku.should_receive(:destroy).with(apps[1][0])
27
+ heroku.should_not_receive(:destroy).with(apps[2][0])
28
+ heroku.should_not_receive(:destroy).with(apps[3][0])
29
+ subject.destroy
30
+ end
31
+ end
32
+
33
+ describe "#request" do
34
+ it "should request with provide link as url paramter to servers" do
35
+ app_names = ["cc-app-1", "cc-app-2", "cc-app-3"]
36
+ subject.stub(:app_names).and_return(app_names)
37
+ app_names.each do |n|
38
+ RestClient.should_receive(:get).with("http://#{n}.heroku.com/?url=http://www.google.com")
39
+ end
40
+ subject.request("http://www.google.com")
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,94 @@
1
+ require 'cloudclone/client'
2
+ require 'cloudclone/group'
3
+
4
+ describe "Cloudclone" do
5
+
6
+ let(:username) { ENV['HEROKU_USERNAME'] }
7
+ let(:password) { ENV['HEROKU_PASSWORD'] }
8
+
9
+ subject { Cloudclone::Client.new(username, password) }
10
+
11
+ let(:heroku) { subject.heroku }
12
+
13
+ describe "#new" do
14
+ it "receive username and password of Heroku account" do
15
+ Cloudclone::Client.new(username, password).should_not be_nil
16
+ end
17
+ end
18
+
19
+ describe "#create" do
20
+ let(:group_name) { "create-group-name" }
21
+ let(:app_names) { (1..2).map{ |n| "cc-#{group_name}-#{n}"} }
22
+
23
+ before(:all) do
24
+ @output = subject.create(group_name, 2)
25
+ end
26
+ it "create certain amount of heroku apps which name contains group name" do
27
+ heroku.list.map{ |e| e[0] }.should == app_names
28
+ end
29
+ it "should return Cloundclone::Group object" do
30
+ @output.should be_kind_of(Cloudclone::Group)
31
+ end
32
+ it "should deploy cloudclone-server to app instances" do
33
+ require 'rest_client'
34
+ RestClient.get("http://#{app_names[0]}.heroku.com").should =~ /Cloudclone installed/
35
+ RestClient.get("http://#{app_names[1]}.heroku.com").should =~ /Cloudclone installed/
36
+ end
37
+ after(:all) do
38
+ app_names.each{ |a| heroku.destroy(a) }
39
+ end
40
+ end
41
+
42
+ describe "#destroy" do
43
+ it "receive an existing group name return true" do
44
+ subject.create("destroy-group-name", 2)
45
+ subject.destroy("destroy-group-name").should be_true
46
+ end
47
+ end
48
+
49
+ describe "#list" do
50
+ let(:group_name) { ["list-group-name-1", "list-group-name-2"] }
51
+ it "should return group names" do
52
+ subject.create(group_name[0], 1)
53
+ subject.create(group_name[1], 1)
54
+
55
+ subject.list.should == group_name
56
+ end
57
+ after do
58
+ group_name.each{ |n| heroku.destroy("cc-#{n}-1") }
59
+ end
60
+ end
61
+
62
+ describe "#group" do
63
+ before(:all) do
64
+ subject.create("valid-group-name", 1)
65
+ end
66
+ context "valid group name" do
67
+ it { subject.group("valid-group-name").should be_kind_of(Cloudclone::Group) }
68
+ end
69
+ context "invalid group name" do
70
+ it { subject.group("invali-group-name").should be_nil }
71
+ end
72
+ after(:all) do
73
+ heroku.destroy("cc-valid-group-name-1");
74
+ end
75
+ end
76
+
77
+ describe "#groups" do
78
+ let(:group_name) { ["groups-group-name-1", "groups-group-name-2"] }
79
+ it "should return group objects" do
80
+ subject.create(group_name[0], 1)
81
+ subject.create(group_name[1], 1)
82
+
83
+ app_groups = subject.groups
84
+ app_groups[0].should be_kind_of(Cloudclone::Group)
85
+ app_groups[0].name.should == group_name[0]
86
+ app_groups[1].should be_kind_of(Cloudclone::Group)
87
+ app_groups[1].name.should == group_name[1]
88
+ end
89
+ after do
90
+ group_name.each{ |n| heroku.destroy("cc-#{n}-1") }
91
+ end
92
+ end
93
+
94
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'cloudclone/client'
3
+ require 'cloudclone/group'
4
+
5
+ describe "Cloudclone" do
6
+
7
+ let(:username) { ENV['HEROKU_USERNAME'] }
8
+ let(:password) { ENV['HEROKU_PASSWORD'] }
9
+
10
+ describe "#request" do
11
+ it "should send request to provided url" do
12
+ WebMock.allow_net_connect!
13
+ subject = Cloudclone::Client.new(username, password).create("group-test", 2)
14
+ subject.request("http://www.google.com")
15
+ WebMock.should have_requested(:get, "cc-group-test-1.heroku.com/?url=http://www.google.com")
16
+ WebMock.should have_requested(:get, "cc-group-test-2.heroku.com/?url=http://www.google.com")
17
+ end
18
+ end
19
+
20
+ after(:all) do
21
+ Heroku::Client.new(username, password).destroy("cc-group-test-1")
22
+ Heroku::Client.new(username, password).destroy("cc-group-test-2")
23
+ end
24
+
25
+ end
@@ -0,0 +1,2 @@
1
+ require 'webmock/rspec'
2
+ WebMock.allow_net_connect!
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudclone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nuttanart Pornprasitsakul
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70102880213700 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70102880213700
25
+ - !ruby/object:Gem::Dependency
26
+ name: webmock
27
+ requirement: &70102880213220 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70102880213220
36
+ - !ruby/object:Gem::Dependency
37
+ name: heroku
38
+ requirement: &70102880212660 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70102880212660
47
+ description: Cloudclone help you easily manage tons of cloud service instances
48
+ email:
49
+ - visibletrap@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .rspec
56
+ - .rvmrc
57
+ - Gemfile
58
+ - LICENSE
59
+ - Rakefile
60
+ - cloudclone.gemspec
61
+ - lib/cloudclone.rb
62
+ - lib/cloudclone/client.rb
63
+ - lib/cloudclone/group.rb
64
+ - lib/cloudclone/version.rb
65
+ - spec/cloudclone/client_spec.rb
66
+ - spec/cloudclone/group_spec.rb
67
+ - spec/functional/cloudclone_client_spec.rb
68
+ - spec/functional/cloudclone_group_spec.rb
69
+ - spec/spec_helper.rb
70
+ homepage: ''
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project: cloudclone
90
+ rubygems_version: 1.8.11
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Multi cloud service instances management
94
+ test_files:
95
+ - spec/cloudclone/client_spec.rb
96
+ - spec/cloudclone/group_spec.rb
97
+ - spec/functional/cloudclone_client_spec.rb
98
+ - spec/functional/cloudclone_group_spec.rb
99
+ - spec/spec_helper.rb