You can subscribe to this list here.
2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(2) |
Dec
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
2009 |
Jan
(3) |
Feb
(15) |
Mar
|
Apr
(9) |
May
|
Jun
(6) |
Jul
|
Aug
|
Sep
(23) |
Oct
(25) |
Nov
(44) |
Dec
(9) |
2010 |
Jan
(14) |
Feb
|
Mar
(4) |
Apr
(1) |
May
|
Jun
(3) |
Jul
|
Aug
(4) |
Sep
|
Oct
(10) |
Nov
(4) |
Dec
(22) |
2011 |
Jan
(14) |
Feb
|
Mar
(3) |
Apr
(7) |
May
(16) |
Jun
(4) |
Jul
(6) |
Aug
(3) |
Sep
|
Oct
(4) |
Nov
|
Dec
|
2012 |
Jan
|
Feb
|
Mar
(10) |
Apr
(24) |
May
|
Jun
|
Jul
(2) |
Aug
(2) |
Sep
|
Oct
|
Nov
|
Dec
|
From: <jh...@us...> - 2009-09-23 00:53:27
|
Revision: 94 http://etch.svn.sourceforge.net/etch/?rev=94&view=rev Author: jheiss Date: 2009-09-23 00:53:21 +0000 (Wed, 23 Sep 2009) Log Message: ----------- Add test of filename with special characters. Modified Paths: -------------- trunk/test/file.rb Modified: trunk/test/file.rb =================================================================== --- trunk/test/file.rb 2009-09-23 00:53:07 UTC (rev 93) +++ trunk/test/file.rb 2009-09-23 00:53:21 UTC (rev 94) @@ -652,7 +652,45 @@ # Verify that the file contents didn't change assert_equal(origcontents, get_file_contents(@targetfile), 'contradictory script instructions') + end + + def test_filename_with_special_characters + # + # Test filename with special characters + # + testname = 'filename with special characters' + # + because urlencode and CGI.escape handle it differently, so we want to + # catch any possible mismatches where one format is used on encode and the + # other on decode. + # [] because they have special meaning to the Rails parameter parsing and + # we want to make sure that doesn't get confused. + specialtargetfile = "#{@targetfile}+[]" + FileUtils.mkdir_p("#{@repodir}/source/#{specialtargetfile}") + File.open("#{@repodir}/source/#{specialtargetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file/> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{specialtargetfile}/source", 'w') do |file| + file.write(sourcecontents) + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase) + + # Verify that the file was created properly + assert_equal(sourcecontents, get_file_contents(specialtargetfile), testname) end def teardown This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-09-23 00:53:19
|
Revision: 93 http://etch.svn.sourceforge.net/etch/?rev=93&view=rev Author: jheiss Date: 2009-09-23 00:53:07 +0000 (Wed, 23 Sep 2009) Log Message: ----------- Put killswitch test in its own method. Modified Paths: -------------- trunk/test/options.rb Modified: trunk/test/options.rb =================================================================== --- trunk/test/options.rb 2009-09-14 20:13:25 UTC (rev 92) +++ trunk/test/options.rb 2009-09-23 00:53:07 UTC (rev 93) @@ -26,7 +26,7 @@ #puts "Using #{@testbase} as client working directory" end - def test_dryrun + def test_killswitch # # Test killswitch (not really a command-line option, but seems to # fit best in this file) @@ -67,9 +67,9 @@ run_etch(@port, @testbase, true) assert_equal(origcontents, get_file_contents(@targetfile), 'killswitch') - - File.delete("#{@repodir}/killswitch") - + end + + def test_dryrun # # Test --dry-run # This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-09-14 20:13:33
|
Revision: 92 http://etch.svn.sourceforge.net/etch/?rev=92&view=rev Author: jheiss Date: 2009-09-14 20:13:25 +0000 (Mon, 14 Sep 2009) Log Message: ----------- Updated per recent migrations Modified Paths: -------------- trunk/server/db/schema.rb Modified: trunk/server/db/schema.rb =================================================================== --- trunk/server/db/schema.rb 2009-09-14 20:12:12 UTC (rev 91) +++ trunk/server/db/schema.rb 2009-09-14 20:13:25 UTC (rev 92) @@ -14,10 +14,12 @@ create_table "clients", :force => true do |t| t.string "name", :null => false t.integer "status" + t.text "message" t.datetime "created_at" t.datetime "updated_at" end + add_index "clients", ["updated_at"], :name => "index_clients_on_updated_at" add_index "clients", ["status"], :name => "index_clients_on_status" add_index "clients", ["name"], :name => "index_clients_on_name", :unique => true @@ -63,6 +65,7 @@ t.datetime "updated_at" end + add_index "results", ["created_at"], :name => "index_results_on_created_at" add_index "results", ["file"], :name => "index_results_on_file" add_index "results", ["client_id"], :name => "index_results_on_client_id" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-09-14 20:12:25
|
Revision: 91 http://etch.svn.sourceforge.net/etch/?rev=91&view=rev Author: jheiss Date: 2009-09-14 20:12:12 +0000 (Mon, 14 Sep 2009) Log Message: ----------- Update paths to reflect directory structure of SF svn repo. Modified Paths: -------------- trunk/test/etchtest.rb trunk/test/outputcapture.rb Modified: trunk/test/etchtest.rb =================================================================== --- trunk/test/etchtest.rb 2009-09-11 23:06:37 UTC (rev 90) +++ trunk/test/etchtest.rb 2009-09-14 20:12:12 UTC (rev 91) @@ -53,7 +53,7 @@ puts "Giving the server some time to start up" sleep(5) else - Dir.chdir('../server/trunk') + Dir.chdir('../server') # FIXME: silence the server (see various failed attempts...) # Causes ruby to fork, so we're left with a useless pid #exec("./script/server -d -p #{port}") @@ -81,7 +81,7 @@ puts "#" sleep 3 end - result = system("ruby ../client/trunk/etch --generate-all --server=http://localhost:#{port} --test-base=#{testbase} --key=keys/testkey #{extra_args}") + result = system("ruby ../client/etch --generate-all --server=http://localhost:#{port} --test-base=#{testbase} --key=keys/testkey #{extra_args}") if errors_expected assert(!result) else Modified: trunk/test/outputcapture.rb =================================================================== --- trunk/test/outputcapture.rb 2009-09-11 23:06:37 UTC (rev 90) +++ trunk/test/outputcapture.rb 2009-09-14 20:12:12 UTC (rev 91) @@ -9,7 +9,7 @@ require 'tempfile' require 'fileutils' require 'timeout' -$: << '../client/trunk' +$: << '../client' require 'etch' class EtchOutputCaptureTests < Test::Unit::TestCase This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-09-11 23:06:49
|
Revision: 90 http://etch.svn.sourceforge.net/etch/?rev=90&view=rev Author: jheiss Date: 2009-09-11 23:06:37 +0000 (Fri, 11 Sep 2009) Log Message: ----------- Add timeout to output capturing so that ill-behaved daemons don't cause etch to hang around forever. Add unit tests for output capturing. Note that the output capturing timeout test is commented out as the timeout is rather long, thus causing the test to take a long time to run. Not quite sure the best way to handle that. Replace instances of assert(string.include?(substring)) with assert_match(substring, string) so that the user gets a better error if the assertion fails. Verified that it does a substring match if the first arg is a string instead of a regex (a la String#sub), as the documentation doesn't make that clear. Add support for local requests. Tests of client authentication Modify start_server method to return pid in addition to port, and modify stop_server method to take a pid to stop. This allows folks to run more than one server at a time. Modify run_etch method to run etch with a test key, otherwise etch tries to use the system's host key, which won't be readable unless you run the tests as root, so etch just skips over attaching an authentication signature to requests. Modified Paths: -------------- trunk/test/actions.rb trunk/test/attributes.rb trunk/test/delete.rb trunk/test/depend.rb trunk/test/etchtest.rb trunk/test/file.rb trunk/test/history.rb trunk/test/link.rb trunk/test/nodegroups.rb trunk/test/options.rb trunk/test/scripts.rb trunk/test/transitions.rb Added Paths: ----------- trunk/test/auth.rb trunk/test/keys/ trunk/test/keys/testkey trunk/test/keys/testkey.pub trunk/test/keys/testkey2 trunk/test/keys/testkey2.pub trunk/test/local_requests.rb trunk/test/outputcapture.rb Modified: trunk/test/actions.rb =================================================================== --- trunk/test/actions.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/actions.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -19,7 +19,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -85,8 +85,8 @@ # The setup actions will get run several times as we loop # back and forth with the server sending original sums and # contents. So just verify that they were run at least once. - assert(get_file_contents("#{@repodir}/server_setup").include?("server_setup\n"), 'server_setup') - assert(get_file_contents("#{@repodir}/setup").include?("setup\n"), 'setup') + assert_match("server_setup\n", get_file_contents("#{@repodir}/server_setup"), 'server_setup') + assert_match("setup\n", get_file_contents("#{@repodir}/setup"), 'setup') assert_equal("pre\n", get_file_contents("#{@repodir}/pre"), 'pre') assert_equal( "exec_once\n", get_file_contents("#{@repodir}/exec_once"), 'exec_once') @@ -372,7 +372,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Modified: trunk/test/attributes.rb =================================================================== --- trunk/test/attributes.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/attributes.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -20,7 +20,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -627,7 +627,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Added: trunk/test/auth.rb =================================================================== --- trunk/test/auth.rb (rev 0) +++ trunk/test/auth.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -0,0 +1,259 @@ +#!/usr/bin/ruby -w + +# +# Test etch's handling of client authentication +# + +require 'test/unit' +require 'etchtest' +require 'tempfile' +require 'fileutils' +require 'net/http' +require 'rexml/document' +require 'facter' + +class EtchAuthTests < Test::Unit::TestCase + include EtchTests + + def setup + # Generate a file to use as our etch target/destination + @targetfile = Tempfile.new('etchtest').path + #puts "Using #{@targetfile} as target file" + + # Generate a directory for our test repository + @repodir = initialize_repository + @port, @pid = start_server(@repodir) + + # Create a directory to use as a working directory for the client + @testbase = tempdir + #puts "Using #{@testbase} as client working directory" + + # Make sure the server will initially think this is a new client + hostname = Facter['fqdn'].value + Net::HTTP.start('localhost', @port) do |http| + # Find our client id + response = http.get("/clients.xml?name=#{hostname}") + if !response.kind_of?(Net::HTTPSuccess) + response.error! + end + response_xml = REXML::Document.new(response.body) + client_id = nil + if response_xml.elements['/clients/client/id'] + client_id = response_xml.elements['/clients/client/id'].text + end + # Delete our client entry + if client_id + response = http.delete("/clients/#{client_id}.xml") + if !response.kind_of?(Net::HTTPSuccess) + response.error! + end + end + end + end + + # Test authentication when new clients are allowed + def test_auth_allow_new_clients + File.open(File.join(@repodir, 'etchserver.conf'), 'w') do |file| + file.puts 'auth_enabled=true' + file.puts 'auth_deny_new_clients=false' + end + + # + # New client, should work + # + testname = 'auth, allow new clients, new client' + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file/> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source", 'w') do |file| + file.write(sourcecontents) + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase) + + # Verify that the file was created properly + assert_equal(sourcecontents, get_file_contents(@targetfile), testname) + + # + # Existing client, should work + # + testname = 'auth, allow new clients, existing client' + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file/> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source", 'w') do |file| + file.write(sourcecontents) + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase) + + # Verify that the file was created properly + assert_equal(sourcecontents, get_file_contents(@targetfile), testname) + + # + # Existing client, bad signature, should be denied + # + testname = 'auth, allow new clients, existing client, bad signature' + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file/> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source", 'w') do |file| + file.write(sourcecontents) + end + + # Put some text into the original file so that we can make sure it + # is not touched. + origcontents = "This is the original text\n" + File.delete(@targetfile) + File.open(@targetfile, 'w') do |file| + file.write(origcontents) + end + + # Run etch with the wrong key to force a bad signature + #puts "Running '#{testname}' test" + run_etch(@port, @testbase, true, '--key=keys/testkey2') + + # Verify that the file was not touched + assert_equal(origcontents, get_file_contents(@targetfile), testname) + end + # Test authentication when new clients are denied + def test_auth_deny_new_clients + File.open(File.join(@repodir, 'etchserver.conf'), 'w') do |file| + file.puts 'auth_enabled=true' + file.puts 'auth_deny_new_clients=true' + end + + # + # New client, should fail + # + testname = 'auth, deny new clients, new client' + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file/> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source", 'w') do |file| + file.write(sourcecontents) + end + + # Put some text into the original file so that we can make sure it + # is not touched. + origcontents = "This is the original text\n" + File.delete(@targetfile) + File.open(@targetfile, 'w') do |file| + file.write(origcontents) + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase, true) + + # Verify that the file was not touched + assert_equal(origcontents, get_file_contents(@targetfile), testname) + + # + # Add this client to the server so that it will now be considered + # an existing client + # + puts "# Starting a second copy of the server and adding this client to the database" + sleep 3 + repodir2 = initialize_repository + port2, pid2 = start_server(repodir2) + run_etch(port2, @testbase) + stop_server(pid2) + remove_repository(repodir2) + + # + # Existing client, should work + # + testname = 'auth, deny new clients, existing client' + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file/> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source", 'w') do |file| + file.write(sourcecontents) + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase) + + # Verify that the file was created properly + assert_equal(sourcecontents, get_file_contents(@targetfile), testname) + end + + def teardown + stop_server(@pid) + remove_repository(@repodir) + FileUtils.rm_rf(@testbase) + FileUtils.rm_rf(@targetfile) + end +end + Property changes on: trunk/test/auth.rb ___________________________________________________________________ Added: svn:executable + * Modified: trunk/test/delete.rb =================================================================== --- trunk/test/delete.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/delete.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -19,7 +19,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -231,7 +231,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Modified: trunk/test/depend.rb =================================================================== --- trunk/test/depend.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/depend.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -21,7 +21,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -202,7 +202,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Modified: trunk/test/etchtest.rb =================================================================== --- trunk/test/etchtest.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/etchtest.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -48,13 +48,13 @@ def start_server(repodir) ENV['etchserverbase'] = repodir # Pick a random port in the 3001-6000 range (range somewhat randomly chosen) - port = 3001 + rand(2999) - if @serverpid = fork + port = 3001 + rand(3000) + if pid = fork puts "Giving the server some time to start up" sleep(5) else Dir.chdir('../server/trunk') - #Dir.chdir('../server/branches/libxml') + # FIXME: silence the server (see various failed attempts...) # Causes ruby to fork, so we're left with a useless pid #exec("./script/server -d -p #{port}") # Causes ruby to invoke a copy of sh to run the command (to handle @@ -62,12 +62,12 @@ #exec("./script/server -p #{port} > /dev/null") exec("./script/server -p #{port}") end - port + [port, pid] end - def stop_server - Process.kill('TERM', @serverpid) - Process.waitpid(@serverpid) + def stop_server(pid) + Process.kill('TERM', pid) + Process.waitpid(pid) end def run_etch(port, testbase, errors_expected=false, extra_args='') @@ -80,9 +80,12 @@ puts "# Errors expected here" puts "#" sleep 3 - assert(!system("ruby ../client/trunk/etch --generate-all --server=http://localhost:#{port} --test-base=#{testbase} #{extra_args}")) + end + result = system("ruby ../client/trunk/etch --generate-all --server=http://localhost:#{port} --test-base=#{testbase} --key=keys/testkey #{extra_args}") + if errors_expected + assert(!result) else - assert(system("ruby ../client/trunk/etch --generate-all --server=http://localhost:#{port} --test-base=#{testbase} #{extra_args}")) + assert(result) end end Modified: trunk/test/file.rb =================================================================== --- trunk/test/file.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/file.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -19,7 +19,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -656,7 +656,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Modified: trunk/test/history.rb =================================================================== --- trunk/test/history.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/history.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -20,7 +20,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -220,7 +220,7 @@ assert_equal(@destfile, File.readlink(origfile), 'original backup of link') system("cd #{historydir} && co -q -f -r1.1 #{historyfile}") - assert(get_file_contents(historyfile).include?("#{@targetfile} -> #{@destfile}"), 'history backup of link') + assert_match("#{@targetfile} -> #{@destfile}", get_file_contents(historyfile), 'history backup of link') end def test_history_directory @@ -275,7 +275,7 @@ assert_equal(before_mode, File.stat(origfile).mode, 'original directory mode') # Check that the history log looks reasonable, it should contain an # 'ls -ld' of the directory - assert(get_file_contents(historyfile).include?(" #{@targetfile}"), 'history backup of directory') + assert_match(" #{@targetfile}", get_file_contents(historyfile), 'history backup of directory') end def test_history_directory_contents @@ -328,7 +328,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Added: trunk/test/keys/testkey =================================================================== --- trunk/test/keys/testkey (rev 0) +++ trunk/test/keys/testkey 2009-09-11 23:06:37 UTC (rev 90) @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEA7+HtwaFvMwwoquMEHrhMBHp0gsy4bXkmWrTc6bn37BTLnIr/ +fA6CkdzP+L/bvLbUzKh8mCRIiJNiuytz3AJEdBF4x2SrRO873iN6/32SN0dh8ER3 +CL2McR+SFgJO5LH59d7vi20VWpcquz3eog/siMb5N2r02D3j7n7fZqOLyQ+bjM29 +XaeuylPJlok7o0KXzp4lFqmLoN7EVAty1PsewYwb/k9RGAirwUW++nB/86qPs3kN +nwibNiGIYAW44U56MMXZuLTKZJOVaOcBDDbSKEK3QkqOPZFIAxMI49k0hDoMX5n3 +mi5n3QSS3p9BiK8elKke4KQZawsg088Q7obQGwIBIwKCAQAUj7VLHHc+48j4tF9w +WPCSq2kShpN3GQNJmnlV3L16orJd4AdFJdCtarK9jMmxFvxMDnEUWuGlTnYtTN4L +iypwWUTd5A6uIyJjf2JQadlVMgEUmCd1xxqqnE5ZqG0pi5kVExvYwDUHw9B2cwR0 +SoH9GF6BF8vYBU4NINFR8MLWuAdqix492wgh16aur/VvDIei4z8t8Ny/DHd8Hpwp +Kn3y5vfnNt5XMEHE53ZNUzYTsYHJtJzZ/y4aITfEu2+GvsNxpRCSnvDNehpppEiu +QYlgEi4fsR52ltyKfli9qKoxGFxN30RqrVDxATK9TDdRoOII9Tfra6txlfJtIj8N +cjzzAoGBAPwhFmTEGx09wh2NHQC9PFRE6EfZqc7xD8ohO3Tz1+N2dS4h6wHHUM43 +yD6hUatbEMrMcg+1zXt9r/V8lDS18QSjtKB2vmwkiHMjRQ4aQRmQCjC9kILnNzTY +FUEkUtAkuXEThKyWclmRpMcalZQ8oUjlsdYc/qElhgzprFa5lewdAoGBAPOQtaJy +kTKsaGi+EguBHr/IBVF6WwKlkt2KwDVWM33j5aGoOYKs2jOQRnF9qV2y2DALIS1P +RLCHmLLsPBmyATgJ4M0+DWTiz+4fz/hWTnXiDxcS/P1EJLDbO7xwK/giubu/kyzR +1gik/HvOOyfpDjNGdRGzvVLDBQebky/tZseXAoGBAPTs8S6v39NDTtrg2lh9UIxg +MhnpYyDM6sRazAs2BOuXpQg+NL/mMT1awoX7y63GLZHN6yU0Q/RONecoj/iwvj8F +baMxhcgjfTyl60+HN+zyUwrGxuWQJwAiXcpAivYVC+owN72ZdmWjbN6sHEbbz+Bq +Gng5bGIHMcNmp2owoERzAoGAFOCEmOU/pUH6Uh7rmpYJ8y5m2xkdvmX2pUZoPxYE +aeBVguKIliwSs/ZscCC2vuNxnbfPpMxHt1wUZxuQH3WoVUKs3mRnjE30kMDl8Lbw +yEaTlEN0xUBa6pZy1aM2+AL5+iZdEnhizYp8Cpyl/BtKXCqcUfl2oLGhWGxrs6ai +aOECgYAdCplM1lgfd9NsCe00kj6j4or+W6dOJuwZuYncLlnFOmtu+zNQyDUIL5tU +DYL6iCst2AzzoKrhlw5W/PdnMSrfKw2KnXhJnibZrucRuCrkjelhgEffQwA2/JQN +efPp5CZ2wZitEwSgKlCSERibHnRQKgXiDdSvXlpCMBY9qyPsdw== +-----END RSA PRIVATE KEY----- Added: trunk/test/keys/testkey.pub =================================================================== --- trunk/test/keys/testkey.pub (rev 0) +++ trunk/test/keys/testkey.pub 2009-09-11 23:06:37 UTC (rev 90) @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7+HtwaFvMwwoquMEHrhMBHp0gsy4bXkmWrTc6bn37BTLnIr/fA6CkdzP+L/bvLbUzKh8mCRIiJNiuytz3AJEdBF4x2SrRO873iN6/32SN0dh8ER3CL2McR+SFgJO5LH59d7vi20VWpcquz3eog/siMb5N2r02D3j7n7fZqOLyQ+bjM29XaeuylPJlok7o0KXzp4lFqmLoN7EVAty1PsewYwb/k9RGAirwUW++nB/86qPs3kNnwibNiGIYAW44U56MMXZuLTKZJOVaOcBDDbSKEK3QkqOPZFIAxMI49k0hDoMX5n3mi5n3QSS3p9BiK8elKke4KQZawsg088Q7obQGw== testkey Added: trunk/test/keys/testkey2 =================================================================== --- trunk/test/keys/testkey2 (rev 0) +++ trunk/test/keys/testkey2 2009-09-11 23:06:37 UTC (rev 90) @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAyKTk+SmvLUnZ1AYCDFw1NAqGwddGcavPJu2mQzx0sfZKVR6V +dUsE8pR4z2uZszEH/yqeA+hoxebjNWUgb7hV6FS+T5Vzwm9sIWeul/d6Erqu2bxU +DNfWamhsY+Xf+ubrnCEeSWku5EsFJNqLiwu7ZlzFG0f6l9kmUH3BGgralUaVWOeR +ci/GSDJ3phIA1XqRBR92ImlMp9nQY2kd9Hr3O3Ey90dwGKfY8a8sUqD1RKya/0+7 +JSbuWHUI4LMNKed8i4JkpH7QP2okXyPJtfq9GzShv5RRl/C5gVsLMvpajPX9tFKe +uk9z7BucQfZ5qDipR3m8RAcUwqOjZpz34RD1qwIBIwKCAQBbuRg3Y4NH5z8B1txd +a/sQedc0CqPcMUjBVrJn4R9nTAS5Mo13csBu5MmDZGOFHbqDRq6is2MYpBAYaL5e +9S6WGBx8JxBY4nqS7ZGkjmOwyl6PbAkqcUwTYu+4o5mXRP3+OwaH9ZHHcsCF4D/K +iQU2G8fR9QTXs7m+ZV+W4GPsdzot5GltI7Xg7ewZq0f6piMYUOwJLtc/ma+cW0F4 +ta9SyN6ORMOH5poJpNYTF6p7YvL6187aCKu8W4NVCeTTct5iJy/i2OQmiFFNIptz +gIbZV/GN0qdrdsoW0Z3VkZlVyKO+rjILV20PRxtSGOks26F1Peuak+iBEkugOKRY +hB4LAoGBAPCKt6C9/o9+HD3XerYKfyXN9+kxALH67mfKJcGuDA2NgyhitczrO2wK +cSmSFCtp86PhALM9NHIV54Psms6ZOZAE6IBhSM/mp5FOC42QvxJtKOkgwrFESeBi +IUpGICMEsaTvVICGzjh7oYD2Eiq1p9e+1xfDokauuLTxJ+wOnBUFAoGBANWJzEoV +cxjuDcVWcJ1Wr/5x9oidUiPRyzhzO70OYXoo1qO9Wzd0I/vyJW52yQTs0IOZlvNq +/4SWhVWqEpoCIqYP5CuC+41EeGE4XIgYkoOym1g4lNIYkv8NTzepq7gcnXKT4RC6 +sfzfDo+ITdDgcGBYKzEeQUF5kukG+SRQ097vAoGAdNWppdigNxFeLKp9blzeq/ZT +16oWR9Gm/zZM2mp64gLn7wtfngvbCJdbiThS8IPrXjoWSG4vapzsywyFtM4UpQmy +wgNd4VLGayXoWrQFCPMxIMbE9wPpXluMgylgEQJHp+H12Ab2czS8KLII0uqEuT9v +ybbLKaVSZoPC7v/IJ3cCgYEAwzwofj9/LLxkXKbNXKcHTwkb6p52/C2GmgL0yhvG +1ha1nQTlrwsZleS0gkC3yf0WabD/CnB0lnr9kCZ3aDx3c0G6q3Bw9i/5CG4LdR3H +yN3P26FyHyxpI7RlrzwKtvWXRC9usDWqC8SfmSwp44tfbgd38mTN8rhLzb07jurQ +VssCgYEAuu5KWdb2ns5CGAclpi/Qm/+si9jSCwq/JGiztrLwG6eJKCS3FPOMzjbt +3skfBA+yepewc3tQgCLYdPfuN0A4R28Yv3uET05ysp4O0AXaMhMx8t63uT+titOT +3if5nWOPnoCM1TCpNnnUQAptJF6R/XSX89FFaszGJfgNKI15+2Q= +-----END RSA PRIVATE KEY----- Added: trunk/test/keys/testkey2.pub =================================================================== --- trunk/test/keys/testkey2.pub (rev 0) +++ trunk/test/keys/testkey2.pub 2009-09-11 23:06:37 UTC (rev 90) @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAyKTk+SmvLUnZ1AYCDFw1NAqGwddGcavPJu2mQzx0sfZKVR6VdUsE8pR4z2uZszEH/yqeA+hoxebjNWUgb7hV6FS+T5Vzwm9sIWeul/d6Erqu2bxUDNfWamhsY+Xf+ubrnCEeSWku5EsFJNqLiwu7ZlzFG0f6l9kmUH3BGgralUaVWOeRci/GSDJ3phIA1XqRBR92ImlMp9nQY2kd9Hr3O3Ey90dwGKfY8a8sUqD1RKya/0+7JSbuWHUI4LMNKed8i4JkpH7QP2okXyPJtfq9GzShv5RRl/C5gVsLMvpajPX9tFKeuk9z7BucQfZ5qDipR3m8RAcUwqOjZpz34RD1qw== testkey2 Modified: trunk/test/link.rb =================================================================== --- trunk/test/link.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/link.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -20,7 +20,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -332,7 +332,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Added: trunk/test/local_requests.rb =================================================================== --- trunk/test/local_requests.rb (rev 0) +++ trunk/test/local_requests.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -0,0 +1,142 @@ +#!/usr/bin/ruby -w + +# +# Test etch's handling of local requests +# + +require 'test/unit' +require 'etchtest' +require 'tempfile' +require 'fileutils' + +class EtchLocalRequestsTests < Test::Unit::TestCase + include EtchTests + + def setup + # Generate a file to use as our etch target/destination + @targetfile = Tempfile.new('etchtest').path + #puts "Using #{@targetfile} as target file" + + # Generate a directory for our test repository + @repodir = initialize_repository + @port, @pid = start_server(@repodir) + + # Create a directory to use as a working directory for the client + @testbase = tempdir + #puts "Using #{@testbase} as client working directory" + end + + def test_local_requests_script + # + # Run a test with a local request and a script + # + testname = 'local request with script' + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file/> + <source> + <script>source.script</script> + </source> + </file> + </config> + EOF + end + + # Create the local request file + requestdir = File.join(@testbase, 'requests', @targetfile) + requestfile = File.join(requestdir, 'testrequest') + FileUtils.mkdir_p(requestdir) + File.open(requestfile, 'w') do |file| + file.puts <<-EOF + <request> + <foo/> + </request> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source.script", 'w') do |file| + file.puts <<-EOF + require 'rexml/document' + doc = REXML::Document.new(@local_requests) + if doc.root.elements['/requests/request/foo'] + @contents << '#{sourcecontents}' + end + EOF + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase) + + # Verify that the file was created properly + assert_equal(sourcecontents, get_file_contents(@targetfile), testname) + end + + def test_local_requests_template + # + # Run a test with a local request and a template + # + testname = 'local request with template' + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file/> + <source> + <template>source.template</template> + </source> + </file> + </config> + EOF + end + + # Create the local request file + requestdir = File.join(@testbase, 'requests', @targetfile) + requestfile = File.join(requestdir, 'testrequest') + FileUtils.mkdir_p(requestdir) + File.open(requestfile, 'w') do |file| + file.puts <<-EOF + <request> + <foo/> + </request> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source.template", 'w') do |file| + file.puts <<-EOF + <% sourcecontents = '#{sourcecontents}' %> + <% require 'rexml/document' %> + <% doc = REXML::Document.new(@local_requests) %> + <% if doc.root.elements['/requests/request/foo'] %> + <%= sourcecontents %> + <% end %> + EOF + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase) + + # Verify that the file was created properly + # Our whitespace in the heredoc above gets added to the generated file, so + # pass both strings through strip so we just compare the meat at the + # center. + assert_equal(sourcecontents.strip, get_file_contents(@targetfile).strip, testname) + end + + def teardown + stop_server(@pid) + remove_repository(@repodir) + FileUtils.rm_rf(@testbase) + FileUtils.rm_rf(@targetfile) + end +end + Property changes on: trunk/test/local_requests.rb ___________________________________________________________________ Added: svn:executable + * Modified: trunk/test/nodegroups.rb =================================================================== --- trunk/test/nodegroups.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/nodegroups.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -20,7 +20,7 @@ # Generate a directory for our test repository # Specify that the node should be put into 'testgroup' in nodes.xml @repodir = initialize_repository(['testgroup']) - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -187,7 +187,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Modified: trunk/test/options.rb =================================================================== --- trunk/test/options.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/options.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -19,7 +19,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -108,7 +108,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Added: trunk/test/outputcapture.rb =================================================================== --- trunk/test/outputcapture.rb (rev 0) +++ trunk/test/outputcapture.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -0,0 +1,140 @@ +#!/usr/bin/ruby -w + +# +# Test output capturing +# + +require 'test/unit' +require 'etchtest' +require 'tempfile' +require 'fileutils' +require 'timeout' +$: << '../client/trunk' +require 'etch' + +class EtchOutputCaptureTests < Test::Unit::TestCase + include EtchTests + + def setup + # Generate a file to use as our etch target/destination + @targetfile = Tempfile.new('etchtest').path + #puts "Using #{@targetfile} as target file" + + # Generate a directory for our test repository + @repodir = initialize_repository + @port, @pid = start_server(@repodir) + + # Create a directory to use as a working directory for the client + @testbase = tempdir + #puts "Using #{@testbase} as client working directory" + end + + def test_output_capture + # + # Run a test where a post command outputs something, make sure that output + # is reported to the server. + # + testname = 'output capture' + + postoutput = "This is output from\nthe post\ncommand" + postcmd = Tempfile.new('etchoutputtest') + postcmd.puts '#!/bin/sh' + # echo may or may not add a trailing \n depending on which echo we end + # up, so use printf, which doesn't add things. + postcmd.puts "printf \"#{postoutput}\"" + postcmd.close + File.chmod(0755, postcmd.path) + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <source> + <plain>source</plain> + </source> + </file> + <post> + <exec>#{postcmd.path}</exec> + </post> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source", 'w') do |file| + file.write(sourcecontents) + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase) + + # Fetch the latest result for this client from the server and verify that + # it contains the output from the post command. + hostname = Facter['fqdn'].value + latest_result_message = '' + Net::HTTP.start('localhost', @port) do |http| + response = http.get("/results.xml?clients.name=#{hostname}&sort=created_at_reverse") + if !response.kind_of?(Net::HTTPSuccess) + response.error! + end + response_xml = REXML::Document.new(response.body) + latest_result_message = nil + if response_xml.elements['/results/result/message'] + latest_result_message = response_xml.elements['/results/result/message'].text + end + end + assert_match(postoutput, latest_result_message, testname) + end + + def test_output_capture_timeout + # + # Run a test where a post command does not properly daemonize, ensure that + # etch eventually times out. + # + testname = 'output capture timeout' + + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <source> + <plain>source</plain> + </source> + </file> + <post> + <exec>ruby -e 'sleep #{Etch::Client::OUTPUT_CAPTURE_TIMEOUT + 30}' &</exec> + </post> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + File.open("#{@repodir}/source/#{@targetfile}/source", 'w') do |file| + file.write(sourcecontents) + end + + begin + Timeout.timeout(Etch::Client::OUTPUT_CAPTURE_TIMEOUT + 15) do + # Run etch + #puts "Running '#{testname}' test" + # + # NOTE: This test is not normally run because the timeout is so long. + # Uncomment this run_etch line to run this test. + # + #run_etch(@port, @testbase) + end + rescue Timeout::Error + flunk('output capturing did not time out as expected') + end + end + + def teardown + stop_server(@pid) + remove_repository(@repodir) + FileUtils.rm_rf(@testbase) + FileUtils.rm_rf(@targetfile) + end +end Property changes on: trunk/test/outputcapture.rb ___________________________________________________________________ Added: svn:executable + * Modified: trunk/test/scripts.rb =================================================================== --- trunk/test/scripts.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/scripts.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -20,7 +20,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -409,7 +409,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) Modified: trunk/test/transitions.rb =================================================================== --- trunk/test/transitions.rb 2009-09-11 22:57:27 UTC (rev 89) +++ trunk/test/transitions.rb 2009-09-11 23:06:37 UTC (rev 90) @@ -20,7 +20,7 @@ # Generate a directory for our test repository @repodir = initialize_repository - @port = start_server(@repodir) + @port, @pid = start_server(@repodir) # Create a directory to use as a working directory for the client @testbase = tempdir @@ -255,7 +255,7 @@ end def teardown - stop_server + stop_server(@pid) remove_repository(@repodir) FileUtils.rm_rf(@testbase) FileUtils.rm_rf(@targetfile) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-09-11 22:57:36
|
Revision: 89 http://etch.svn.sourceforge.net/etch/?rev=89&view=rev Author: jheiss Date: 2009-09-11 22:57:27 +0000 (Fri, 11 Sep 2009) Log Message: ----------- Added authentication support. Add support for local requests. Add RESTful controllers for etch configs, facts and originals. Populate remaining RESTful methods for results and clients controllers. Modify repo_update to update repo directories atomically. We don't want clients getting a partial or partially updated copy of the repository versiontype bug fixes Remove the line that enabled protect_from_forgery in application.rb, thus allowing the removal of the lines from various controllers that turned around and disabled that feature. It is incompatible with the way the etch server works, as POST requests to the etch server are not form data. Remove the line that turned off sessions for robots in application.rb, as etch does not store sessions in the database so there's no real penalty for generating a session for robots. That line was copied over initially from nVentory, which does (or at least used to) store sessions in the database. Copy the convert_includes method from nVentory. Clean up the handling of find and paginate calls in the results and client controllers, reducing code duplication. Add support for XML formatting of results in same. Rename method in files controller from index to create. See comment there for reasoning. Modified Paths: -------------- trunk/server/app/controllers/application.rb trunk/server/app/controllers/clients_controller.rb trunk/server/app/controllers/dashboard_controller.rb trunk/server/app/controllers/files_controller.rb trunk/server/app/controllers/results_controller.rb trunk/server/config/environment.rb trunk/server/config/repo_update trunk/server/config/routes.rb trunk/server/lib/etchserver.rb trunk/server/lib/versiontype.rb Added Paths: ----------- trunk/server/app/controllers/etch_configs_controller.rb trunk/server/app/controllers/facts_controller.rb trunk/server/app/controllers/originals_controller.rb trunk/server/app/helpers/etch_configs_helper.rb trunk/server/app/helpers/facts_helper.rb trunk/server/app/helpers/originals_helper.rb trunk/server/app/views/etch_configs/ trunk/server/app/views/facts/ trunk/server/app/views/originals/ trunk/server/db/backups/ trunk/server/lib/intmax.rb trunk/server/test/functional/etch_configs_controller_test.rb trunk/server/test/functional/facts_controller_test.rb trunk/server/test/functional/originals_controller_test.rb Modified: trunk/server/app/controllers/application.rb =================================================================== --- trunk/server/app/controllers/application.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/app/controllers/application.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -1,3 +1,5 @@ +require 'etchserver' + # Filters added to this controller apply to all controllers in the application. # Likewise, all the methods added will be available for all controllers. @@ -4,15 +6,6 @@ class ApplicationController < ActionController::Base helper :all # include all helpers, all the time - # See ActionController::RequestForgeryProtection for details - # Uncomment the :secret if you're not using the cookie session store - protect_from_forgery # :secret => '6f341ac14ba3f458f8420d3a2a879084' - - # GET requests with no user agent are probably monitoring agents of some - # sort (including load balancer health checks) and creating sessions for - # them just fills up the session table with junk - session :off, :if => Proc.new { |request| request.env['HTTP_USER_AGENT'].blank? && request.get? } - # See ActionController::Base for details # Uncomment this to filter the contents of submitted sensitive data parameters # from your application log (in this case, all fields with names like "password"). @@ -25,4 +18,51 @@ # Pick a unique cookie name to distinguish our session data from others' session :session_key => '_etch_session_id' + # Verify that any changes are signed if the administrator has + # enabled authentication + before_filter :authenticate, :only => [:create, :update, :destroy] + + # This authentication system is targeted at etch clients. There should be + # an alternate authentication mechanism targeted at humans so that humans + # can interact with this service when authentication is enabled. + def authenticate + if Etch::Server.auth_enabled? + if request.headers['Authorization'] && + request.headers['Authorization'] =~ /^EtchSignature / + signature = request.headers['Authorization'].sub(/^EtchSignature /, '') + verified = false + begin + verified = Etch::Server.verify_message(request.raw_post, + signature, + params) + rescue Exception => e + logger.error e.message + logger.info e.backtrace.join("\n") if params[:debug] + response = e.message + response << e.backtrace.join("\n") if params[:debug] + render :text => response, :status => :unauthorized + end + else + logger.info "Authentication required, no authentication data found" + render :text => "Authentication required, no authentication data found", :status => :unauthorized + end + end + end + + # find and to_xml take their :include options in different formats + # find wants: + # :include => { :rack => { :datacenter_rack_assignment => :datacenter } } + # or this (which is what we use because it is easier to generate recursively) + # :include => { :rack => { :datacenter_rack_assignment => { :datacenter => {} } } } + # to_xml wants: + # :include => { :rack => { :include => { :datacenter_rack_assignment => { :include => { :datacenter => {} } } } } } + # This method takes the find format and returns the to_xml format + def convert_includes(includes) + includes.each do |key, value| + unless (value.nil? || value.blank?) + includes[key] = { :include => convert_includes(value) } + end + end + includes + end end Modified: trunk/server/app/controllers/clients_controller.rb =================================================================== --- trunk/server/app/controllers/clients_controller.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/app/controllers/clients_controller.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -1,3 +1,5 @@ +require 'intmax' + class ClientsController < ApplicationController # GET /clients def index @@ -50,19 +52,25 @@ conditions_values << value end end + conditions_string = conditions_query.join(' AND ') - if conditions_query.empty? - @clients = Client.paginate(:all, - :include => includes, - :order => sort, - :page => params[:page]) - else - conditions_string = conditions_query.join(' AND ') - @clients = Client.paginate(:all, - :include => includes, - :conditions => [ conditions_string, *conditions_values ], - :order => sort, - :page => params[:page]) + per_page = Client.per_page # will_paginate's default value + # Client's requesting XML get all entries + respond_to { |format| format.html {}; format.xml { per_page = Integer::MAX } } + + @clients = Client.paginate(:all, + :include => includes, + :conditions => [ conditions_string, *conditions_values ], + :order => sort, + :page => params[:page], + :per_page => per_page) + + respond_to do |format| + format.html # index.html.erb + format.xml do + render :xml => @clients.to_xml(:include => convert_includes(includes), + :dasherize => false) + end end end @@ -74,5 +82,69 @@ end @client = Client.find(params[:id]) + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @client.to_xml(:include => convert_includes(includes), + :dasherize => false) } + end end + + # GET /clients/new + def new + @client = Client.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render :xml => @client } + end + end + + # GET /clients/1/edit + def edit + @client = Client.find(params[:id]) + end + + # POST /clients + def create + @client = Client.new(params[:client]) + + respond_to do |format| + if @client.save + flash[:notice] = 'Client was successfully created.' + format.html { redirect_to(@client) } + format.xml { render :xml => @client, :status => :created, :location => @client } + else + format.html { render :action => "new" } + format.xml { render :xml => @client.errors, :status => :unprocessable_entity } + end + end + end + + # PUT /clients/1 + def update + @client = Client.find(params[:id]) + + respond_to do |format| + if @client.update_attributes(params[:client]) + flash[:notice] = 'Client was successfully updated.' + format.html { redirect_to(@client) } + format.xml { head :ok } + else + format.html { render :action => "edit" } + format.xml { render :xml => @client.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /clients/1 + def destroy + @client = Client.find(params[:id]) + @client.destroy + + respond_to do |format| + format.html { redirect_to(clients_url) } + format.xml { head :ok } + end + end end + Modified: trunk/server/app/controllers/dashboard_controller.rb =================================================================== --- trunk/server/app/controllers/dashboard_controller.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/app/controllers/dashboard_controller.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -39,30 +39,32 @@ months = [] # Find the oldest client oldest = Client.find(:first, :order => 'created_at') - # Get the month and year of that date - month = oldest.created_at.mon - year = oldest.created_at.year - # Iterate months to present - (year..Time.now.year).each do |y| - start_month = 1 - end_month = 12 - if y == year - start_month = month - end - if y == Time.now.year - end_month = Time.now.month - end - (start_month..end_month).each do |m| - end_time = nil - if m == 12 - end_time = Time.local(y+1, 1) - else - end_time = Time.local(y, m+1) + if oldest + # Get the month and year of that date + month = oldest.created_at.mon + year = oldest.created_at.year + # Iterate months to present + (year..Time.now.year).each do |y| + start_month = 1 + end_month = 12 + if y == year + start_month = month end - # This should get us the last second of the desired month - end_time - 1 - clients << Client.count(:conditions => ["created_at <= ?", end_time]) - months << end_time.strftime('%b %Y') + if y == Time.now.year + end_month = Time.now.month + end + (start_month..end_month).each do |m| + end_time = nil + if m == 12 + end_time = Time.local(y+1, 1) + else + end_time = Time.local(y, m+1) + end + # This should get us the last second of the desired month + end_time - 1 + clients << Client.count(:conditions => ["created_at <= ?", end_time]) + months << end_time.strftime('%b %Y') + end end end Added: trunk/server/app/controllers/etch_configs_controller.rb =================================================================== --- trunk/server/app/controllers/etch_configs_controller.rb (rev 0) +++ trunk/server/app/controllers/etch_configs_controller.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,79 @@ +class EtchConfigsController < ApplicationController + # GET /etch_configs + def index + @etch_configs = EtchConfig.find :all + + respond_to do |format| + format.html # index.html.erb + format.xml { render :xml => @etch_configs } + end + end + + # GET /etch_configs/1 + def show + @etch_config = EtchConfig.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @etch_config } + end + end + + # GET /etch_configs/new + def new + @etch_config = EtchConfig.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render :xml => @etch_config } + end + end + + # GET /etch_configs/1/edit + def edit + @etch_config = EtchConfig.find(params[:id]) + end + + # POST /etch_configs + def create + @etch_config = EtchConfig.new(params[:etch_config]) + + respond_to do |format| + if @etch_config.save + flash[:notice] = 'EtchConfig was successfully created.' + format.html { redirect_to(@etch_config) } + format.xml { render :xml => @etch_config, :status => :created, :location => @etch_config } + else + format.html { render :action => "new" } + format.xml { render :xml => @etch_config.errors, :status => :unprocessable_entity } + end + end + end + + # PUT /etch_configs/1 + def update + @etch_config = EtchConfig.find(params[:id]) + + respond_to do |format| + if @etch_config.update_attributes(params[:etch_config]) + flash[:notice] = 'EtchConfig was successfully updated.' + format.html { redirect_to(@etch_config) } + format.xml { head :ok } + else + format.html { render :action => "edit" } + format.xml { render :xml => @etch_config.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /etch_configs/1 + def destroy + @etch_config = EtchConfig.find(params[:id]) + @etch_config.destroy + + respond_to do |format| + format.html { redirect_to(admin_etch_configs_url) } + format.xml { head :ok } + end + end +end Added: trunk/server/app/controllers/facts_controller.rb =================================================================== --- trunk/server/app/controllers/facts_controller.rb (rev 0) +++ trunk/server/app/controllers/facts_controller.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,79 @@ +class FactsController < ApplicationController + # GET /facts + def index + @facts = Fact.find :all + + respond_to do |format| + format.html # index.html.erb + format.xml { render :xml => @facts } + end + end + + # GET /facts/1 + def show + @fact = Fact.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @fact } + end + end + + # GET /facts/new + def new + @fact = Fact.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render :xml => @fact } + end + end + + # GET /facts/1/edit + def edit + @fact = Fact.find(params[:id]) + end + + # POST /facts + def create + @fact = Fact.new(params[:fact]) + + respond_to do |format| + if @fact.save + flash[:notice] = 'Fact was successfully created.' + format.html { redirect_to(@fact) } + format.xml { render :xml => @fact, :status => :created, :location => @fact } + else + format.html { render :action => "new" } + format.xml { render :xml => @fact.errors, :status => :unprocessable_entity } + end + end + end + + # PUT /facts/1 + def update + @fact = Fact.find(params[:id]) + + respond_to do |format| + if @fact.update_attributes(params[:fact]) + flash[:notice] = 'Fact was successfully updated.' + format.html { redirect_to(@fact) } + format.xml { head :ok } + else + format.html { render :action => "edit" } + format.xml { render :xml => @fact.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /facts/1 + def destroy + @fact = Fact.find(params[:id]) + @fact.destroy + + respond_to do |format| + format.html { redirect_to(admin_facts_url) } + format.xml { head :ok } + end + end +end Modified: trunk/server/app/controllers/files_controller.rb =================================================================== --- trunk/server/app/controllers/files_controller.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/app/controllers/files_controller.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -1,14 +1,11 @@ -require 'etchserver' - class FilesController < ApplicationController - # Turn off this Rails security mechanism, as it is doesn't work in the - # way this application works. It expects POST requests to include a - # token that it auto-inserts into forms, but our POST requests aren't - # form data, they're unsolicited so Rails never gets a chance to insert - # the token. - skip_before_filter :verify_authenticity_token - - def index + # POST /files + # The method name doesn't exactly make sense in this case (since database + # entries are only indirectly created here, there is no File model), but it + # is consistent with the method name associated with POST in RESTful + # controllers, and thus also falls into the actions that are checked for + # authentication by our before_filter. + def create response = nil begin etchserver = Etch::Server.new(params[:facts], params[:tag], params[:debug]) @@ -20,8 +17,6 @@ response = e.message response << e.backtrace.join("\n") if params[:debug] render :text => response, :status => :internal_server_error - #raise end end - end Added: trunk/server/app/controllers/originals_controller.rb =================================================================== --- trunk/server/app/controllers/originals_controller.rb (rev 0) +++ trunk/server/app/controllers/originals_controller.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,79 @@ +class OriginalsController < ApplicationController + # GET /originals + def index + @originals = Original.find :all + + respond_to do |format| + format.html # index.html.erb + format.xml { render :xml => @originals } + end + end + + # GET /originals/1 + def show + @original = Original.find(params[:id]) + + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @original } + end + end + + # GET /originals/new + def new + @original = Original.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render :xml => @original } + end + end + + # GET /originals/1/edit + def edit + @original = Original.find(params[:id]) + end + + # POST /originals + def create + @original = Original.new(params[:original]) + + respond_to do |format| + if @original.save + flash[:notice] = 'Original was successfully created.' + format.html { redirect_to(@original) } + format.xml { render :xml => @original, :status => :created, :location => @original } + else + format.html { render :action => "new" } + format.xml { render :xml => @original.errors, :status => :unprocessable_entity } + end + end + end + + # PUT /originals/1 + def update + @original = Original.find(params[:id]) + + respond_to do |format| + if @original.update_attributes(params[:original]) + flash[:notice] = 'Original was successfully updated.' + format.html { redirect_to(@original) } + format.xml { head :ok } + else + format.html { render :action => "edit" } + format.xml { render :xml => @original.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /originals/1 + def destroy + @original = Original.find(params[:id]) + @original.destroy + + respond_to do |format| + format.html { redirect_to(admin_originals_url) } + format.xml { head :ok } + end + end +end Modified: trunk/server/app/controllers/results_controller.rb =================================================================== --- trunk/server/app/controllers/results_controller.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/app/controllers/results_controller.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -1,11 +1,6 @@ +require 'intmax' + class ResultsController < ApplicationController - # Turn off this Rails security mechanism, as it is doesn't work in the - # way this application works. It expects POST requests to include a - # token that it auto-inserts into forms, but our POST requests aren't - # form data, they're unsolicited so Rails never gets a chance to insert - # the token. - skip_before_filter :verify_authenticity_token - # GET /results def index includes = {} @@ -50,6 +45,7 @@ next if key == 'page' @query_params << "#{key}=#{value}" # Used by view next if key == 'sort' + next if key == 'combined' if key == 'starttime' conditions_query << "results.created_at > ?" @@ -62,40 +58,57 @@ conditions_values << value end end + conditions_string = conditions_query.join(' AND ') - if conditions_query.empty? - if @combined # Don't paginate combined results - @results = Result.find(:all, - :include => includes, - :order => sort) - else - @results = Result.paginate(:all, - :include => includes, - :order => sort, - :page => params[:page]) + per_page = Result.per_page # will_paginate's default value + # Client's requesting XML get all entries + respond_to { |format| format.html {}; format.xml { per_page = Integer::MAX } } + # As do clients who specifically request everything + if @combined + per_page = Integer::MAX + end + + @results = Result.paginate(:all, + :include => includes, + :conditions => [ conditions_string, *conditions_values ], + :order => sort, + :page => params[:page], + :per_page => per_page) + + respond_to do |format| + format.html # index.html.erb + format.xml do + render :xml => @results.to_xml(:include => convert_includes(includes), + :dasherize => false) end - else - conditions_string = conditions_query.join(' AND ') - if @combined # Don't paginate combined results - @results = Result.find(:all, - :include => includes, - :conditions => [ conditions_string, *conditions_values ], - :order => sort) - else - @results = Result.paginate(:all, - :include => includes, - :conditions => [ conditions_string, *conditions_values ], - :order => sort, - :page => params[:page]) - end end end # GET /results/1 def show @result = Result.find(params[:id]) + respond_to do |format| + format.html # show.html.erb + format.xml { render :xml => @result.to_xml(:include => convert_includes(includes), + :dasherize => false) } + end end + # GET /results/new + def new + @result = Result.new + + respond_to do |format| + format.html # new.html.erb + format.xml { render :xml => @result } + end + end + + # GET /results/1/edit + def edit + @result = Result.find(params[:id]) + end + # POST /results def create if !params.has_key?(:fqdn) @@ -127,6 +140,13 @@ # remains unchanged. client.updated_at = Time.now client.save + + # NOTE: This skips over the individual file results recording below + # Re-enabled by jheiss, 7-July-2009 We have more disk space on the + # servers now, we'll see if they can keep up + #render :text => 'Client status recorded, individual file results recording disabled for now, jheiss, 9-May-2009' + #return + success_count = 0 params[:results].each do |result| # The Rails parameter parsing strips out parameters with empty values. @@ -147,5 +167,32 @@ render :text => "Successfully recorded #{success_count} of #{params[:results].size} results" end - + + # PUT /results/1 + def update + @result = Result.find(params[:id]) + + respond_to do |format| + if @result.update_attributes(params[:result]) + flash[:notice] = 'Result was successfully updated.' + format.html { redirect_to(@result) } + format.xml { head :ok } + else + format.html { render :action => "edit" } + format.xml { render :xml => @result.errors, :status => :unprocessable_entity } + end + end + end + + # DELETE /results/1 + def destroy + @result = Result.find(params[:id]) + @result.destroy + + respond_to do |format| + format.html { redirect_to(admin_results_url) } + format.xml { head :ok } + end + end end + Added: trunk/server/app/helpers/etch_configs_helper.rb =================================================================== --- trunk/server/app/helpers/etch_configs_helper.rb (rev 0) +++ trunk/server/app/helpers/etch_configs_helper.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,2 @@ +module EtchConfigsHelper +end Added: trunk/server/app/helpers/facts_helper.rb =================================================================== --- trunk/server/app/helpers/facts_helper.rb (rev 0) +++ trunk/server/app/helpers/facts_helper.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,2 @@ +module FactsHelper +end Added: trunk/server/app/helpers/originals_helper.rb =================================================================== --- trunk/server/app/helpers/originals_helper.rb (rev 0) +++ trunk/server/app/helpers/originals_helper.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,2 @@ +module OriginalsHelper +end Modified: trunk/server/config/environment.rb =================================================================== --- trunk/server/config/environment.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/config/environment.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -25,12 +25,7 @@ # config.gem "bj" # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" # config.gem "aws-s3", :lib => "aws/s3" - # This is the syntax in the will_paginate docs, but seems to trigger - # an install of exactly 2.3.6. At this point I don't know of any - # specific version requirement we have, so I'm just letting it install - # the latest. - #config.gem 'mislav-will_paginate', :version => '~> 2.3.6', :lib => 'will_paginate', :source => 'http://gems.github.com' - config.gem 'mislav-will_paginate', :lib => 'will_paginate', :source => 'http://gems.github.com' + config.gem 'mislav-will_paginate', :version => '~> 2.3.8', :lib => 'will_paginate', :source => 'http://gems.github.com' # Only load the plugins named here, in the order given. By default, all plugins # in vendor/plugins are loaded in alphabetical order. Modified: trunk/server/config/repo_update =================================================================== --- trunk/server/config/repo_update 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/config/repo_update 2009-09-11 22:57:27 UTC (rev 89) @@ -1,6 +1,11 @@ #!/usr/bin/ruby -w +require 'fileutils' +require 'tempfile' + Dir.mkdir('/etc/etchserver') if !File.directory?('/etc/etchserver') +Dir.mkdir('/etc/etchserver/orig') if !File.directory?('/etc/etchserver/orig') +FileUtils.chown('nobody', nil, '/etc/etchserver/orig') Dir.chdir('/etc/etchserver') system('svn update --quiet') @@ -8,10 +13,16 @@ currenttag = Time.now.strftime('etchautotag-%Y%m%d-%H00') tagdir = File.join('tags', currenttag) if !File.directory?(tagdir) - #system('svn up trunk') - #system("svn copy trunk #{tagdir}") - #system("svn ci -m 'Creating current autotag' --non-interactive #{tagdir}") - system("cp -a trunk #{tagdir}") + # Use Tempfile to make a unique filename + tmpdirfile = Tempfile.new('newtag', 'tags') + # Turn it into a directory + File.delete(tmpdirfile.path) + Dir.mkdir(tmpdirfile.path) + # Use it to create the new tag atomically + system("cp -a trunk #{tmpdirfile.path}") + File.rename(File.join(tmpdirfile.path, 'trunk'), tagdir) + # Cleanup + Dir.delete(tmpdirfile.path) end def convert_tagtime_to_unixtime(tagdate, tagtime) @@ -28,9 +39,7 @@ next unless entry =~ /^etchautotag-(\d{8})-(\d{4})$/ tagunixtime = convert_tagtime_to_unixtime($1, $2) if tagunixtime < timelimit - #system("svn delete #{entry}") - #system("svn ci -m 'Removing old autotag' --non-interactive #{entry}") - system("rm -rf #{entry}") + FileUtils.rm_rf(entry) end end Modified: trunk/server/config/routes.rb =================================================================== --- trunk/server/config/routes.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/config/routes.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -4,8 +4,11 @@ # Sample of regular route: # map.connect 'products/:id', :controller => 'catalog', :action => 'view' # Keep in mind you can assign values other than :controller and :action - map.connect 'files', :controller => 'files', :action => 'index' + map.connect 'files', :controller => 'files', :action => 'create' map.resources :clients + map.resources :etch_configs + map.resources :facts + map.resources :originals map.resources :results # Sample of named route: Modified: trunk/server/lib/etchserver.rb =================================================================== --- trunk/server/lib/etchserver.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/lib/etchserver.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -2,7 +2,11 @@ require 'pathname' # absolute? require 'digest/sha1' # hexdigest require 'base64' # decode64, encode64 +require 'openssl' +require 'time' # Time.parse +require 'cgi' require 'fileutils' # mkdir_p +require 'rubygems' # libxml is a gem require 'libxml' require 'erb' require 'versiontype' # Version @@ -11,12 +15,193 @@ end class Etch::Server + DEFAULT_CONFIGBASE = '/etc/etchserver' + + @@configbase = nil + def self.configbase + if !@@configbase + if ENV['etchserverbase'] && !ENV['etchserverbase'].empty? + @@configbase = ENV['etchserverbase'] + else + @@configbase = DEFAULT_CONFIGBASE + end + end + @@configbase + end + + @@auth_enabled = nil + @@auth_deny_new_clients = nil + def self.read_config_file + config_file = File.join(configbase, 'etchserver.conf') + if File.exist?(config_file) + auth_enabled = false + auth_deny_new_clients = false + IO.foreach(config_file) do |line| + # Skip blank lines and comments + next if line =~ /^\s*$/ + next if line =~ /^\s*#/ + line.chomp + if line =~ /^\s*auth_enabled\s*=\s*(.*)/ + if $1 == 'true' + auth_enabled = true + end + end + if line =~ /^\s*auth_deny_new_clients\s*=\s*(.*)/ + if $1 == 'true' + auth_deny_new_clients = true + end + end + end + @@auth_enabled = auth_enabled + @@auth_deny_new_clients = auth_deny_new_clients + end + end + def self.auth_enabled? + if @@auth_enabled.nil? + read_config_file + end + @@auth_enabled + end + # How to handle new clients (allow or deny) + def self.auth_deny_new_clients? + if @@auth_deny_new_clients.nil? + read_config_file + end + @@auth_deny_new_clients + end + + # This method verifies signatures from etch clients. + # message - the message to be verify + # signature - the signature of the message + # key - public key (in openssh format) + # Currently, this only supports RSA keys. + # Returns true if the signature is valid, false otherwise + def self.verify_signature(message, signature, key) + # + # Parse through the public key to get e and m + # + + str = Base64.decode64(key) + # check header + hdr = str.slice!(0..3) + unless hdr[0] == 0 && hdr[1] == 0 && hdr[2] == 0 && hdr[3] == 7 + raise "Bad key format #{hdr}" + end + + # check key type + keytype = str.slice!(0..6) + unless keytype == "ssh-rsa" + raise "Unsupported key type #{keytype}. Only support ssh-rsa right now" + end + + # get exponent + elength = str.slice!(0..3) + num = 0 + elength.each_byte { |x| + num = (num << 8) + x.to_i + } + elength_i = num + + num = 0 + e = str.slice!(0..elength_i-1) + e.each_byte { |x| + num = (num << 8) + x.to_i + } + e_i = num + + # get modulus + num = 0 + nlength = str.slice!(0..3) + nlength.each_byte { |x| + num = (num << 8) + x.to_i + } + nlength_i = num + + num = 0 + n = str.slice!(0..nlength_i-1) + n.each_byte { |x| + num = (num << 8) + x.to_i + } + + # + # Create key based on e and m + # + + key = OpenSSL::PKey::RSA.new + exponent = OpenSSL::BN.new e_i.to_s + modulus = OpenSSL::BN.new num.to_s + key.e = exponent + key.n = modulus + + # + # Check signature + # + + hash_from_sig = key.public_decrypt(Base64.decode64(signature)) + hash_from_msg = Digest::SHA1.hexdigest(message) + if hash_from_sig == hash_from_msg + return true # good signature + else + return false # bad signature + end + end + + def self.verify_message(message, signature, params) + timestamp = params[:timestamp] + # Don't accept if any of the required bits are missing + if message.nil? + raise "message is missing" + end + if signature.nil? + raise "signature is missing" + end + if timestamp.nil? + raise "timestamp param is missing" + end + + # Check timestamp, narrows the window of vulnerability to replay attack + # Window is set to 5 minutes + now = Time.new.to_i + parsed_timestamp = Time.parse(timestamp).to_i + timediff = now - parsed_timestamp + if timediff.abs >= (60 * 5) + raise "timestamp too far off (now:#{now}, timestamp:#{parsed_timestamp})" + end + + # Try to find the public key + public_key = nil + client = Client.find_by_name(params[:fqdn]) + if client + sshrsakey_fact = Fact.find_by_key_and_client_id('sshrsakey', client.id) + if sshrsakey_fact + public_key = sshrsakey_fact.value + end + end + if !public_key + if !auth_deny_new_clients? && + params[:facts] && params[:facts][:sshrsakey] + # If the user has configured the server to transparently accept + # new clients then do so, as long as the client is providing a + # key so that we won't consider them a new client on future + # connections. Otherwise a rogue client could continually + # impersonate any as-yet unregistered server by supplying some + # or all facts except the key fact. + return true + else + raise "Unknown client #{params[:fqdn]}, server configured to reject unknown clients" + end + end + + # Check signature + verify_signature(message, signature, public_key) + end + def initialize(facts, tag=nil, debug=false) @facts = facts @tag = tag @debug = debug + @fqdn = @facts['fqdn'] - @fqdn = @facts['fqdn'] if !@fqdn raise "fqdn fact not supplied" end @@ -34,12 +219,8 @@ fact.destroy end end - - if ENV['etchserverbase'] && !ENV['etchserverbase'].empty? - @configbase = ENV['etchserverbase'] - else - @configbase = '/etc/etchserver' - end + + @configbase = Etch::Server.configbase RAILS_DEFAULT_LOGGER.info "Using #{@configbase} as config base for node #{@fqdn}" if (@debug) if !File.directory?(@configbase) raise "Config base #{@configbase} doesn't exist" @@ -444,7 +625,13 @@ done = true end end - + + # Pull out any local requests + local_requests = nil + if files[file] && files[file]['local_requests'] + local_requests = files[file]['local_requests'] + end + # # Regular file # @@ -472,7 +659,7 @@ # Run the template through ERB to generate the file contents template = template_elements.first.content - external = EtchExternalSource.new(file, original_file, @facts, @groups, @sourcebase, @sitelibbase, @debug) + external = EtchExternalSource.new(file, original_file, @facts, @groups, local_requests, @sourcebase, @sitelibbase, @debug) newcontents = external.process_template(template) elsif config_xml.find_first('/config/file/source/script') script_elements = config_xml.find('/config/file/source/script').to_a @@ -482,7 +669,7 @@ # Run the script to generate the file contents script = script_elements.first.content - external = EtchExternalSource.new(file, original_file, @facts, @groups, @sourcebase, @sitelibbase, @debug) + external = EtchExternalSource.new(file, original_file, @facts, @groups, local_requests, @sourcebase, @sitelibbase, @debug) newcontents = external.run_script(script) elsif config_xml.find_first('/config/file/always_manage_metadata') # always_manage_metadata is a special case where we proceed @@ -669,7 +856,7 @@ end script = script_elements.first.content - external = EtchExternalSource.new(file, original_file, @facts, @groups, @sourcebase, @sitelibbase, @debug) + external = EtchExternalSource.new(file, original_file, @facts, @groups, local_requests, @sourcebase, @sitelibbase, @debug) dest = external.run_script(script) # Remove the script element(s) from the XML, the client won't need @@ -740,7 +927,7 @@ end script = script_elements.first.content - external = EtchExternalSource.new(file, original_file, @facts, @groups, @sourcebase, @sitelibbase, @debug) + external = EtchExternalSource.new(file, original_file, @facts, @groups, local_requests, @sourcebase, @sitelibbase, @debug) create = external.run_script(script) create = false if create.empty? @@ -811,7 +998,7 @@ end script = script_elements.first.content - external = EtchExternalSource.new(file, original_file, @facts, @groups, @sourcebase, @sitelibbase, @debug) + external = EtchExternalSource.new(file, original_file, @facts, @groups, local_requests, @sourcebase, @sitelibbase, @debug) proceed = external.run_script(script) proceed = false if proceed.empty? @@ -996,7 +1183,7 @@ end class EtchExternalSource - def initialize(file, original_file, facts, groups, sourcebase, sitelibbase, debug=false) + def initialize(file, original_file, facts, groups, local_requests, sourcebase, sitelibbase, debug=false) # The external source is going to be processed within the same Ruby # instance as etch. We want to make it clear what variables we are # intentionally exposing to external sources, essentially this @@ -1005,6 +1192,7 @@ @original_file = original_file @facts = facts @groups = groups + @local_requests = local_requests @sourcebase = sourcebase @sitelibbase = sitelibbase @debug = debug Added: trunk/server/lib/intmax.rb =================================================================== --- trunk/server/lib/intmax.rb (rev 0) +++ trunk/server/lib/intmax.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,7 @@ +# http://drawohara.com/post/117643208/ruby-integer-max-and-integer-min +class Integer + N_BYTES = [42].pack('i').size + N_BITS = N_BYTES * 8 + MAX = 2 ** (N_BITS - 2) - 1 + MIN = -MAX - 1 +end Modified: trunk/server/lib/versiontype.rb =================================================================== --- trunk/server/lib/versiontype.rb 2009-09-11 22:42:34 UTC (rev 88) +++ trunk/server/lib/versiontype.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -1,5 +1,8 @@ # This class stores numbers with multiple decimal points, a format # commonly used for version numbers. For example '2.5.1'. + +require 'generator' # SyncEnumerator + class Version include Comparable @@ -29,11 +32,54 @@ (ourfields.length...larger).each { ourfields << '0' } (otherfields.length...larger).each { otherfields << '0' } end - # Convert fields of all digits from string to number to get a numeric - # rather than string comparison. This ensures that 5.9 < 5.10 - ourfields.map! { |field| if field =~ /^\d+$/ then field.to_i else field end } - otherfields.map! { |field| if field =~ /^\d+$/ then field.to_i else field end } + + ourfields, otherfields = convert_and_split(ourfields, otherfields) + # Array conveniently implements <=> ourfields <=> otherfields end + + # Private methods below + private + + # Loops over two arrays in parallel. If the entry at a given + # position in both arrays is numeric it is converted from a string to + # a number, or if either entry is a mixture of numeric and + # non-numeric characters then both are split into an array consisting + # of the numeric and non-numeric components. + def convert_and_split(fields1, fields2) + # Squish both arrays together + bothfields = [] + sync = SyncEnumerator.new(fields1, fields2) + sync.each do |field1, field2| + bothfields << [field1, field2] + end + bothfields.map! do |fields| + # Convert fields of all digits from string to number to get a numeric + # rather than string comparison. This ensures that 5.9 < 5.10 + # Unless either start with a zero, as 1.1 != 1.01, but converting + # 01 to a number turns it into 1. + if fields[0] =~ /^[1-9]\d*$/ && fields[1] =~ /^[1-9]\d*$/ + fields.map! { |f| f.to_i } + else + # If the field is a mixture of numeric and non-numeric + # characters then split it up into an array of those components + # so that we compare "naturally". I.e. 9a < 10a + # This is similar to the method used by most "natural sort" + # algorithms that aim to sort file9 above file10. + if fields[0] =~ /\d\D/ || fields[0] =~ /\D\d/ || + fields[1] =~ /\d\D/ || fields[1] =~ /\D\d/ + fields.map! { |f| f.scan(/\d+|\D+/) } + # Pass back through this method to convert the numeric + # entries to numbers + fields = convert_and_split(fields[0], fields[1]) + end + end + fields + end + # Unsquish back to separate arrays + fields1 = bothfields.collect { |fields| fields[0] } + fields2 = bothfields.collect { |fields| fields[1] } + [fields1, fields2] + end end Added: trunk/server/test/functional/etch_configs_controller_test.rb =================================================================== --- trunk/server/test/functional/etch_configs_controller_test.rb (rev 0) +++ trunk/server/test/functional/etch_configs_controller_test.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,8 @@ +require 'test_helper' + +class EtchConfigsControllerTest < ActionController::TestCase + # Replace this with your real tests. + def test_truth + assert true + end +end Added: trunk/server/test/functional/facts_controller_test.rb =================================================================== --- trunk/server/test/functional/facts_controller_test.rb (rev 0) +++ trunk/server/test/functional/facts_controller_test.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,8 @@ +require 'test_helper' + +class FactsControllerTest < ActionController::TestCase + # Replace this with your real tests. + def test_truth + assert true + end +end Added: trunk/server/test/functional/originals_controller_test.rb =================================================================== --- trunk/server/test/functional/originals_controller_test.rb (rev 0) +++ trunk/server/test/functional/originals_controller_test.rb 2009-09-11 22:57:27 UTC (rev 89) @@ -0,0 +1,8 @@ +require 'test_helper' + +class OriginalsControllerTest < ActionController::TestCase + # Replace this with your real tests. + def test_truth + assert true + end +end This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-09-11 22:42:49
|
Revision: 88 http://etch.svn.sourceforge.net/etch/?rev=88&view=rev Author: jheiss Date: 2009-09-11 22:42:34 +0000 (Fri, 11 Sep 2009) Log Message: ----------- Config file for unicorn (mongrel alternative) Added Paths: ----------- trunk/server/config/unicorn.rb Added: trunk/server/config/unicorn.rb =================================================================== --- trunk/server/config/unicorn.rb (rev 0) +++ trunk/server/config/unicorn.rb 2009-09-11 22:42:34 UTC (rev 88) @@ -0,0 +1,4 @@ +worker_processes(20) +stdout_path('log/unicorn_stdout') +stderr_path('log/unicorn_stderr') + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-09-11 16:59:30
|
Revision: 87 http://etch.svn.sourceforge.net/etch/?rev=87&view=rev Author: jheiss Date: 2009-09-11 16:59:21 +0000 (Fri, 11 Sep 2009) Log Message: ----------- Added authentication support. The client will sign messages using its ssh host key. Add support for local requests. Update the method used to eliminate the OpenSSL "using default DH parameters" warning. http://techbits.blogspot.com/2009/08/ive-been-using-workaround-shown-at.html Add timeout to output capturing so that ill-behaved daemons don't cause etch to hang around forever. Add unit tests for output capturing. Note that the output capturing timeout test is commented out as the timeout is rather long, thus causing the test to take a long time to run. Not quite sure the best way to handle that. Move Etch::Client.initialize arguments to a hash so that the method doesn't take 10 or whatever arguments. Set etch version as a constant in the library (Etch::Client::VERSION). Add a command line option to etch to display the version. Modify the make targets to insert the version into the appropriate place rather than having the version hard-coded in a bunch of files. Add redhatprep, debianprep and solarisprep targets which install the packages necessary to extract the version from the library file. Add code to require rubygems if requiring facter fails. Change /usr/lib/ruby/site_ruby to /usr/local/lib/ruby/site_ruby for the Debian package. That seems to be the correct location on Debian systems. Modified Paths: -------------- trunk/client/Makefile trunk/client/control trunk/client/etch trunk/client/etch-client.spec trunk/client/etch.rb trunk/client/pkginfo Added Paths: ----------- trunk/client/dhparams Modified: trunk/client/Makefile =================================================================== --- trunk/client/Makefile 2009-06-25 00:13:39 UTC (rev 86) +++ trunk/client/Makefile 2009-09-11 16:59:21 UTC (rev 87) @@ -1,13 +1,15 @@ -# When updating the version number you currently need to update -# etch-client.spec, control and pkginfo as well -VER=1.1 +BUILDROOT=/var/tmp/etch-client-buildroot +# Grab the current version from the library +VER=$(shell ruby -e "$$:.unshift('.'); require 'etch'; puts Etch::Client::VERSION") all: -redhat: rpmbuild-redhat rpm -rpmbuild-redhat: - rpm --quiet -q rpm-build || yum -y install rpm-build -BUILDROOT=/var/tmp/etch-client-buildroot +redhat: redhatprep rpm +redhatprep: + # Install everything needed for the command which sets VER above + rpm --quiet -q ruby || sudo yum install ruby + # And the package which contains the rpmbuild command + rpm --quiet -q rpm-build || sudo yum install rpm-build TMPSPEC = etch-client-temp.spec rpm: etch-client.spec # @@ -22,6 +24,7 @@ chmod 444 $(BUILDROOT)/usr/lib/ruby/site_ruby/1.8/etch.rb mkdir -p $(BUILDROOT)/etc/etch cp -p ca.pem $(BUILDROOT)/etc/etch + cp -p dhparams $(BUILDROOT)/etc/etch # Cron job mkdir -p $(BUILDROOT)/etc/cron.d cp etch_cron $(BUILDROOT)/etc/cron.d/etch @@ -30,26 +33,38 @@ # # Now build the package # - rpmbuild -bb --buildroot $(BUILDROOT) etch-client.spec + sed 's/VER/$(VER)/' etch-client.spec > etch-client.spec_withversion + rpmbuild -bb --buildroot $(BUILDROOT) etch-client.spec_withversion rm -rf $(BUILDROOT) -debian: control - rm -rf debtmp - mkdir -p debtmp/DEBIAN - grep -v '^#' control > debtmp/DEBIAN/control - mkdir -p debtmp/usr/sbin - cp -p etch debtmp/usr/sbin - chmod 555 debtmp/usr/sbin/etch - mkdir -p debtmp/usr/lib/ruby/site_ruby/1.8 - cp -p etch.rb debtmp/usr/lib/ruby/site_ruby/1.8 - chmod 444 debtmp/usr/lib/ruby/site_ruby/1.8 - mkdir -p debtmp/etc/etch - cp -p ca.pem debtmp/etc/etch - sudo chown -R 0:0 debtmp - dpkg --build debtmp etch-client-$(VER).deb - rm -rf debtmp +debian: debianprep deb +debianprep: + # Install everything needed for the command which sets VER above + sudo apt-get --no-upgrade --quiet install ruby libopenssl-ruby rubygems facter +deb: control + rm -rf $(BUILDROOT) + mkdir -p $(BUILDROOT)/DEBIAN + grep -v '^#' control | sed 's/VER/$(VER)/' > $(BUILDROOT)/DEBIAN/control + mkdir -p $(BUILDROOT)/usr/sbin + cp -p etch $(BUILDROOT)/usr/sbin + chmod 555 $(BUILDROOT)/usr/sbin/etch + mkdir -p $(BUILDROOT)/usr/local/lib/site_ruby/1.8 + cp -p etch.rb $(BUILDROOT)/usr/local/lib/site_ruby/1.8 + chmod 444 $(BUILDROOT)/usr/local/lib/site_ruby/1.8/etch.rb + mkdir -p $(BUILDROOT)/etc/etch + cp -p ca.pem $(BUILDROOT)/etc/etch + cp -p dhparams $(BUILDROOT)/etc/etch + sudo chown -R 0:0 $(BUILDROOT) + dpkg --build $(BUILDROOT) etch-client-$(VER).deb + rm -rf $(BUILDROOT) -solaris: pkginfo depend +solaris: solarisprep sysvpkg sysvpkg-sparc +solarisprep: + # Install everything needed for the command which sets VER above + pkginfo -q CSWruby || sudo pkg-get -i ruby + pkginfo -q CSWrubygems || sudo pkg-get -i rubygems + pkginfo -q CSWfacter || sudo pkg-get -i facter +sysvpkg: pkginfo depend # # Create package file structure in build root # @@ -65,27 +80,33 @@ chmod 444 $(BUILDROOT)/opt/csw/lib/ruby/site_ruby/1.8/etch.rb mkdir -p $(BUILDROOT)/etc/etch cp -p ca.pem $(BUILDROOT)/etc/etch + cp -p dhparams $(BUILDROOT)/etc/etch # Cron job for registration cat etch_cron_wrapper | sed 's,/usr/bin/perl,/opt/csw/bin/perl,' > $(BUILDROOT)/usr/sbin/etch_cron_wrapper chmod 555 $(BUILDROOT)/usr/sbin/etch_cron_wrapper # # Now build the package # - echo "i pkginfo=./pkginfo" > prototype - echo "i depend=./depend" >> prototype - echo "i postinstall=./postinstall" >> prototype - echo "i postremove=./postremove" >> prototype + rm -rf solbuild + mkdir solbuild + sed 's/%VER%/$(VER)/' pkginfo > solbuild/pkginfo + echo "i pkginfo=./pkginfo" > solbuild/prototype + cp depend solbuild/depend + echo "i depend=./depend" >> solbuild/prototype + cp postinstall solbuild/postinstall + echo "i postinstall=./postinstall" >> solbuild/prototype + cp postremove solbuild/postremove + echo "i postremove=./postremove" >> solbuild/prototype # The tail +2 removes the first line, which is the base directory # and doesn't need to be included in the package. # The first sed just cleans up the directory names # The second sed tell pkgadd to not force our permissions on directories # The $$ in that sed escapes the $ from make - find $(BUILDROOT) | pkgproto | tail +2 | sed "s,$(BUILDROOT),," | sed '/^d/s/[^ ]* [^ ]* [^ ]*$$/? ? ?/' >> prototype - pkgmk -r $(BUILDROOT) -d $(PWD) - pkgtrans . YPCetch-$(VER).pkg YPCetch - rm -rf YPCetch + find $(BUILDROOT) | pkgproto | tail +2 | sed "s,$(BUILDROOT),," | sed '/^d/s/[^ ]* [^ ]* [^ ]*$$/? ? ?/' >> solbuild/prototype + cd solbuild && pkgmk -r $(BUILDROOT) -d $(PWD)/solbuild + pkgtrans solbuild ../YPCetch-$(VER).pkg YPCetch + rm -rf solbuild rm -rf $(BUILDROOT) - rm -f prototype # On Sparc systems we're having problems with the CSW/Blastwave ruby # core dumping when running etch. The Sunfreeware ruby seems to work. @@ -94,7 +115,7 @@ # our library file (etch.rb) into /opt/csw. We modify etch to use # the Sunfreeware ruby in /usr/local/bin, but then tell it to also look # in the /opt/csw directory for libraries. -solaris-sparc: pkginfo depend +sysvpkg-sparc: pkginfo depend # # Create package file structure in build root # @@ -114,25 +135,31 @@ chmod 444 $(BUILDROOT)/opt/csw/lib/ruby/site_ruby/1.8/etch.rb mkdir -p $(BUILDROOT)/etc/etch cp -p ca.pem $(BUILDROOT)/etc/etch + cp -p dhparams $(BUILDROOT)/etc/etch # Cron job for registration cat etch_cron_wrapper | sed 's,/usr/bin/perl,/opt/csw/bin/perl,' > $(BUILDROOT)/usr/sbin/etch_cron_wrapper chmod 555 $(BUILDROOT)/usr/sbin/etch_cron_wrapper # # Now build the package # - echo "i pkginfo=./pkginfo" > prototype - echo "i depend=./depend" >> prototype - echo "i postinstall=./postinstall" >> prototype - echo "i postremove=./postremove" >> prototype + rm -rf solbuild + mkdir solbuild + sed 's/%VER%/$(VER)/' pkginfo > solbuild/pkginfo + echo "i pkginfo=./pkginfo" > solbuild/prototype + cp depend solbuild/depend + echo "i depend=./depend" >> solbuild/prototype + cp postinstall solbuild/postinstall + echo "i postinstall=./postinstall" >> solbuild/prototype + cp postremove solbuild/postremove + echo "i postremove=./postremove" >> solbuild/prototype # The tail +2 removes the first line, which is the base directory # and doesn't need to be included in the package. # The first sed just cleans up the directory names # The second sed tell pkgadd to not force our permissions on directories # The $$ in that sed escapes the $ from make - find $(BUILDROOT) | pkgproto | tail +2 | sed "s,$(BUILDROOT),," | sed '/^d/s/[^ ]* [^ ]* [^ ]*$$/? ? ?/' >> prototype - pkgmk -r $(BUILDROOT) -d $(PWD) - pkgtrans . YPCetch-$(VER)-sparc.pkg YPCetch - rm -rf YPCetch + find $(BUILDROOT) | pkgproto | tail +2 | sed "s,$(BUILDROOT),," | sed '/^d/s/[^ ]* [^ ]* [^ ]*$$/? ? ?/' >> solbuild/prototype + cd solbuild && pkgmk -r $(BUILDROOT) -d $(PWD)/solbuild + pkgtrans solbuild ../YPCetch-$(VER)-sparc.pkg YPCetch + rm -rf solbuild rm -rf $(BUILDROOT) - rm -f prototype Modified: trunk/client/control =================================================================== --- trunk/client/control 2009-06-25 00:13:39 UTC (rev 86) +++ trunk/client/control 2009-09-11 16:59:21 UTC (rev 87) @@ -1,5 +1,5 @@ Package: etch-client -Version: 1.1-1 +Version: VER-1 Maintainer: etc...@li... Architecture: all Depends: ruby facter rcs Added: trunk/client/dhparams =================================================================== --- trunk/client/dhparams (rev 0) +++ trunk/client/dhparams 2009-09-11 16:59:21 UTC (rev 87) @@ -0,0 +1,9 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA3HySq1WdL67BCSRCJCZYMUIojAWAsvK63D3cOGk0wI9UeM/yeVhz +jTswvHOVPZFKIBg1Aeo2eAEdPryDnmjVTgvLbuWkCPouQhBCVsQ1El9ZcXPix1rC +tYsg4Kll1jgnwFoHf4xvjPnD/SqsASAiDxYlh4CFVyT1gLgSiUU0rIdudgO3agI5 +NgiyGOKwyHmNOOQSKA62M/JnoxcBDC7Nou3lqtHpR5yWsUz+csyk+hXZeUba97bm +M8OB0PmfK4Vo6JpdO+yc8hjeYBoMsH7g/l3Gm1JqUxxctcY/OuJ+2nkXwsD66E3D +yZCoiVd3u4OqAxNO/GG0iUmskjIvokMhUwIBAg== +-----END DH PARAMETERS----- + Modified: trunk/client/etch =================================================================== --- trunk/client/etch 2009-06-25 00:13:39 UTC (rev 86) +++ trunk/client/etch 2009-09-11 16:59:21 UTC (rev 87) @@ -13,25 +13,16 @@ # Parse the command line options # -$generateall = nil -$dryrun = nil -$interactive = nil -$fullfile = nil -$filenameonly = nil -$disableforce = nil -$lockforce = nil -$debug = nil -$server = nil -$tag = nil -$varbase = nil +options = {} +@generateall = nil opts = OptionParser.new opts.banner = 'Usage: etch [options] [/path/to/config/file]' opts.on('--generate-all', 'Can be used instead of giving a specific file to generate.') do |opt| - $generateall = opt + @generateall = opt end opts.on('--dry-run', '-n', 'Prints contents of generated files instead of writing them out to disk.') do |opt| - $dryrun = opt + options[:dryrun] = opt end opts.on('--damp-run', "Perform a dry run but run 'setup' entries for files. Normally all setup/pre/post entries are ignored for a dry run. However, files with setup entries will generally fail to build if the setup entry hasn't been run.") do |opt| # Rather than sprinkle checks of two different variables throught the code, if @@ -39,35 +30,42 @@ # value. Then we can just check for that specific value at the one place where # the two modes differ: enabling or disabling the execution of 'setup' # entries. - $dryrun = 'damp' + options[:dryrun] = 'damp' end opts.on('--interactive', 'Causes etch to pause before making each change and prompt the user for confirmation.') do |opt| - $interactive = opt + options[:interactive] = opt end opts.on('--full-file', 'Normally etch will print a diff to show what changes it will make to a file. This will cause etch to display the full new file contents instead.') do |opt| - $fullfile = opt + options[:fullfile] = opt end opts.on('--filename-only', 'Similar to the previous option, but in the opposite direction. Etch will only display the name of file to be changed.') do |opt| - $filenameonly = opt + options[:filenameonly] = opt end opts.on('--disable-force', 'Ignore the disable_etch file. Use with caution.') do |opt| - $disableforce = opt + options[:disableforce] = opt end opts.on('--lock-force', 'Force the removal of any existing lockfiles. Normally only lockfile over 2 hours old are removed.') do |opt| - $lockforce = opt + options[:lockforce] = opt end opts.on('--debug', 'Print lots of messages about what etch is doing') do |opt| - $debug = opt + options[:debug] = opt end opts.on('--server SERVER', 'Point etch to an alternate server') do |opt| - $server = opt + options[:server] = opt end opts.on('--tag TAG', 'Request a specific repository tag from the server') do |opt| - $tag = opt + options[:tag] = opt end opts.on('--test-base TESTDIR', 'Use an alternate local working directory (for use by test suite only)') do |opt| - $varbase = opt + options[:varbase] = opt end +opts.on('--key PRIVATE_KEY', 'Use this private key for signing messages that are sent to the server') do |opt| + options[:key] = opt +end +opts.on('--version', 'Show etch client version') do |opt| + puts Etch::Client::VERSION + exit +end opts.on_tail("-h", "--help", "Show this message") do puts opts exit @@ -77,7 +75,7 @@ files_to_generate = opts.parse(ARGV) # Display a usage message if the user did not specify a valid action to perform. -unless (!files_to_generate.nil? && !files_to_generate.empty?) || $generateall +unless (!files_to_generate.nil? && !files_to_generate.empty?) || @generateall puts opts exit end @@ -86,7 +84,7 @@ # Do stuff # -etchclient = Etch::Client.new($server, $tag, $varbase, $debug, $dryrun, $interactive, $filenameonly, $fullfile) -status = etchclient.process_until_done(files_to_generate, $disableforce, $lockforce) +etchclient = Etch::Client.new(options) +status = etchclient.process_until_done(files_to_generate) exit status Modified: trunk/client/etch-client.spec =================================================================== --- trunk/client/etch-client.spec 2009-06-25 00:13:39 UTC (rev 86) +++ trunk/client/etch-client.spec 2009-09-11 16:59:21 UTC (rev 87) @@ -1,6 +1,6 @@ Name: etch-client Summary: Etch client -Version: 1.1 +Version: VER Release: 1 Group: Applications/System License: MIT Modified: trunk/client/etch.rb =================================================================== --- trunk/client/etch.rb 2009-06-25 00:13:39 UTC (rev 86) +++ trunk/client/etch.rb 2009-09-11 16:59:21 UTC (rev 87) @@ -2,9 +2,17 @@ # Etch configuration file management tool library ############################################################################## -require 'facter' +begin + # Try loading facter w/o gems first so that we don't introduce a + # dependency on gems if it is not needed. + require 'facter' # Facter +rescue LoadError + require 'rubygems' + require 'facter' +end require 'find' require 'digest/sha1' # hexdigest +require 'openssl' # OpenSSL require 'base64' # decode64, encode64 require 'uri' require 'net/http' @@ -14,24 +22,19 @@ require 'fcntl' # Fcntl::O_* require 'etc' # getpwnam, getgrnam require 'tempfile' # Tempfile +require 'cgi' +require 'timeout' -# clean up "using default DH parameters" warning for https -# http://blog.zenspider.com/2008/05/httpsssl-warning-cleanup.html -class Net::HTTP - alias :old_use_ssl= :use_ssl= - def use_ssl= flag - self.old_use_ssl = flag - @ssl_context.tmp_dh_callback = proc {} - end -end - module Etch end class Etch::Client + VERSION = '1.13' + CONFIRM_PROCEED = 1 CONFIRM_SKIP = 2 CONFIRM_QUIT = 3 + PRIVATE_KEY_PATHS = ["/etc/ssh/ssh_host_rsa_key", "/etc/ssh_host_rsa_key"] # We need these in relation to the output capturing ORIG_STDOUT = STDOUT.dup @@ -39,16 +42,18 @@ attr_reader :exec_once_per_run - # Cutting down the size of the arg list would be nice - def initialize(server=nil, tag=nil, varbase=nil, debug=false, dryrun=false, interactive=false, filenameonly=false, fullfile=false) - @server = server.nil? ? 'https://etch' : server - @tag = tag - @varbase = varbase.nil? ? '/var/etch' : varbase - @debug = debug - @dryrun = dryrun - @interactive = interactive - @filenameonly = filenameonly - @fullfile = fullfile + def initialize(options) + @server = options[:server] ? options[:server] : 'https://etch' + @tag = options[:tag] + @varbase = options[:varbase] ? options[:varbase] : '/var/etch' + @debug = options[:debug] + @dryrun = options[:dryrun] + @interactive = options[:interactive] + @filenameonly = options[:filenameonly] + @fullfile = options[:fullfile] + @key = options[:key] ? options[:key] : get_private_key_path + @disableforce = options[:disableforce] + @lockforce = options[:lockforce] # Ensure we have a sane path, particularly since we are often run from # cron. @@ -61,10 +66,17 @@ @origbase = File.join(@varbase, 'orig') @historybase = File.join(@varbase, 'history') @lockbase = File.join(@varbase, 'locks') + @requestbase = File.join(@varbase, 'requests') @blankrequest = {} @facts = Facter.to_hash + # If the user specified a non-standard key then override the sshrsakey + # fact so that authentication works + if @key + @facts['sshrsakey'] = IO.read(@key+'.pub').chomp.split[1] + end @facts.each_pair { |key, value| @blankrequest["facts[#{key}]"] = value.to_s } + @blankrequest['fqdn'] = @facts['fqdn'] if @facts['operatingsystemrelease'] # Some versions of Facter have a bug that leaves extraneous # whitespace on this fact. Work around that with strip. I.e. on @@ -90,8 +102,8 @@ @lchown_supported = nil @lchmod_supported = nil end - - def process_until_done(files_to_generate, disableforce, lockforce) + + def process_until_done(files_to_generate) # Our overall status. Will be reported to the server and used as the # return value for this method. Command-line clients should use it as # their exit value. Zero indicates no errors. @@ -100,6 +112,12 @@ http = Net::HTTP.new(@filesuri.host, @filesuri.port) if @filesuri.scheme == "https" + # Eliminate the OpenSSL "using default DH parameters" warning + if File.exist?('/etc/etch/dhparams') + dh = OpenSSL::PKey::DH.new(IO.read('/etc/etch/dhparams')) + Net::HTTP.ssl_context_accessor(:tmp_dh_callback) + http.tmp_dh_callback = proc { dh } + end http.use_ssl = true if File.exist?('/etc/etch/ca.pem') http.ca_file = '/etc/etch/ca.pem' @@ -115,14 +133,14 @@ # begin/raise for error events that end processing catch :stop_processing do begin - enabled, message = check_for_disable_etch_file(disableforce) + enabled, message = check_for_disable_etch_file if !enabled # 200 is the arbitrarily picked exit value indicating # that etch is disabled status = 200 throw :stop_processing end - remove_stale_lock_files(lockforce) + remove_stale_lock_files # Assemble the initial request request = get_blank_request @@ -130,6 +148,10 @@ if !files_to_generate.nil? && !files_to_generate.empty? files_to_generate.each do |file| request["files[#{CGI.escape(file)}][sha1sum]"] = get_orig_sum(file) + local_requests = get_local_requests(file) + if local_requests + request["files[#{CGI.escape(file)}][local_requests]"] = local_requests + end end else request['files[GENERATEALL]'] = '1' @@ -155,6 +177,7 @@ puts "Sending request to server #{@filesuri}" if (@debug) post = Net::HTTP::Post.new(@filesuri.path) post.set_form_data(request) + sign_post!(post, @key) response = http.request(post) response_xml = nil case response @@ -198,12 +221,20 @@ response_xml.root.elements.each('/files/need_sums/need_sum') do |need_sum| puts "Processing request for sum of #{need_sum.text}" if (@debug) request["files[#{CGI.escape(need_sum.text)}][sha1sum]"] = get_orig_sum(need_sum.text) + local_requests = get_local_requests(need_sum.text) + if local_requests + request["files[#{CGI.escape(need_sum.text)}][local_requests]"] = local_requests + end need_to_loop = true end response_xml.root.elements.each('/files/need_origs/need_orig') do |need_orig| puts "Processing request for contents of #{need_orig.text}" if (@debug) request["files[#{CGI.escape(need_orig.text)}][contents]"] = Base64.encode64(get_orig_contents(need_orig.text)) request["files[#{CGI.escape(need_orig.text)}][sha1sum]"] = get_orig_sum(need_orig.text) + local_requests = get_local_requests(need_orig.text) + if local_requests + request["files[#{CGI.escape(need_orig.text)}][local_requests]"] = local_requests + end need_to_loop = true end @@ -226,17 +257,18 @@ # Send results to server if !@dryrun rails_results = [] - # CGI.escape doesn't work on things that aren't strings, so we don't - # call it on a few of the fields here that are numbers or booleans + # A few of the fields here are numbers or booleans and need a + # to_s to make them compatible with CGI.escape, which expects a + # string. rails_results << "fqdn=#{CGI.escape(@facts['fqdn'])}" - rails_results << "status=#{status}" + rails_results << "status=#{CGI.escape(status.to_s)}" rails_results << "message=#{CGI.escape(message)}" @results.each do |result| # Strangely enough this works. Even though the key is not unique to # each result the Rails parameter parsing code keeps track of keys it # has seen, and if it sees a duplicate it starts a new hash. rails_results << "results[][file]=#{CGI.escape(result['file'])}" - rails_results << "results[][success]=#{result['success']}" + rails_results << "results[][success]=#{CGI.escape(result['success'].to_s)}" rails_results << "results[][message]=#{CGI.escape(result['message'])}" end puts "Sending results to server #{@resultsuri}" if (@debug) @@ -244,8 +276,10 @@ # We have to bypass Net::HTTP's set_form_data method in this case # because it expects a hash and we can't provide the results in the # format we want in a hash because we'd have duplicate keys (see above). - resultspost.body = rails_results.join('&') + results_as_string = rails_results.join('&') + resultspost.body = results_as_string resultspost.content_type = 'application/x-www-form-urlencoded' + sign_post!(resultspost, @key) response = http.request(resultspost) case response when Net::HTTPSuccess @@ -259,11 +293,11 @@ status end - def check_for_disable_etch_file(disableforce) + def check_for_disable_etch_file disable_etch = File.join(@varbase, 'disable_etch') message = '' if File.exist?(disable_etch) - if !disableforce + if !@disableforce message = "Etch disabled:\n" message << IO.read(disable_etch) puts message @@ -1464,7 +1498,39 @@ histrcspath = "#{histrcsdir}/#{histbase},v" File.chmod(histperms, histrcspath) if (!@dryrun) end - + + def get_local_requests(file) + requestdir = File.join(@requestbase, file) + requestlist = [] + if File.directory?(requestdir) + Dir.foreach(requestdir) do |entry| + next if entry == '.' + next if entry == '..' + requestfile = File.join(requestdir, entry) + request = IO.read(requestfile) + # Make sure it is valid XML + begin + request_xml = REXML::Document.new(request) + rescue REXML::ParseException => e + warn "Local request file #{requestfile} is not valid XML and will be ignored:\n" + e.message + next + end + # Make sure the root element is <request> + if request_xml.root.name != 'request' + warn "Local request file #{requestfile} is not properly formatted and will be ignored, XML root element is not <request>" + next + end + # Add it to the queue + requestlist << request + end + end + requests = nil + if !requestlist.empty? + requests = "<requests>\n#{requestlist.join('')}\n</requests>" + end + requests + end + # Haven't found a Ruby method for creating temporary directories, # so create a temporary file and replace it with a directory. def tempdir(file) @@ -1866,13 +1932,13 @@ # Any etch lockfiles more than a couple hours old are most likely stale # and can be removed. If told to force we remove all lockfiles. - def remove_stale_lock_files(force=false) + def remove_stale_lock_files twohoursago = Time.at(Time.now - 60 * 60 * 2) Find.find(@lockbase) do |file| next unless file =~ /\.LOCK$/ next unless File.file?(file) - if force || File.mtime(file) < twohoursago + if @lockforce || File.mtime(file) < twohoursago puts "Removing stale lock file #{file}" File.delete(file) end @@ -1883,6 +1949,10 @@ @already_processed.clear end + # We limit capturing to 5 minutes. That should be plenty of time + # for etch to handle any given file, including running any + # setup/pre/post commands. + OUTPUT_CAPTURE_TIMEOUT = 5 * 60 def start_output_capture # Establish a pipe, spawn a child process, and redirect stdout/stderr # to the pipe. The child gathers up anything sent over the pipe and @@ -1928,9 +1998,20 @@ # newline. $stdout.sync = true output = '' - while char = pread.getc - putc(char) - output << char.chr + begin + # A surprising number of apps that we restart are ill-behaved and do + # not properly close stdin/stdout/stderr. With etch's output + # capturing feature this results in etch hanging around forever + # waiting for the pipes to close. We time out after a suitable + # period of time so that etch processes don't hang around forever. + Timeout.timeout(OUTPUT_CAPTURE_TIMEOUT) do + while char = pread.getc + putc(char) + output << char.chr + end + end + rescue Timeout::Error + $stderr.puts "Timeout in output capture, some app restarted via post probably didn't daemonize properly" end pread.close owrite.write(output) @@ -1959,5 +2040,40 @@ Process.wait output end + + def get_private_key_path + key = nil + PRIVATE_KEY_PATHS.each do |path| + if File.readable?(path) + key = path + break + end + end + if !key + warn "No readable private key found, messages to server will not be signed and may be rejected depending on server configuration" + end + key + end + + # This method takes in a Net::HTTP::Post and a path to a private key. + # It will insert a 'timestamp' parameter to the post body, hash the body of + # the post, sign the hash using the private key, and insert that signature + # in the HTTP Authorization header field in the post. + def sign_post!(post, key) + if key + post.body << "×tamp=#{CGI.escape(Time.now.to_s)}" + private_key = OpenSSL::PKey::RSA.new(File.read(key)) + hashed_body = Digest::SHA1.hexdigest(post.body) + signature = Base64.encode64(private_key.private_encrypt(hashed_body)) + # encode64 breaks lines at 60 characters with newlines. Having newlines + # in an HTTP header screws things up (the lines get interpreted as + # separate headers) so strip them out. The Base64 standards seem to + # generally have a limit on line length, but Ruby's decode64 doesn't + # seem to complain. If it ever becomes a problem the server could + # rebreak the lines. + signature.gsub!("\n", '') + post['Authorization'] = "EtchSignature #{signature}" + end + end end Modified: trunk/client/pkginfo =================================================================== --- trunk/client/pkginfo 2009-06-25 00:13:39 UTC (rev 86) +++ trunk/client/pkginfo 2009-09-11 16:59:21 UTC (rev 87) @@ -1,7 +1,7 @@ PKG="OSSetch" NAME="Etch client" ARCH="sparc,i386" -VERSION="1.1" +VERSION="%VER%" CATEGORY="application" CLASSES="none" PSTAMP="none" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-06-25 01:03:37
|
Revision: 86 http://etch.svn.sourceforge.net/etch/?rev=86&view=rev Author: jheiss Date: 2009-06-25 00:13:39 +0000 (Thu, 25 Jun 2009) Log Message: ----------- Use openssl to generate md5 and sha1 checksum files rather than some messy code to check for md5 or md5sum executables. Modified Paths: -------------- Makefile Modified: Makefile =================================================================== --- Makefile 2009-06-18 00:28:05 UTC (rev 85) +++ Makefile 2009-06-25 00:13:39 UTC (rev 86) @@ -1,19 +1,6 @@ VER=3.5 TAGNAME=release-$(VER) -# Perhaps someone with better make skill than I've got can make this -# more elegant -# Note, must indent with spaces here -MD5=$(shell which md5) -MD5SUM=$(shell which md5) -ifdef MD5SUM - MD5PROG=$(MD5) -else ifdef MD5 - MD5PROG=$(MD5SUM) -else - $(error Couldn't find an MD5 program) -endif - all: dist test: @@ -23,7 +10,8 @@ (cd tags/$(TAGNAME) && find client server test etchserver-* README | grep -v '\.svn' | cpio -pdum ../../etch-$(VER)) tar czf etch-$(VER).tar.gz etch-$(VER) rm -rf etch-$(VER) - $(MD5PROG) etch-$(VER).tar.gz > etch-$(VER).tar.gz.md5 + openssl md5 etch-$(VER).tar.gz > etch-$(VER).tar.gz.md5 + openssl sha1 etch-$(VER).tar.gz > etch-$(VER).tar.gz.sha1 gpg --detach --armor etch-$(VER).tar.gz tag: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-06-18 01:05:47
|
Revision: 85 http://etch.svn.sourceforge.net/etch/?rev=85&view=rev Author: jheiss Date: 2009-06-18 00:28:05 +0000 (Thu, 18 Jun 2009) Log Message: ----------- Slightly simplify the URL in the comments Modified Paths: -------------- trunk/etchserver-samples/source/var/sadm/install/admin/noask/noask Modified: trunk/etchserver-samples/source/var/sadm/install/admin/noask/noask =================================================================== --- trunk/etchserver-samples/source/var/sadm/install/admin/noask/noask 2009-06-18 00:08:49 UTC (rev 84) +++ trunk/etchserver-samples/source/var/sadm/install/admin/noask/noask 2009-06-18 00:28:05 UTC (rev 85) @@ -1,4 +1,4 @@ -# http://docs.sun.com/app/docs/doc/805-7228/6j6q7uerj?l=en&a=view +# http://docs.sun.com/app/docs/doc/805-7228/6j6q7uerj?a=view # "Avoiding User Interaction When Adding Packages" mail= instance=overwrite This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-06-18 00:09:52
|
Revision: 84 http://etch.svn.sourceforge.net/etch/?rev=84&view=rev Author: jheiss Date: 2009-06-18 00:08:49 +0000 (Thu, 18 Jun 2009) Log Message: ----------- Use pool.ntp.org servers rather than real server names so we don't encourage too much use of specific stratum 0 and 1 servers. Modified Paths: -------------- trunk/etchserver-samples/source/etc/inet/ntp.conf/ntp.conf.server Modified: trunk/etchserver-samples/source/etc/inet/ntp.conf/ntp.conf.server =================================================================== --- trunk/etchserver-samples/source/etc/inet/ntp.conf/ntp.conf.server 2009-06-18 00:08:05 UTC (rev 83) +++ trunk/etchserver-samples/source/etc/inet/ntp.conf/ntp.conf.server 2009-06-18 00:08:49 UTC (rev 84) @@ -4,11 +4,7 @@ # Permit all access from localhost restrict 127.0.0.1 -server time.caltech.edu -server time.apple.com -server clock.isc.org -server tick.ucla.edu -server timekeeper.isi.edu -server nist1-dc.witime.net -server nist1-ny.witime.net +server 0.pool.ntp.org +server 1.pool.ntp.org +server 2.pool.ntp.org This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-06-18 00:09:12
|
Revision: 83 http://etch.svn.sourceforge.net/etch/?rev=83&view=rev Author: jheiss Date: 2009-06-18 00:08:05 +0000 (Thu, 18 Jun 2009) Log Message: ----------- Remove full path to post command, etch sets a reasonable path internally. Modified Paths: -------------- trunk/etchserver-samples/source/etc/inet/inetd.conf/config.xml Modified: trunk/etchserver-samples/source/etc/inet/inetd.conf/config.xml =================================================================== --- trunk/etchserver-samples/source/etc/inet/inetd.conf/config.xml 2009-06-18 00:07:34 UTC (rev 82) +++ trunk/etchserver-samples/source/etc/inet/inetd.conf/config.xml 2009-06-18 00:08:05 UTC (rev 83) @@ -5,7 +5,7 @@ </source> </file> <post> - <exec>/usr/bin/pkill -HUP inetd</exec> + <exec>pkill -HUP inetd</exec> </post> </config> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-06-18 00:08:58
|
Revision: 82 http://etch.svn.sourceforge.net/etch/?rev=82&view=rev Author: jheiss Date: 2009-06-18 00:07:34 +0000 (Thu, 18 Jun 2009) Log Message: ----------- Fix shebang line Modified Paths: -------------- trunk/etchserver-samples/source/etc/inet/inetd.conf/inetd.conf.script Modified: trunk/etchserver-samples/source/etc/inet/inetd.conf/inetd.conf.script =================================================================== --- trunk/etchserver-samples/source/etc/inet/inetd.conf/inetd.conf.script 2009-06-18 00:07:09 UTC (rev 81) +++ trunk/etchserver-samples/source/etc/inet/inetd.conf/inetd.conf.script 2009-06-18 00:07:34 UTC (rev 82) @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/ruby IO.foreach(@original_file) do |line| if line =~ /^\s*#/ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-06-18 00:08:56
|
Revision: 81 http://etch.svn.sourceforge.net/etch/?rev=81&view=rev Author: jheiss Date: 2009-06-18 00:07:09 +0000 (Thu, 18 Jun 2009) Log Message: ----------- Combine Red Hat and CentOS lines using regex Modified Paths: -------------- trunk/etchserver-samples/source/etc/sysconfig/clock/config.xml Modified: trunk/etchserver-samples/source/etc/sysconfig/clock/config.xml =================================================================== --- trunk/etchserver-samples/source/etc/sysconfig/clock/config.xml 2009-04-02 01:21:34 UTC (rev 80) +++ trunk/etchserver-samples/source/etc/sysconfig/clock/config.xml 2009-06-18 00:07:09 UTC (rev 81) @@ -1,8 +1,7 @@ <config> <file> <source> - <script operatingsystem="RedHat">clock.script</script> - <script operatingsystem="CentOS">clock.script</script> + <script operatingsystem="/RedHat|CentOS/">clock.script</script> </source> </file> </config> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: SourceForge.net <no...@so...> - 2009-04-11 05:48:45
|
Support Requests item #2728740, was opened at 2009-04-03 12:26 Message generated for change (Settings changed) made by jheiss You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=1093659&aid=2728740&group_id=234501 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Install Problem (example) Group: None >Status: Closed Priority: 5 Private: No Submitted By: Christian Lyra (poplyra) Assigned to: Jason Heiss (jheiss) Summary: Etch fails to rake db:migrate Initial Comment: I´m trying to install etch on lenny (sounds funny on debian...), and while trying to rake db:migrate i got this: # rake db:migrate (in /usr/src/etch/trunk/server) rake aborted! no such file to load -- openssl (See full trace by running task with --trace) maybe something is missing? ---------------------------------------------------------------------- Comment By: Jason Heiss (jheiss) Date: 2009-04-10 22:46 Message: Gack, sorry for the slow reply. I lost track of this somehow. A quick check of Google indicates that Debian and Ubuntu package the Ruby OpenSSL library as a separate package call libopenssl-ruby. I built up a Debian box in a virtual machine and confirmed that installing that package with apt-get fixes this error. I've added Debian instructions to http://etch.wiki.sourceforge.net/GettingStarted ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=1093659&aid=2728740&group_id=234501 |
From: SourceForge.net <no...@so...> - 2009-04-11 05:47:45
|
Support Requests item #2728740, was opened at 2009-04-03 12:26 Message generated for change (Settings changed) made by jheiss You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=1093659&aid=2728740&group_id=234501 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Install Problem (example) Group: None Status: Open Priority: 5 Private: No Submitted By: Christian Lyra (poplyra) >Assigned to: Jason Heiss (jheiss) Summary: Etch fails to rake db:migrate Initial Comment: I´m trying to install etch on lenny (sounds funny on debian...), and while trying to rake db:migrate i got this: # rake db:migrate (in /usr/src/etch/trunk/server) rake aborted! no such file to load -- openssl (See full trace by running task with --trace) maybe something is missing? ---------------------------------------------------------------------- Comment By: Jason Heiss (jheiss) Date: 2009-04-10 22:46 Message: Gack, sorry for the slow reply. I lost track of this somehow. A quick check of Google indicates that Debian and Ubuntu package the Ruby OpenSSL library as a separate package call libopenssl-ruby. I built up a Debian box in a virtual machine and confirmed that installing that package with apt-get fixes this error. I've added Debian instructions to http://etch.wiki.sourceforge.net/GettingStarted ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=1093659&aid=2728740&group_id=234501 |
From: SourceForge.net <no...@so...> - 2009-04-11 05:46:53
|
Support Requests item #2728740, was opened at 2009-04-03 12:26 Message generated for change (Comment added) made by jheiss You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=1093659&aid=2728740&group_id=234501 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Install Problem (example) Group: None Status: Open Priority: 5 Private: No Submitted By: Christian Lyra (poplyra) Assigned to: Nobody/Anonymous (nobody) Summary: Etch fails to rake db:migrate Initial Comment: I´m trying to install etch on lenny (sounds funny on debian...), and while trying to rake db:migrate i got this: # rake db:migrate (in /usr/src/etch/trunk/server) rake aborted! no such file to load -- openssl (See full trace by running task with --trace) maybe something is missing? ---------------------------------------------------------------------- >Comment By: Jason Heiss (jheiss) Date: 2009-04-10 22:46 Message: Gack, sorry for the slow reply. I lost track of this somehow. A quick check of Google indicates that Debian and Ubuntu package the Ruby OpenSSL library as a separate package call libopenssl-ruby. I built up a Debian box in a virtual machine and confirmed that installing that package with apt-get fixes this error. I've added Debian instructions to http://etch.wiki.sourceforge.net/GettingStarted ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=1093659&aid=2728740&group_id=234501 |
From: SourceForge.net <no...@so...> - 2009-04-03 19:26:15
|
Support Requests item #2728740, was opened at 2009-04-03 15:26 Message generated for change (Tracker Item Submitted) made by poplyra You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=1093659&aid=2728740&group_id=234501 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Install Problem (example) Group: None Status: Open Priority: 5 Private: No Submitted By: Christian Lyra (poplyra) Assigned to: Nobody/Anonymous (nobody) Summary: Etch fails to rake db:migrate Initial Comment: I´m trying to install etch on lenny (sounds funny on debian...), and while trying to rake db:migrate i got this: # rake db:migrate (in /usr/src/etch/trunk/server) rake aborted! no such file to load -- openssl (See full trace by running task with --trace) maybe something is missing? ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=1093659&aid=2728740&group_id=234501 |
From: <jh...@us...> - 2009-04-02 01:21:39
|
Revision: 80 http://etch.svn.sourceforge.net/etch/?rev=80&view=rev Author: jheiss Date: 2009-04-02 01:21:34 +0000 (Thu, 02 Apr 2009) Log Message: ----------- Tag 3.5 release Modified Paths: -------------- Makefile Added Paths: ----------- tags/release-3.5/ Modified: Makefile =================================================================== --- Makefile 2009-04-02 00:21:34 UTC (rev 79) +++ Makefile 2009-04-02 01:21:34 UTC (rev 80) @@ -1,7 +1,7 @@ -VER=3.4 +VER=3.5 TAGNAME=release-$(VER) -# Perhaps somewith with better make skill than I've got can make this +# Perhaps someone with better make skill than I've got can make this # more elegant # Note, must indent with spaces here MD5=$(shell which md5) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-04-02 00:21:36
|
Revision: 79 http://etch.svn.sourceforge.net/etch/?rev=79&view=rev Author: jheiss Date: 2009-04-02 00:21:34 +0000 (Thu, 02 Apr 2009) Log Message: ----------- Convert the cron wrapper script to Ruby so that the client package has no dependency on Perl. Modified Paths: -------------- trunk/client/control trunk/client/depend trunk/client/etch_cron_wrapper Modified: trunk/client/control =================================================================== --- trunk/client/control 2009-04-02 00:20:54 UTC (rev 78) +++ trunk/client/control 2009-04-02 00:21:34 UTC (rev 79) @@ -2,6 +2,6 @@ Version: 1.1-1 Maintainer: etc...@li... Architecture: all -Depends: ruby facter rcs libdigest-sha1-perl +Depends: ruby facter rcs Description: Etch client Modified: trunk/client/depend =================================================================== --- trunk/client/depend 2009-04-02 00:20:54 UTC (rev 78) +++ trunk/client/depend 2009-04-02 00:21:34 UTC (rev 79) @@ -1,5 +1,4 @@ P CSWruby P CSWfacter -P CSWpmdigestsha1 P CSWrcs Modified: trunk/client/etch_cron_wrapper =================================================================== --- trunk/client/etch_cron_wrapper 2009-04-02 00:20:54 UTC (rev 78) +++ trunk/client/etch_cron_wrapper 2009-04-02 00:21:34 UTC (rev 79) @@ -1,20 +1,18 @@ -#!/usr/bin/perl +#!/usr/bin/ruby -w -use Digest::SHA1 qw(sha1_hex); -use Sys::Hostname; -use strict; -use warnings; +require 'socket' +require 'digest/sha1' # Seed the random number generator with the hostname of this box so that # we get a consistent random number. We want to run the registration at a # consistent time on each individual box, but randomize the runs across # the environment. -srand(hex(substr(sha1_hex(hostname), 0, 7))); +srand(Digest::SHA1.hexdigest(Socket.gethostname)[0,7].hex) # Cron job is set to run every hour -my $MAX_SLEEP = 60 * 60; +MAX_SLEEP = 60 * 60 -sleep(int(rand($MAX_SLEEP))); +sleep(rand(MAX_SLEEP)) -exec '/usr/sbin/etch', '--generate-all'; +exec('/usr/sbin/etch', '--generate-all') This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-04-02 00:20:55
|
Revision: 78 http://etch.svn.sourceforge.net/etch/?rev=78&view=rev Author: jheiss Date: 2009-04-02 00:20:54 +0000 (Thu, 02 Apr 2009) Log Message: ----------- Work around Facter bug on the operatingsystemrelease fact. Modified Paths: -------------- trunk/client/etch.rb Modified: trunk/client/etch.rb =================================================================== --- trunk/client/etch.rb 2009-04-02 00:20:31 UTC (rev 77) +++ trunk/client/etch.rb 2009-04-02 00:20:54 UTC (rev 78) @@ -65,6 +65,12 @@ @blankrequest = {} @facts = Facter.to_hash @facts.each_pair { |key, value| @blankrequest["facts[#{key}]"] = value.to_s } + if @facts['operatingsystemrelease'] + # Some versions of Facter have a bug that leaves extraneous + # whitespace on this fact. Work around that with strip. I.e. on + # CentOS you'll get '5 ' or '5.2 '. + @facts['operatingsystemrelease'].strip! + end if @debug @blankrequest['debug'] = '1' end This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-04-02 00:20:32
|
Revision: 77 http://etch.svn.sourceforge.net/etch/?rev=77&view=rev Author: jheiss Date: 2009-04-02 00:20:31 +0000 (Thu, 02 Apr 2009) Log Message: ----------- Import improved Version class from tpkg. Many bug fixes. tpkg has unit tests for the class, any changes should be made and tested there and imported to etch. Modified Paths: -------------- trunk/server/lib/versiontype.rb Modified: trunk/server/lib/versiontype.rb =================================================================== --- trunk/server/lib/versiontype.rb 2009-04-02 00:20:11 UTC (rev 76) +++ trunk/server/lib/versiontype.rb 2009-04-02 00:20:31 UTC (rev 77) @@ -14,6 +14,25 @@ def <=>(other) ourfields = @version.split('.') otherfields = other.to_s.split('.') + # Convert anything like '.5' to '0.5' + # '.5'.split('.') returns ['', '5'] + [ourfields, otherfields].each do |fields| + if fields[0] == '' + fields[0] = '0' + end + end + # Pad with zeros so that '1' == '1.0', etc. + if ourfields.length != otherfields.length + larger = [ourfields.length, otherfields.length].max + # For the longer number this depends on something like (3...1).each + # doing nothing. That currently works, but is not documented behavior. + (ourfields.length...larger).each { ourfields << '0' } + (otherfields.length...larger).each { otherfields << '0' } + end + # Convert fields of all digits from string to number to get a numeric + # rather than string comparison. This ensures that 5.9 < 5.10 + ourfields.map! { |field| if field =~ /^\d+$/ then field.to_i else field end } + otherfields.map! { |field| if field =~ /^\d+$/ then field.to_i else field end } # Array conveniently implements <=> ourfields <=> otherfields end This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-04-02 00:20:23
|
Revision: 76 http://etch.svn.sourceforge.net/etch/?rev=76&view=rev Author: jheiss Date: 2009-04-02 00:20:11 +0000 (Thu, 02 Apr 2009) Log Message: ----------- Fix handling of operator comparisons in attribute filtering. Improve test for same. Rescue any exceptions in configfilter! where it is called by generate file so that we can insert the name of the config.xml file that we were handling when the exception occurred. This should help the user with debugging. Same logic as the exception catch/re-raise that occurs when running user scripts. Modified Paths: -------------- trunk/server/lib/etchserver.rb trunk/test/attributes.rb Modified: trunk/server/lib/etchserver.rb =================================================================== --- trunk/server/lib/etchserver.rb 2009-02-06 19:47:29 UTC (rev 75) +++ trunk/server/lib/etchserver.rb 2009-04-02 00:20:11 UTC (rev 76) @@ -344,7 +344,11 @@ config_xml = LibXML::XML::Document.file(config_xml_file) # Filter the config.xml file by looking for attributes - configfilter!(config_xml.root) + begin + configfilter!(config_xml.root) + rescue Exception => e + raise e.exception("Error filtering config.xml for #{file}:\n" + e.message) + end # Validate the filtered file against config.dtd if !config_xml.validate(@config_dtd) @@ -939,7 +943,7 @@ operator = $1 valueversion = Version.new($2) compversion = Version.new(comp) - if valueversion.send(operator.to_sym, compversion) + if compversion.send(operator.to_sym, valueversion) result = true end # Regular expressions Modified: trunk/test/attributes.rb =================================================================== --- trunk/test/attributes.rb 2009-02-06 19:47:29 UTC (rev 75) +++ trunk/test/attributes.rb 2009-04-02 00:20:11 UTC (rev 76) @@ -481,7 +481,17 @@ # Version fact operator comparison # testname = 'version fact operator comparison' - + + # Try to make up a subset of operatingsystemrelease so that we really + # test the operator functionality and not just equality. I.e. if osrel + # is 2.5.1 we'd like to extract 2.5 + osrelsubset = osrel + osrelparts = osrel.split('.') + if osrelparts.length > 1 + osrelparts.pop + osrelsubset = osrelparts.join('.') + end + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| file.puts <<-EOF @@ -489,7 +499,7 @@ <file> <warning_file/> <source> - <plain operatingsystemrelease=">=#{osrel}">source</plain> + <plain operatingsystemrelease=">=#{osrelsubset}">source</plain> </source> </file> </config> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-02-06 19:47:36
|
Revision: 75 http://etch.svn.sourceforge.net/etch/?rev=75&view=rev Author: jheiss Date: 2009-02-06 19:47:29 +0000 (Fri, 06 Feb 2009) Log Message: ----------- Work around LibXML's poor handling of non-existent files. Modified Paths: -------------- trunk/server/lib/etchserver.rb Modified: trunk/server/lib/etchserver.rb =================================================================== --- trunk/server/lib/etchserver.rb 2009-02-06 17:43:29 UTC (rev 74) +++ trunk/server/lib/etchserver.rb 2009-02-06 19:47:29 UTC (rev 75) @@ -333,8 +333,15 @@ end @filestack[file] = true + # LibXML doesn't handle attempting to open a non-existent file well + # http://rubyforge.org/tracker/index.php?func=detail&aid=23836&group_id=494&atid=1971 + config_xml_file = File.join(@sourcebase, file, 'config.xml') + if !File.exist?(config_xml_file) + raise "config.xml for #{file} does not exist" + end + # Load the config.xml file - config_xml = LibXML::XML::Document.file(File.join(@sourcebase, file, 'config.xml')) + config_xml = LibXML::XML::Document.file(config_xml_file) # Filter the config.xml file by looking for attributes configfilter!(config_xml.root) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jh...@us...> - 2009-02-06 17:43:32
|
Revision: 74 http://etch.svn.sourceforge.net/etch/?rev=74&view=rev Author: jheiss Date: 2009-02-06 17:43:29 +0000 (Fri, 06 Feb 2009) Log Message: ----------- Add a make target to make a variant of the Solaris package for Sparc systems to work around problems we're having with the CSW ruby package. See the comments added to the file here for more info. Modified Paths: -------------- trunk/client/Makefile Modified: trunk/client/Makefile =================================================================== --- trunk/client/Makefile 2009-02-06 17:42:49 UTC (rev 73) +++ trunk/client/Makefile 2009-02-06 17:43:29 UTC (rev 74) @@ -87,3 +87,52 @@ rm -rf $(BUILDROOT) rm -f prototype +# On Sparc systems we're having problems with the CSW/Blastwave ruby +# core dumping when running etch. The Sunfreeware ruby seems to work. +# Sunfreeware doesn't play well with pkg-get, so we create a bit of a +# hybrid. We still express all the dependencies against CSW, and put +# our library file (etch.rb) into /opt/csw. We modify etch to use +# the Sunfreeware ruby in /usr/local/bin, but then tell it to also look +# in the /opt/csw directory for libraries. +solaris-sparc: pkginfo depend + # + # Create package file structure in build root + # + rm -rf $(BUILDROOT) + mkdir -p $(BUILDROOT)/usr/sbin + cp -p etch $(BUILDROOT)/usr/sbin + mv $(BUILDROOT)/usr/sbin/etch $(BUILDROOT)/usr/sbin/etch.tmp + cat $(BUILDROOT)/usr/sbin/etch.tmp | sed 's,/usr/bin/ruby,/usr/local/bin/ruby,' > $(BUILDROOT)/usr/sbin/etch + rm $(BUILDROOT)/usr/sbin/etch.tmp + mv $(BUILDROOT)/usr/sbin/etch $(BUILDROOT)/usr/sbin/etch.tmp + # The $$ in that awk escapes the $ from make + cat $(BUILDROOT)/usr/sbin/etch.tmp | awk '/unshift.*__FILE__/ {print "$$:.unshift \"/opt/csw/lib/ruby/site_ruby/1.8\"\n" $$0; next}; {print}' > $(BUILDROOT)/usr/sbin/etch + rm $(BUILDROOT)/usr/sbin/etch.tmp + chmod 555 $(BUILDROOT)/usr/sbin/etch + mkdir -p $(BUILDROOT)/opt/csw/lib/ruby/site_ruby/1.8 + cp -p etch.rb $(BUILDROOT)/opt/csw/lib/ruby/site_ruby/1.8 + chmod 444 $(BUILDROOT)/opt/csw/lib/ruby/site_ruby/1.8/etch.rb + mkdir -p $(BUILDROOT)/etc/etch + cp -p ca.pem $(BUILDROOT)/etc/etch + # Cron job for registration + cat etch_cron_wrapper | sed 's,/usr/bin/perl,/opt/csw/bin/perl,' > $(BUILDROOT)/usr/sbin/etch_cron_wrapper + chmod 555 $(BUILDROOT)/usr/sbin/etch_cron_wrapper + # + # Now build the package + # + echo "i pkginfo=./pkginfo" > prototype + echo "i depend=./depend" >> prototype + echo "i postinstall=./postinstall" >> prototype + echo "i postremove=./postremove" >> prototype + # The tail +2 removes the first line, which is the base directory + # and doesn't need to be included in the package. + # The first sed just cleans up the directory names + # The second sed tell pkgadd to not force our permissions on directories + # The $$ in that sed escapes the $ from make + find $(BUILDROOT) | pkgproto | tail +2 | sed "s,$(BUILDROOT),," | sed '/^d/s/[^ ]* [^ ]* [^ ]*$$/? ? ?/' >> prototype + pkgmk -r $(BUILDROOT) -d $(PWD) + pkgtrans . YPCetch-$(VER)-sparc.pkg YPCetch + rm -rf YPCetch + rm -rf $(BUILDROOT) + rm -f prototype + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |