larrow-runner 0.0.1
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 +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
|
+
|