larrow-runner 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +80 -0
- data/Rakefile +2 -0
- data/bin/larrow +4 -0
- data/larrow-runner.gemspec +42 -0
- data/lib/larrow/runner/cli/build.rb +41 -0
- data/lib/larrow/runner/cli/main.rb +35 -0
- data/lib/larrow/runner/cli/tools.rb +34 -0
- data/lib/larrow/runner/cli.rb +11 -0
- data/lib/larrow/runner/errors.rb +1 -0
- data/lib/larrow/runner/helper.rb +0 -0
- data/lib/larrow/runner/logger.rb +69 -0
- data/lib/larrow/runner/manager.rb +114 -0
- data/lib/larrow/runner/manifest/adapter/blank.rb +8 -0
- data/lib/larrow/runner/manifest/adapter/larrow.rb +32 -0
- data/lib/larrow/runner/manifest/adapter/travis.rb +73 -0
- data/lib/larrow/runner/manifest/base_loader.rb +21 -0
- data/lib/larrow/runner/manifest/configuration.rb +126 -0
- data/lib/larrow/runner/manifest.rb +48 -0
- data/lib/larrow/runner/model/app.rb +62 -0
- data/lib/larrow/runner/model/node.rb +73 -0
- data/lib/larrow/runner/service/cloud.rb +54 -0
- data/lib/larrow/runner/service/executor.rb +56 -0
- data/lib/larrow/runner/service.rb +8 -0
- data/lib/larrow/runner/session.rb +64 -0
- data/lib/larrow/runner/vcs/base.rb +17 -0
- data/lib/larrow/runner/vcs/file_system.rb +58 -0
- data/lib/larrow/runner/vcs/github.rb +48 -0
- data/lib/larrow/runner/vcs.rb +20 -0
- data/lib/larrow/runner/version.rb +5 -0
- data/lib/larrow/runner.rb +34 -0
- data/spec/fixtures/travis_erlang.yml +8 -0
- data/spec/fixtures/travis_ruby.yml +9 -0
- data/spec/integration/build_cmds_spec.rb +18 -0
- data/spec/integration/test_cmds_spec.rb +13 -0
- data/spec/manifest/travis_spec.rb +42 -0
- data/spec/model/node_spec.rb +18 -0
- data/spec/service/executor_spec.rb +26 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/vcs/github_spec.rb +33 -0
- metadata +340 -0
@@ -0,0 +1,126 @@
|
|
1
|
+
module Larrow::Runner::Manifest
|
2
|
+
# The top of manifest model which store Steps information
|
3
|
+
class Configuration
|
4
|
+
DEFINED_GROUPS = {
|
5
|
+
all:[
|
6
|
+
:init,
|
7
|
+
:source_sync, #inner step
|
8
|
+
:prepare,
|
9
|
+
:compile, :unit_test,
|
10
|
+
:before_install, #inner_step
|
11
|
+
:install, :functional_test,
|
12
|
+
:before_start, #inner_step
|
13
|
+
:start, :integration_test,
|
14
|
+
:after_start, :complete #inner_step
|
15
|
+
],
|
16
|
+
custom: [
|
17
|
+
:init,
|
18
|
+
:prepare,
|
19
|
+
:compile, :unit_test,
|
20
|
+
:install, :functional_test,
|
21
|
+
:start, :integration_test,
|
22
|
+
],
|
23
|
+
deploy: [
|
24
|
+
:init,:source_sync,:prepare,
|
25
|
+
:compile,:before_install,:install,
|
26
|
+
:before_start,:start,:after_start,
|
27
|
+
:complete
|
28
|
+
],
|
29
|
+
image: [:init]
|
30
|
+
}
|
31
|
+
|
32
|
+
attr_accessor :steps, :image, :source_dir
|
33
|
+
def initialize
|
34
|
+
self.steps = {}
|
35
|
+
self.source_dir = '$HOME/source'
|
36
|
+
end
|
37
|
+
|
38
|
+
def put_to_step title, *scripts
|
39
|
+
steps[title] ||= CmdStep.new(nil, title)
|
40
|
+
steps[title].scripts += scripts.flatten
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def insert_to_step title, *scripts
|
45
|
+
steps[title] ||= CmdStep.new(nil, title)
|
46
|
+
steps[title].scripts.unshift *scripts.flatten
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_source_sync source_accessor
|
51
|
+
steps[:source_sync] = FunctionStep.new(:source_sync) do |node|
|
52
|
+
source_accessor.update_source node,source_dir
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def steps_for type
|
57
|
+
groups = DEFINED_GROUPS[type]
|
58
|
+
# ignore init when image id is specified
|
59
|
+
groups = groups - [:init] if image
|
60
|
+
groups.each do |title|
|
61
|
+
yield steps[title] if steps[title]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def dump
|
66
|
+
data = DEFINED_GROUPS[:all].reduce({}) do |sum,title|
|
67
|
+
next sum if steps[title].nil?
|
68
|
+
scripts_data = steps[title].scripts.map(&:dump).compact
|
69
|
+
sum.update title.to_s => scripts_data
|
70
|
+
end
|
71
|
+
YAML.dump data
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Describe a set of scripts to accomplish a specific goal
|
76
|
+
class CmdStep
|
77
|
+
attr_accessor :scripts, :title
|
78
|
+
def initialize scripts, title
|
79
|
+
self.scripts = scripts || []
|
80
|
+
self.title = title
|
81
|
+
end
|
82
|
+
|
83
|
+
def run_on node
|
84
|
+
scripts.each do |script|
|
85
|
+
node.execute script.actual_command, base_dir: script.base_dir
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# An abstract step which bind business logic with block
|
91
|
+
# This class designed for some typically service,eg:
|
92
|
+
# * local file folder sync
|
93
|
+
# * some service invoke
|
94
|
+
class FunctionStep
|
95
|
+
attr_accessor :block, :title
|
96
|
+
def initialize title, &block
|
97
|
+
self.title = title
|
98
|
+
self.block = block
|
99
|
+
end
|
100
|
+
|
101
|
+
def run_on node
|
102
|
+
block.call node
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# store the real command line
|
107
|
+
# :cannt_fail used to declare `non-zero retcode of current script will be fail`
|
108
|
+
class Script
|
109
|
+
attr_accessor :cmd, :base_dir, :args, :cannt_fail
|
110
|
+
def initialize cmd, base_dir:nil, args:{}, cannt_fail: true
|
111
|
+
self.cmd = cmd
|
112
|
+
self.args = args
|
113
|
+
self.cannt_fail = cannt_fail
|
114
|
+
self.base_dir = base_dir
|
115
|
+
end
|
116
|
+
|
117
|
+
def actual_command
|
118
|
+
sprintf(cmd, args)
|
119
|
+
end
|
120
|
+
|
121
|
+
def dump
|
122
|
+
return nil if cmd.empty?
|
123
|
+
cmd
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'larrow/runner/manifest/base_loader'
|
2
|
+
require 'larrow/runner/manifest/configuration'
|
3
|
+
|
4
|
+
module Larrow
|
5
|
+
module Runner
|
6
|
+
# support multiple manifest style, such as travis, larrow, etc...
|
7
|
+
module Manifest
|
8
|
+
# Adapters is a set of class to adapt different manifest style.
|
9
|
+
# There isn't Adapter module, these classes are under Manifest module.
|
10
|
+
autoload :Travis, 'larrow/runner/manifest/adapter/travis'
|
11
|
+
autoload :Larrow, 'larrow/runner/manifest/adapter/larrow'
|
12
|
+
autoload :Blank, 'larrow/runner/manifest/adapter/blank'
|
13
|
+
|
14
|
+
extend self
|
15
|
+
|
16
|
+
def configuration source_accessor
|
17
|
+
[ Larrow, Travis, Blank ].each do |clazz|
|
18
|
+
configuration = clazz.new(source_accessor).load
|
19
|
+
return configuration if configuration
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_base_scripts configuration,source_accessor
|
24
|
+
configuration.add_source_sync source_accessor
|
25
|
+
unless configuration.image
|
26
|
+
lines = <<-EOF
|
27
|
+
#{package_update}
|
28
|
+
#{bashrc_cleanup}
|
29
|
+
EOF
|
30
|
+
scripts = lines.split(/\n/).map{|s| Script.new s}
|
31
|
+
configuration.insert_to_step :init, scripts
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def package_update
|
36
|
+
<<-EOF
|
37
|
+
apt-get update -qq
|
38
|
+
apt-get install git libssl-dev build-essential curl libncurses5-dev -y -qq
|
39
|
+
EOF
|
40
|
+
end
|
41
|
+
|
42
|
+
# remove PS1 check, for user to make ssh connection without tty
|
43
|
+
def bashrc_cleanup
|
44
|
+
"sed '/$PS1/ d' -i /root/.bashrc"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Larrow::Runner
|
2
|
+
module Model
|
3
|
+
class App
|
4
|
+
|
5
|
+
attr_accessor :vcs, :node, :configuration
|
6
|
+
def initialize vcs, attributes={}
|
7
|
+
self.vcs = vcs
|
8
|
+
self.configuration = vcs.configuration
|
9
|
+
self.assign attributes unless attributes.empty?
|
10
|
+
end
|
11
|
+
|
12
|
+
def assign arg
|
13
|
+
arg.each_pair do |k,v|
|
14
|
+
self.send "#{k}=".to_sym, v
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def action group
|
19
|
+
configuration.steps_for(group) do |a_step|
|
20
|
+
RunLogger.title "[#{a_step.title}]"
|
21
|
+
begin_at = Time.new
|
22
|
+
a_step.run_on node
|
23
|
+
during = sprintf('%.2f',Time.new - begin_at)
|
24
|
+
RunLogger.level(1).detail "#{a_step.title} complete (#{during}s)"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def allocate
|
29
|
+
RunLogger.title 'allocate resource'
|
30
|
+
begin_at = Time.new
|
31
|
+
option = {image_id: configuration.image}
|
32
|
+
self.node = Node.new(*Cloud.create(option).first)
|
33
|
+
during = sprintf('%.2f', Time.new - begin_at)
|
34
|
+
RunLogger.level(1).detail "allocated(#{during}s)"
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_image
|
38
|
+
action :image
|
39
|
+
node.stop
|
40
|
+
new_image = Cloud.create_image node.instance.id
|
41
|
+
RunLogger.level(1).detail "New Image Id: #{new_image.id}"
|
42
|
+
[
|
43
|
+
"To reduce the system setup, you might want to change larrow.yml.",
|
44
|
+
" You can replace init step with the follow contents:",
|
45
|
+
" image: #{new_image.id}"
|
46
|
+
].each{|s| RunLogger.level(1).detail s}
|
47
|
+
|
48
|
+
new_image
|
49
|
+
end
|
50
|
+
|
51
|
+
def deploy
|
52
|
+
action :deploy
|
53
|
+
RunLogger.level(1).detail "application is deploy on: #{node.host}"
|
54
|
+
node
|
55
|
+
end
|
56
|
+
|
57
|
+
def dump
|
58
|
+
{nodes:[node.dump]}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Larrow::Runner
|
2
|
+
module Model
|
3
|
+
class Node
|
4
|
+
include Larrow::Runner::Service
|
5
|
+
include Larrow::Qingcloud
|
6
|
+
attr_accessor :instance, :eip
|
7
|
+
attr_accessor :user,:host
|
8
|
+
def initialize instance, eip, user='root'
|
9
|
+
self.instance = instance
|
10
|
+
self.eip = eip
|
11
|
+
self.host = eip.address
|
12
|
+
self.user = user
|
13
|
+
@executor = Executor.new host, user, nil, nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute command, base_dir:nil
|
17
|
+
block = if block_given?
|
18
|
+
-> (data) { yield data }
|
19
|
+
else
|
20
|
+
-> (data) {
|
21
|
+
data.split(/\r?\n/).each do |msg|
|
22
|
+
RunLogger.level(1).info msg
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
@executor.execute command, base_dir: base_dir, &block
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop
|
30
|
+
self.instance = instance.stop
|
31
|
+
end
|
32
|
+
|
33
|
+
def destroy
|
34
|
+
instance.destroy.force
|
35
|
+
eip.destroy.force
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def dump
|
40
|
+
{
|
41
|
+
instance:{id: instance.id},
|
42
|
+
eip:{id:eip.id, address:eip.address}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.show resources, level=0
|
47
|
+
resources.map do |hash|
|
48
|
+
node = load_obj hash
|
49
|
+
RunLogger.level(level).info "instance: #{node.instance.id}"
|
50
|
+
RunLogger.level(level).info "eip:"
|
51
|
+
RunLogger.level(level+1).info "id: #{node.eip.id}"
|
52
|
+
RunLogger.level(level+1).info "address: #{node.eip.address}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.cleanup resources
|
57
|
+
resources.map do |hash|
|
58
|
+
node = load_obj hash
|
59
|
+
future{node.destroy}
|
60
|
+
end.map do |instance|
|
61
|
+
RunLogger.detail "node cleaned: #{instance.address}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.load_obj data
|
66
|
+
instance = Instance.new data[:instance][:id]
|
67
|
+
eip = Eip.new data[:eip][:id],address:data[:eip][:address]
|
68
|
+
new instance,eip
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'larrow/qingcloud'
|
2
|
+
|
3
|
+
module Larrow
|
4
|
+
module Runner
|
5
|
+
module Service
|
6
|
+
class Cloud
|
7
|
+
include Qingcloud
|
8
|
+
def initialize args={}
|
9
|
+
Qingcloud.remove_connection
|
10
|
+
access_id = args[:qy_access_key_id]
|
11
|
+
secret_key = args[:qy_secret_access_key]
|
12
|
+
zone_id = args[:zone_id]
|
13
|
+
@keypair_id = args[:keypair_id]
|
14
|
+
Qingcloud.establish_connection access_id,secret_key,zone_id
|
15
|
+
end
|
16
|
+
|
17
|
+
# return: Array< [ instance,eip ] >
|
18
|
+
# WARN: eips contains promise object, so it should be force
|
19
|
+
def create image_id:nil,count:1
|
20
|
+
RunLogger.level(1).detail "assign node"
|
21
|
+
instances = Instance.create(image_id: image_id||'trustysrvx64c',
|
22
|
+
count:count,
|
23
|
+
login_mode:'keypair',
|
24
|
+
keypair_id: @keypair_id
|
25
|
+
)
|
26
|
+
|
27
|
+
eips = Eip.create(count:count)
|
28
|
+
|
29
|
+
(0...count).map do |i|
|
30
|
+
RunLogger.level(1).detail "bind ip: #{eips[i].address}"
|
31
|
+
eips[i] = eips[i].associate instances[i].id
|
32
|
+
[ instances[i], eips[i] ]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# return image future
|
37
|
+
def create_image instance_id
|
38
|
+
Image.create instance_id
|
39
|
+
end
|
40
|
+
|
41
|
+
def image? image_id
|
42
|
+
Image.list(:self, ids: [image_id]).size == 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_available
|
46
|
+
KeyPair.list
|
47
|
+
rescue
|
48
|
+
Qingcloud.remove_connection
|
49
|
+
raise $!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'net/scp'
|
3
|
+
|
4
|
+
module Larrow
|
5
|
+
module Runner
|
6
|
+
module Service
|
7
|
+
class Executor
|
8
|
+
attr_accessor :ip, :user, :port, :password
|
9
|
+
def initialize ip, user, port, password
|
10
|
+
self.ip = ip
|
11
|
+
self.user = user
|
12
|
+
self.port = port
|
13
|
+
self.password = password
|
14
|
+
@canceling = nil
|
15
|
+
@dlogger = RunLogger #::Logger.new "#{ip}_cmd.log"
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute cmd, base_dir:nil
|
19
|
+
connection.open_channel do |ch|
|
20
|
+
RunLogger.level(1).detail "# #{cmd}"
|
21
|
+
cmd = "cd #{base_dir}; #{cmd}" unless base_dir.nil?
|
22
|
+
errmsg = ''
|
23
|
+
ch.exec cmd do |ch,success|
|
24
|
+
if RunOption.key? :debug
|
25
|
+
ch.on_data{ |c, data| yield data }
|
26
|
+
ch.on_extended_data{ |c, type, data| yield data }
|
27
|
+
else
|
28
|
+
ch.on_extended_data{ |c, type, data| errmsg << data }
|
29
|
+
end
|
30
|
+
ch.on_request('exit-status') do |c,data|
|
31
|
+
status = data.read_long
|
32
|
+
if status != 0
|
33
|
+
fail ExecutionError,{cmd:cmd,
|
34
|
+
errmsg: errmsg,
|
35
|
+
status: status}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
trap("INT") { @canceling = true }
|
41
|
+
connection.loop(0.1) do
|
42
|
+
not (@canceling || connection.channels.empty?)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def scp local_file_path, remote_file_path
|
47
|
+
raise 'not completed.'
|
48
|
+
end
|
49
|
+
|
50
|
+
def connection
|
51
|
+
@connection ||= Net::SSH.start(ip,user)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
module Larrow
|
3
|
+
module Runner
|
4
|
+
module Session
|
5
|
+
extend self
|
6
|
+
|
7
|
+
FILE = "#{ENV['HOME']}/.larrow"
|
8
|
+
def login
|
9
|
+
return unless check_file
|
10
|
+
puts "The larrow config will be generated at #{FILE}."
|
11
|
+
data = nil
|
12
|
+
loop do
|
13
|
+
data = [:qy_access_key_id,
|
14
|
+
:qy_secret_access_key,
|
15
|
+
:zone_id,
|
16
|
+
:keypair_id].
|
17
|
+
reduce({}){|s,key| s.update key => value_for(key)}
|
18
|
+
|
19
|
+
cloud = Service::Cloud.new data
|
20
|
+
begin
|
21
|
+
cloud.check_available
|
22
|
+
RunLogger.info "login success! write to ~/.larrow"
|
23
|
+
break
|
24
|
+
rescue Exception => e
|
25
|
+
RunLogger.info "login fail: #{e.message}"
|
26
|
+
return unless ask "try again"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
content={'qingcloud' => data}
|
30
|
+
File.write FILE, YAML.dump(content)
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_cloud
|
34
|
+
args = begin
|
35
|
+
YAML.
|
36
|
+
load(File.read FILE).
|
37
|
+
with_indifferent_access[:qingcloud]
|
38
|
+
rescue
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
Service::Cloud.new args if args
|
42
|
+
end
|
43
|
+
|
44
|
+
def value_for name
|
45
|
+
print sprintf("%25s: ", name)
|
46
|
+
v = $stdin.gets.strip
|
47
|
+
v.empty? ? nil : v
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_file
|
51
|
+
return true unless File.exist?(FILE)
|
52
|
+
puts "#{FILE} does exist: "
|
53
|
+
puts File.read(FILE)
|
54
|
+
RunOption[:force] || ask("overwrite #{FILE}")
|
55
|
+
end
|
56
|
+
|
57
|
+
def ask title
|
58
|
+
print "#{title} ? (yes/[no]) "
|
59
|
+
v = $stdin.gets.strip
|
60
|
+
['yes','y','Y','Yes','YES'].include? v
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Larrow::Runner::Vcs
|
2
|
+
class Base
|
3
|
+
attr_accessor :larrow_file
|
4
|
+
include Larrow::Runner
|
5
|
+
def configuration merge=true
|
6
|
+
configuration = Manifest.configuration(self)
|
7
|
+
if merge
|
8
|
+
Manifest.add_base_scripts configuration,self
|
9
|
+
end
|
10
|
+
configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def formatted_url
|
14
|
+
raise 'not implement yet'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Larrow::Runner
|
2
|
+
module Vcs
|
3
|
+
class FileSystem < Base
|
4
|
+
# path: absulute path of LarrowFile
|
5
|
+
attr_accessor :project_folder
|
6
|
+
def initialize path
|
7
|
+
if File.file? path
|
8
|
+
path = File.absolute_path path
|
9
|
+
self.larrow_file = File.basename path
|
10
|
+
self.project_folder = File.dirname path
|
11
|
+
else # directory
|
12
|
+
self.project_folder = File.absolute_path path
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def formatted_url
|
17
|
+
self.project_folder
|
18
|
+
end
|
19
|
+
|
20
|
+
def get filename
|
21
|
+
file_path = "#{project_folder}/#{filename}"
|
22
|
+
return nil unless File.exist? file_path
|
23
|
+
|
24
|
+
File.read(file_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_source node, target_dir
|
28
|
+
command = rsync_command node.user, node.host,target_dir
|
29
|
+
invoke command
|
30
|
+
invoke "ssh-keygen -R #{node.host} 2>&1"
|
31
|
+
end
|
32
|
+
|
33
|
+
def rsync_command user, host, target_dir
|
34
|
+
ssh_path = '%s@%s:%s' % [user, host, target_dir]
|
35
|
+
|
36
|
+
excludes = (get('.gitignore')||''). # rsync exclude according .gitignore
|
37
|
+
split(/[\r\n]/). #
|
38
|
+
select{|s| s =~ /^[^#]/}. # not commented
|
39
|
+
compact. # not blank
|
40
|
+
unshift('.git'). # .git itself is ignored
|
41
|
+
map{|s| "--exclude '#{s}'" } # build rsync exclude arguments
|
42
|
+
|
43
|
+
ssh_options = "-e 'ssh -o StrictHostKeyChecking=no'"
|
44
|
+
|
45
|
+
rsync_options = "-az #{ssh_options} #{excludes.join ' '}"
|
46
|
+
rsync_options += ' -v' if RunOption.key? :debug
|
47
|
+
|
48
|
+
"rsync #{rsync_options} #{project_folder}/ '#{ssh_path}' 2>&1"
|
49
|
+
end
|
50
|
+
def invoke command
|
51
|
+
`#{command}`.split(/\r?\n/).each do |msg|
|
52
|
+
RunLogger.level(1).info msg
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module Larrow::Runner::Vcs
|
4
|
+
class Github < Base
|
5
|
+
URL_TEMPLATE='https://raw.githubusercontent.com/%s/%s/%s/%s'
|
6
|
+
attr_accessor :organize, :name, :branch
|
7
|
+
# url sample:
|
8
|
+
# git@github.com:fsword/larrow-qingcloud.git
|
9
|
+
# https://github.com/fsword/larrow-qingcloud.git
|
10
|
+
def initialize url
|
11
|
+
self.branch = 'master'
|
12
|
+
case url
|
13
|
+
when /git@github\.com:(.+)\/(.+)\.git/
|
14
|
+
self.organize = $1
|
15
|
+
self.name = $2
|
16
|
+
when /http.:\/\/github.com\/(.+)\/(.+)\.git/
|
17
|
+
self.organize = $1
|
18
|
+
self.name = $2
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def formatted_url
|
23
|
+
'git@github.com:%s/%s.git' % [organize, name]
|
24
|
+
end
|
25
|
+
|
26
|
+
def get filename
|
27
|
+
url = URL_TEMPLATE % [organize, name, branch, filename]
|
28
|
+
resp = Faraday.get(url)
|
29
|
+
case resp.status
|
30
|
+
when 200
|
31
|
+
resp.body
|
32
|
+
when 404
|
33
|
+
nil
|
34
|
+
else
|
35
|
+
raise resp.body
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def update_source node, target_dir
|
40
|
+
template = ["git clone ",
|
41
|
+
"--depth 1",
|
42
|
+
"http://github.com/%s/%s.git",
|
43
|
+
"-b %s %s"].join(' ')
|
44
|
+
cmd = template % [organize, name, branch, target_dir]
|
45
|
+
node.execute cmd
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'larrow/runner/vcs/base'
|
2
|
+
module Larrow
|
3
|
+
module Runner
|
4
|
+
# Access source code from Version Control System
|
5
|
+
# eg: Subversion, Github, LocalStore
|
6
|
+
module Vcs
|
7
|
+
autoload :Github, 'larrow/runner/vcs/github'
|
8
|
+
autoload :FileSystem,'larrow/runner/vcs/file_system'
|
9
|
+
def self.detect url
|
10
|
+
case url
|
11
|
+
when /github\.com.+\.git$/
|
12
|
+
Github.new(url)
|
13
|
+
else # local file/folder
|
14
|
+
fail "cannot recognized: #{url}" unless File.exist? url
|
15
|
+
FileSystem.new url
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
require 'active_support/core_ext/hash'
|
3
|
+
|
4
|
+
require "larrow/runner/version"
|
5
|
+
require 'larrow/runner/logger'
|
6
|
+
require 'larrow/runner/service'
|
7
|
+
require 'larrow/runner/session'
|
8
|
+
|
9
|
+
require 'larrow/runner/errors'
|
10
|
+
|
11
|
+
module Larrow
|
12
|
+
module Runner
|
13
|
+
# default runtime logger
|
14
|
+
RunLogger = if ENV['RUN_AS']
|
15
|
+
Logger.new "#{ENV['RUN_AS']}.log"
|
16
|
+
else
|
17
|
+
Logger.new $stdout
|
18
|
+
end
|
19
|
+
# global options
|
20
|
+
RunOption = {}.with_indifferent_access
|
21
|
+
# cloud wrapper
|
22
|
+
Cloud = Session.load_cloud
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'larrow/runner/vcs'
|
27
|
+
require 'larrow/runner/manifest'
|
28
|
+
require 'larrow/runner/helper'
|
29
|
+
|
30
|
+
require 'larrow/runner/manager'
|
31
|
+
require 'larrow/runner/cli'
|
32
|
+
require 'larrow/runner/model/app'
|
33
|
+
require 'larrow/runner/model/node'
|
34
|
+
|