Commit cce2b1a27706cd2c83bc391301cad68c69840ce6

Task system for async repos and ssh keys handling

Commit diff

app/models/repository.rb

 
3737 end
3838
3939 def full_repository_path
40 File.expand_path(File.join(BASE_REPOSITORY_DIR, gitdir))
40 self.class.full_path_from_partial_path(gitdir)
4141 end
4242
43 def create_git_repository
44 git_backend.create(full_repository_path)
43 def self.create_git_repository(path)
44 git_backend.create(full_path_from_partial_path(path))
4545 end
4646
47 def delete_git_repository
48 git_backend.delete!(full_repository_path)
47 def self.delete_git_repository(path)
48 git_backend.delete!(full_path_from_partial_path(path))
4949 end
5050
5151 def has_commits?
5252 git_backend.repository_has_commits?(full_repository_path)
5353 end
5454
55 def self.git_backend
56 RAILS_ENV == "test" ? MockGitBackend : GitBackend
57 end
58
5559 def git_backend
5660 RAILS_ENV == "test" ? MockGitBackend : GitBackend
5761 end
7575 end
7676
7777 def create_new_repos_task
78 Task.create!(:target => self, :command => "create_git_repository")
78 Task.create!(:target_class => self.class.name,
79 :command => "create_git_repository", :arguments => gitdir)
7980 end
8081
8182 def create_delete_repos_task
82 Task.create!(:target => self, :command => "delete_git_repository")
83 Task.create!(:target_class => self.class.name,
84 :command => "delete_git_repository", :arguments => gitdir)
8385 end
8486
8587 protected
9494 def add_user_as_committer
9595 committers << user
9696 end
97
98 def self.full_path_from_partial_path(path)
99 File.expand_path(File.join(BASE_REPOSITORY_DIR, path))
100 end
97101end
toggle raw diff

app/models/ssh_key.rb

 
2222 %Q{\n### END KEY #{self.id || "nil"} ###}
2323 end
2424
25 def add_to_authorized_keys(key_file_class=SshKeyFile)
25 def self.add_to_authorized_keys(keydata, key_file_class=SshKeyFile)
2626 key_file = key_file_class.new
27 key_file.add_key(self.to_key)
27 key_file.add_key(keydata)
2828 end
2929
30 def delete_from_authorized_keys(key_file_class=SshKeyFile)
30 def self.delete_from_authorized_keys(keydata, key_file_class=SshKeyFile)
3131 key_file = key_file_class.new
32 key_file.delete_key(self.to_key)
32 key_file.delete_key(keydata)
3333 end
3434
3535 def create_new_task
36 Task.create!(:target => self, :command => "add_to_authorized_keys")
36 Task.create!(:target_class => self.class.name,
37 :command => "add_to_authorized_keys", :arguments => self.to_key)
3738 end
3839
3940 def create_delete_task
40 Task.create!(:target => self, :command => "delete_from_authorized_keys")
41 Task.create!(:target_class => self.class.name,
42 :command => "delete_from_authorized_keys", :arguments => self.to_key)
4143 end
4244
4345 protected
toggle raw diff

app/models/task.rb

 
55 find(:all, :conditions => {:performed => false})
66 end
77
8 def self.perform_all_pending!
9 find_all_pending.each do |task|
10 task.perform!
8 def self.perform_all_pending!(log=RAILS_DEFAULT_LOGGER)
9 tasks_to_perform = find_all_pending
10 log.info("Got #{tasks_to_perform.size.inspect} tasks to perform...")
11 tasks_to_perform.each do |task|
12 task.perform!(log)
1113 end
1214 end
1315
14 def perform!
16 def perform!(log=RAILS_DEFAULT_LOGGER)
1517 transaction do
16 target.send(command)
18 log.info("Performing Task #{self.id.inspect}: #{target_class}::#{command}(#{arguments[0..64].inspect}..)")
19 target_class.constantize.send(command, arguments)
1720 self.performed = true
1821 self.performed_at = Time.now
1922 save!
toggle raw diff

db/migrate/016_create_tasks.rb

 
11class CreateTasks < ActiveRecord::Migration
22 def self.up
33 create_table :tasks do |t|
4 t.integer :target_id
5 t.string :target_type
4 t.string :target_class
65 t.string :command
6 t.text :arguments
77 t.boolean :performed, :default => false
88 t.datetime :performed_at
99 t.timestamps
1010 end
11 add_index :tasks, :target_id
12 add_index :tasks, :target_type
1311 add_index :tasks, :performed
1412 end
1513
toggle raw diff

db/schema.rb

 
8181 end
8282
8383 create_table "tasks", :force => true do |t|
84 t.integer "target_id"
85 t.string "target_type"
84 t.string "target_class"
8685 t.string "command"
86 t.text "arguments"
8787 t.boolean "performed", :default => false
8888 t.datetime "performed_at"
8989 t.datetime "created_at"
9090 t.datetime "updated_at"
9191 end
9292
93 add_index "tasks", ["target_id"], :name => "index_tasks_on_target_id"
94 add_index "tasks", ["target_type"], :name => "index_tasks_on_target_type"
9593 add_index "tasks", ["performed"], :name => "index_tasks_on_performed"
9694
9795 create_table "users", :force => true do |t|
toggle raw diff

script/task_performer

 
1#!/usr/bin/env ruby
2
3ENV["RAILS_ENV"] ||= "production"
4require File.dirname(__FILE__) + "/../config/environment"
5require "fileutils"
6
7LOCK_FILE_PATH = File.join(RAILS_ROOT, "tmp", "task_lockfile")
8
9class TaskLockError < StandardError; end
10
11def with_lock(&block)
12 begin
13 if File.exist?(LOCK_FILE_PATH)
14 raise TaskLockError
15 end
16 FileUtils.touch(LOCK_FILE_PATH)
17 yield
18 FileUtils.rm(LOCK_FILE_PATH)
19 rescue TaskLockError
20 $stderr.puts "Task lockfile exists"
21 exit(1)
22 end
23end
24
25with_lock do
26 log = Logger.new(File.join(RAILS_ROOT, "log", "tasks.log"))
27 log.formatter = Logger::Formatter.new
28 log.formatter.datetime_format = "%Y-%m-%d %H:%M:%S"
29 Task.perform_all_pending!(log)
30end
toggle raw diff

spec/fixtures/tasks.yml

 
11# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
22create_repo:
33 id: 1
4 target_id: 1
5 target_type: Repository
4 target_class: Repository
65 command: create_git_repository
6 arguments: foo/bar.git
77 performed: 0
88
99add_key:
1010 id: 2
11 target_id: 1
12 target_type: SshKey
11 target_class: SshKey
1312 command: add_to_authorized_keys
13 arguments: |-
14 ### START KEY 2 ###
15 command="gitorious johan",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa bXljYWtkZHlpemltd21vY2NqdGJnaHN2bXFjdG9zbXplaGlpZnZ0a3VyZWFc2dkanB4aXNxamxieGVib3l6Z3hmb2ZxZW15Y2FrZGR5aXppbXdtb2NjanRiZ2hzdm1xY3Rvc216ZWhpaWZ2dGt1cmVhc3NnZGpweGlzcWpsYnhlYm95emd4Zm9mcWU= foo@example.com
16 ### END KEY 2 ###
1417 performed: 0
toggle raw diff

spec/models/repository_spec.rb

 
8181 end
8282
8383 it "inits the git repository" do
84 @repository.git_backend.should_receive(:create).with(@repository.full_repository_path).and_return(true)
85 @repository.create_git_repository
84 Repository.git_backend.should_receive(:create).with(@repository.full_repository_path).and_return(true)
85 Repository.create_git_repository(@repository.gitdir)
8686 end
8787
8888 it "deletes a repository" do
89 @repository.git_backend.should_receive(:delete!).with(@repository.full_repository_path).and_return(true)
90 @repository.delete_git_repository
89 Repository.git_backend.should_receive(:delete!).with(@repository.full_repository_path).and_return(true)
90 Repository.delete_git_repository(@repository.gitdir)
9191 end
9292
9393 it "knows if has commits" do
130130 proc{
131131 @repository.save!
132132 }.should change(Task, :count)
133 task = Task.find(:first, :conditions => ["target_id = ?", @repository.id], :order => "id desc")
133 task = Task.find(:first, :conditions => ["target_class = 'Repository'"], :order => "id desc")
134134 task.command.should == "create_git_repository"
135 task.arguments.should match(/#{@repository.gitdir}$/)
135136 end
136137
137138 it "creates a Task on destroy" do
140140 proc{
141141 @repository.destroy
142142 }.should change(Task, :count)
143 task = Task.find(:first, :conditions => ["target_id = ?", @repository.id], :order => "id desc")
143 task = Task.find(:first, :conditions => ["target_class = 'Repository'"], :order => "id desc")
144144 task.command.should == "delete_git_repository"
145 task.arguments.should match(/#{@repository.gitdir}$/)
145146 end
146147end
toggle raw diff

spec/models/ssh_key_spec.rb

 
6363 ssh_key = new_key
6464 ssh_key_file_mock.should_receive(:new).and_return(ssh_key_file_mock)
6565 ssh_key_file_mock.should_receive(:add_key).with(ssh_key.to_key).and_return(true)
66 ssh_key.add_to_authorized_keys(ssh_key_file_mock)
66 SshKey.add_to_authorized_keys(ssh_key.to_key, ssh_key_file_mock)
6767 end
6868
6969 it "removes itself to the authorized keys file" do
7171 ssh_key = new_key
7272 ssh_key_file_mock.should_receive(:new).and_return(ssh_key_file_mock)
7373 ssh_key_file_mock.should_receive(:delete_key).with(ssh_key.to_key).and_return(true)
74 ssh_key.delete_from_authorized_keys(ssh_key_file_mock)
74 SshKey.delete_from_authorized_keys(ssh_key.to_key, ssh_key_file_mock)
7575 end
7676
7777 it "creates a Task on create and update" do
7979 proc{
8080 ssh_key.save!
8181 }.should change(Task, :count)
82 task = Task.find(:first, :conditions => ["target_id = ?", ssh_key.id], :order => "id desc")
82 task = Task.find(:first, :conditions => ["target_class = 'SshKey'"], :order => "id desc")
8383 task.command.should == "add_to_authorized_keys"
84 task.arguments.should == ssh_key.to_key
8485 end
8586
8687 it "creates a Task on destroy" do
8788 ssh_key = new_key
8889 ssh_key.save!
90 keydata = ssh_key.to_key.dup
8991 proc{
9092 ssh_key.destroy
9193 }.should change(Task, :count)
92 task = Task.find(:first, :conditions => ["target_id = ?", ssh_key.id], :order => "id desc")
94 task = Task.find(:first, :conditions => ["target_class = 'SshKey'"], :order => "id desc")
9395 task.command.should == "delete_from_authorized_keys"
96 task.arguments.should == keydata
9497 end
9598end
toggle raw diff

spec/models/task_spec.rb

 
1010 end
1111
1212 it "performs a task" do
13 @task.target.should_receive(@task.command).and_return(true)
13 @task.target_class.constantize.should_receive(@task.command) \
14 .with(@task.arguments).and_return(true)
1415 @task.perform!
1516 @task.reload
1617 @task.performed?.should == true
toggle raw diff

tmp/.gitignore

 
11cache/*
22pids/*
33sessions/*
4sockets/*
4sockets/*
5*lockfile
toggle raw diff