yggdrasil 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,9 @@
1
1
  class Yggdrasil
2
- VERSION = "0.0.5"
3
- CMD = File::basename($0)
2
+ VERSION = "0.0.6"
4
3
 
5
- def Yggdrasil.version
4
+ def version
6
5
  puts <<"EOS"
7
- #{CMD}, version #{VERSION}
6
+ #@base_cmd, version #{VERSION}
8
7
 
9
8
  Copyright (C) 2012-2013 Tomohisa Kusukawa.
10
9
  Yggdrasil is open source software, see https://github.com/tkusukawa/yggdrasil/
@@ -0,0 +1,102 @@
1
+ module YggdrasilCommon
2
+
3
+ =begin
4
+ def method_missing(action, *args)
5
+ self.class.send action, *args
6
+ end
7
+ =end
8
+
9
+ # load config value from config file
10
+ def read_config(config_file)
11
+ configs = Hash.new
12
+ begin
13
+ File.open(config_file) do |file|
14
+ l = 0
15
+ while (line = file.gets)
16
+ l += 1
17
+ next if /^\s*#.*$/ =~ line # comment line
18
+ if /^\s*(\S+)\s*=\s*(\S+).*$/ =~ line
19
+ key, val = $1, $2
20
+ key.gsub!(/-/, '_')
21
+ configs[key.to_sym] = val
22
+ else
23
+ error "syntax error. :#{config_file}(#{l})"
24
+ end
25
+ end
26
+ end
27
+ rescue
28
+ error "can not open config file: #{config_file}"
29
+ end
30
+ configs
31
+ end
32
+
33
+ def parse_options(args, valid_params)
34
+ valid_params['--debug'] = :debug? # common
35
+ @options ||= Hash.new
36
+ pos = 0
37
+ while args.size > pos
38
+ if valid_params.has_key?(args[pos])
39
+ option_note = args[pos]
40
+ option_key = valid_params[option_note]
41
+ args = args[0...pos]+args[pos+1..-1]
42
+ if option_key.to_s[-1,1] == '?'
43
+ @options[option_key] = true
44
+ else
45
+ error "Not enough arguments provided: #{option_note}" unless args.size > pos
46
+ option_value = args[pos].dup
47
+ args = args[0...pos]+args[pos+1..-1]
48
+ @options[option_key] = option_value
49
+ end
50
+ next
51
+ end
52
+ pos += 1
53
+ end
54
+ args
55
+ end
56
+
57
+ def input_user_pass
58
+ until @options.has_key?(:username) do
59
+ error "Can't get username or password" if @options.has_key?(:non_interactive?)
60
+ print "Input svn username: "
61
+ input = $stdin.gets
62
+ error "can not input username" unless input
63
+ input.chomp!
64
+ return if input.size == 0
65
+ @options[:username] = @options[:ro_username] = input
66
+ end
67
+ until @options.has_key?(:password) do
68
+ error "Can't get username or password" if @options.has_key?(:non_interactive?)
69
+ print "Input svn password: "
70
+ #input = `sh -c 'read -s hoge;echo $hoge'`
71
+ system3 'stty -echo', false
72
+ input = $stdin.gets
73
+ system3 'stty echo', false
74
+ puts
75
+ error "can not input password" unless input
76
+ input.chomp!
77
+ @options[:password] = @options[:ro_password] = input
78
+ end
79
+ end
80
+
81
+ # @param [String] cmd
82
+ def system3(cmd, err_exit=true)
83
+ out = `#{cmd} 2>&1`
84
+ stat = $?
85
+
86
+ unless stat.success?
87
+ return nil unless err_exit
88
+ $stderr.puts "#@base_cmd error: command failure: #{cmd}"
89
+ $stderr.puts "command output:"
90
+ $stderr.puts out
91
+ exit stat.exitstatus
92
+ end
93
+ out
94
+ end
95
+
96
+ # @param [String] msg
97
+ def error(msg)
98
+ $stderr.puts "#@base_cmd error: #{msg}"
99
+ $stderr.puts
100
+ exit 1
101
+ end
102
+ end
@@ -0,0 +1,77 @@
1
+ require 'socket'
2
+
3
+ require "yggdrasil_common"
4
+
5
+ require "yggdrasil_server/init_server"
6
+ require "yggdrasil_server/results"
7
+
8
+ require "yggdrasil_server/get_repo"
9
+ require "yggdrasil_server/get_ro_id_pw"
10
+ require "yggdrasil_server/put_result"
11
+
12
+ class YggdrasilServer
13
+ MESSAGE_QUIT = 'quit'
14
+ MESSAGES = {
15
+ :get_repo => [],
16
+ :get_ro_id_pw => [],
17
+ :put_result => [:hostname],
18
+ }
19
+
20
+ def initialize(exist_config = true)
21
+ @base_cmd = File::basename($0)
22
+ @current_dir = `readlink -f .`.chomp
23
+ @config_dir = "#{ENV["HOME"]}/.yggdrasil"
24
+ @server_config_file = "#@config_dir/server_config"
25
+ @results_dir = "#@config_dir/results"
26
+
27
+ return unless exist_config
28
+ configs = read_config(@server_config_file)
29
+ error "need 'port' in config file" unless (@port = configs[:port])
30
+ error "need 'repo' in config file" unless (@repo = configs[:repo])
31
+ @ro_username = configs[:ro_username] if configs.has_key?(:ro_username)
32
+ @ro_password = configs[:ro_password] if configs.has_key?(:ro_password)
33
+ end
34
+
35
+ def server(args)
36
+ args = parse_options(args, {'--debug'=>:debug?})
37
+ if args.size != 0
38
+ error "invalid arguments: #{args.join(',')}"
39
+ end
40
+
41
+ puts "Start: yggdrasil server (port:#@port)"
42
+ TCPServer.do_not_reverse_lookup = true
43
+ s0 = TCPServer.open(@port.to_i)
44
+ loop do
45
+ sock = s0.accept
46
+ msg = sock.gets # first line
47
+ if msg && msg.chomp! != MESSAGE_QUIT
48
+ msg.chomp!
49
+ puts "RCV: #{msg}"
50
+ msg_parts = msg.split
51
+ if msg_parts.size != 0
52
+ msg_cmd = msg_parts[0]
53
+ part_names = MESSAGES[msg_cmd.to_sym]
54
+ if (msg_parts.size - 1) == part_names.size
55
+ # make hash of args
56
+ msg_arg_hash = Hash.new
57
+ (0...part_names.size).each do |i|
58
+ msg_arg_hash[part_names[i]] = msg_parts[i+1]
59
+ end
60
+
61
+ # execute request (msg_cmd == method name)
62
+ send msg_cmd, sock, msg_arg_hash
63
+ else
64
+ puts "fail: number of arguments is mismatch: #{msg}"
65
+ end
66
+ end
67
+ end
68
+ sock.close
69
+ break if @options.has_key?(:debug?) && msg == MESSAGE_QUIT
70
+ end
71
+ s0.close # MESSAGE_QUIT
72
+ end
73
+
74
+ protected
75
+ include YggdrasilCommon
76
+ end
77
+
@@ -0,0 +1,6 @@
1
+ class YggdrasilServer
2
+ def get_repo(sock, arg_hash = {})
3
+ sock.puts @repo
4
+ arg_hash
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ class YggdrasilServer
2
+ def get_ro_id_pw(sock, arg_hash = {})
3
+ if @ro_username
4
+ sock.puts @ro_username
5
+ sock.puts @ro_password
6
+ end
7
+ arg_hash
8
+ end
9
+ end
@@ -0,0 +1,62 @@
1
+ class YggdrasilServer
2
+
3
+ # @param [Array] args
4
+ def init_server(args)
5
+
6
+ args = parse_options(args,
7
+ {'--port'=>:port, '--repo'=>:repo,
8
+ '--ro-username'=>:ro_username, '--ro-password'=>:ro_password})
9
+ if args.size != 0
10
+ error "invalid arguments: #{args.join(',')}"
11
+ end
12
+
13
+ if !@options.has_key?(:ro_username) && @options.has_key?(:ro_password)
14
+ error "--ro-password option need --ro-username, too."
15
+ end
16
+
17
+ until @options.has_key?(:port)
18
+ print "Input tcp port number: "
19
+ input = $stdin.gets
20
+ error "can not input tcp port number" unless input
21
+
22
+ input.chomp!
23
+ unless %r{\d+} =~ input && input.to_i < 0x10000
24
+ puts "ERROR: Invalid port number."
25
+ redo
26
+ end
27
+ @options[:port] = input # string
28
+ end
29
+
30
+ until @options.has_key?(:repo)
31
+ print "Input svn repo URL: "
32
+ input = $stdin.gets
33
+ error "can not input svn repo URL." unless input
34
+
35
+ unless %r{^(http://|https://|file://|svn://)} =~ input
36
+ puts "ERROR: Invalid URL."
37
+ redo
38
+ end
39
+ @options[:repo] = input
40
+ end
41
+ @options[:repo].chomp!
42
+ @options[:repo].chomp!('/')
43
+
44
+ unless @options.has_key?(:ro_password)
45
+ puts "Input read-only username/password (clients use this to read repo)."
46
+ puts "ATTENTION! username/password are stored to disk unencrypted!"
47
+ input_user_pass
48
+ end
49
+
50
+ # make config file
51
+ Dir.mkdir @config_dir, 0755 unless File.exist?(@config_dir)
52
+ File.open(@server_config_file, "w") do |f|
53
+ f.write "port=#{@options[:port]}\n"\
54
+ "repo=#{@options[:repo]}\n"
55
+ if @options.has_key?(:ro_password)
56
+ f.write "ro_username=#{@options[:ro_username]}\n"\
57
+ "ro_password=#{@options[:ro_password]}\n"
58
+ end
59
+ end
60
+ end
61
+ end
62
+
@@ -0,0 +1,16 @@
1
+ class YggdrasilServer
2
+ def put_result(sock, arg_hash = {})
3
+ result_string = ""
4
+ while (line=sock.gets)
5
+ result_string << line
6
+ end
7
+ # make result file
8
+ Dir.mkdir @results_dir, 0755 unless File.exist?(@results_dir)
9
+ result_file = "#@results_dir/#{arg_hash[:hostname]}_#{sock.peeraddr[3]}"
10
+ File.delete result_file if File.exist?(result_file)
11
+ File.open(result_file, "w") do |f|
12
+ f.write result_string
13
+ end
14
+ arg_hash
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ class YggdrasilServer
2
+
3
+ # @param [Array] args
4
+ def results(args)
5
+ args = parse_options(args, {'--limit'=>:limit})
6
+
7
+ if args.size != 0
8
+ error "invalid arguments: #{args.join(',')}"
9
+ end
10
+
11
+ return unless File.exist?(@results_dir)
12
+ files = Dir.entries(@results_dir)
13
+ files.each do |file|
14
+ next if /^\./ =~ file
15
+ absolute = "#@results_dir/#{file}"
16
+ if @options.has_key?(:limit)
17
+ stat = File.stat(absolute)
18
+ if stat.mtime < (Time.now - @options[:limit].to_i * 60)
19
+ puts "######## #{file} TOO OLD: #{stat.mtime.to_s}"
20
+ next
21
+ end
22
+ end
23
+ buf = File.read("#@results_dir/#{file}")
24
+ if buf.gsub(/\s*\n/m, '') != ''
25
+ puts "######## #{file} Mismatch:"
26
+ puts buf
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,145 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Yggdrasil, "check" do
4
+ it '-------- check' do
5
+ puts '-------- check'
6
+ prepare_environment
7
+ init_yggdrasil
8
+ end
9
+
10
+ it 'should execute checker and svn add the result' do
11
+ puts "\n---- should execute checker and svn add the result"
12
+ `rm -f /tmp/yggdrasil-test/.yggdrasil/checker/gem_list`
13
+ `echo 'echo hoge' > /tmp/yggdrasil-test/.yggdrasil/checker/hoge`
14
+ `chmod +x /tmp/yggdrasil-test/.yggdrasil/checker/hoge`
15
+
16
+ cmd = %w{check --username hoge --password foo}
17
+ out = catch_out {Yggdrasil.command(cmd)}
18
+ out.should == <<"EOS"
19
+ A 0 tmp/yggdrasil-test/.yggdrasil/checker_result
20
+ A 0 tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
21
+ A 0 tmp/yggdrasil-test/.yggdrasil
22
+
23
+ Index: tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
24
+ ===================================================================
25
+ --- tmp/yggdrasil-test/.yggdrasil/checker_result/hoge (revision 0)
26
+ +++ tmp/yggdrasil-test/.yggdrasil/checker_result/hoge (revision 0)
27
+ @@ -0,0 +1 @@
28
+ +hoge
29
+ EOS
30
+ end
31
+
32
+ it 'should commit the checker result' do
33
+ puts "\n---- should commit the checker result"
34
+ cmd = %w{commit / --username hoge --password foo --non-interactive -m add\ checker}
35
+ out = catch_out {Yggdrasil.command cmd}
36
+
37
+ out.should == <<"EOS"
38
+ Adding tmp/yggdrasil-test/.yggdrasil
39
+ Adding tmp/yggdrasil-test/.yggdrasil/checker_result
40
+ Adding tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
41
+ Transmitting file data .
42
+ Committed revision 4.
43
+ EOS
44
+ end
45
+
46
+ it 'should delete result if checker deleted' do
47
+ puts "\n---- should delete result if checker deleted"
48
+ `rm -f /tmp/yggdrasil-test/.yggdrasil/checker/hoge`
49
+ cmd = %w{check --username hoge --password foo}
50
+ out = catch_out {Yggdrasil.command(cmd)}
51
+ out.should == <<"EOS"
52
+ D 4 tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
53
+
54
+ Index: tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
55
+ ===================================================================
56
+ --- tmp/yggdrasil-test/.yggdrasil/checker_result/hoge (revision 4)
57
+ +++ tmp/yggdrasil-test/.yggdrasil/checker_result/hoge (working copy)
58
+ @@ -1 +0,0 @@
59
+ -hoge
60
+ EOS
61
+ end
62
+
63
+ it 'should commit the checker result(delete)' do
64
+ puts "\n---- should commit the checker result(delete)"
65
+ cmd = %w{commit / --username hoge --password foo --non-interactive -m delete\ checker}
66
+ out = catch_out {Yggdrasil.command cmd}
67
+
68
+ out.should == <<"EOS"
69
+ Deleting tmp/yggdrasil-test/.yggdrasil/checker_result/hoge
70
+
71
+ Committed revision 5.
72
+ EOS
73
+ end
74
+
75
+ it 'should record check result by yggdrasil server' do
76
+ puts "\n---- should record check result by yggdrasil server"
77
+
78
+ prepare_environment
79
+
80
+ sock = 0
81
+ begin
82
+ sock = TCPSocket.open("localhost", 4000)
83
+ rescue
84
+ puts "OK. no server"
85
+ else
86
+ puts "NG. zombie server. try quit"
87
+ sock.puts("quit")
88
+ sock.close
89
+ end
90
+
91
+ Yggdrasil.command %w{init-server} +
92
+ %w{--port 4000} +
93
+ %w{--repo svn://localhost/tmp/yggdrasil-test/svn-repo/servers/{HOST}/}+
94
+ %w{--ro-username hoge --ro-password foo},
95
+ "\n\n"
96
+ fork do
97
+ Yggdrasil.command %w{server --debug}
98
+ end
99
+
100
+ sleep 1
101
+ Yggdrasil.command %w{init --debug --server localhost:4000} +
102
+ %w{--username hoge --password foo},
103
+ "Y\nhoge\nfoo\n"
104
+ `rm -f /tmp/yggdrasil-test/.yggdrasil/checker/gem_list`
105
+ Yggdrasil.command %w{check}
106
+
107
+ sleep 1
108
+ File.exist?("/tmp/yggdrasil-test/.yggdrasil/results").should be_true
109
+ files = Dir.entries("/tmp/yggdrasil-test/.yggdrasil/results")
110
+ result_files = files.select{|file| %r{^#{Socket.gethostname}} =~ file}
111
+ result_files.size.should == 1
112
+ `cat /tmp/yggdrasil-test/.yggdrasil/results/#{result_files[0]}`.should == <<"EOS"
113
+ A 0 tmp/yggdrasil-test
114
+ A 0 tmp/yggdrasil-test/.yggdrasil
115
+ A 0 tmp/yggdrasil-test/.yggdrasil/checker_result
116
+ A 0 tmp
117
+
118
+ EOS
119
+
120
+ `echo hoge > /tmp/yggdrasil-test/A`
121
+ Yggdrasil.command %w{add /tmp/yggdrasil-test/A}
122
+ Yggdrasil.command %w{commit --username hoge --password foo /},
123
+ "Y\nHOGE\n"
124
+
125
+ `echo foo >> /tmp/yggdrasil-test/A`
126
+ Yggdrasil.command %w{check}
127
+
128
+ `cat /tmp/yggdrasil-test/.yggdrasil/results/#{result_files[0]}`.should == <<"EOS"
129
+ M 2 tmp/yggdrasil-test/A
130
+
131
+ Index: tmp/yggdrasil-test/A
132
+ ===================================================================
133
+ --- tmp/yggdrasil-test/A (revision 2)
134
+ +++ tmp/yggdrasil-test/A (working copy)
135
+ @@ -1 +1,2 @@
136
+ hoge
137
+ +foo
138
+ EOS
139
+
140
+ sock = TCPSocket.open("localhost", 4000)
141
+ sock.puts("quit")
142
+ sock.close
143
+ Process.waitall
144
+ end
145
+ end