cloudclone 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.rvmrc +71 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/Rakefile +1 -0
- data/cloudclone.gemspec +28 -0
- data/lib/cloudclone.rb +5 -0
- data/lib/cloudclone/client.rb +65 -0
- data/lib/cloudclone/group.rb +31 -0
- data/lib/cloudclone/version.rb +3 -0
- data/spec/cloudclone/client_spec.rb +67 -0
- data/spec/cloudclone/group_spec.rb +44 -0
- data/spec/functional/cloudclone_client_spec.rb +94 -0
- data/spec/functional/cloudclone_group_spec.rb +25 -0
- data/spec/spec_helper.rb +2 -0
- metadata +99 -0
data/.gitignore
ADDED
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
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"
|
data/cloudclone.gemspec
ADDED
@@ -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,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,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
|
data/spec/spec_helper.rb
ADDED
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
|