yggdrasil 0.0.5 → 0.0.6

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.
@@ -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