prodder 1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ Given 'a "$project" git repository' do |project|
2
+ fixture_repo = File.join(@prodder_root, 'features', 'support', "#{project}.git")
3
+ unless File.directory? fixture_repo
4
+ raise "Cannot initialize repo for project #{project}; expected fixture at: #{fixture_repo}"
5
+ end
6
+
7
+ run_simple "mkdir -p repos"
8
+ run_simple "chmod -R a+w repos/#{project}.git" if File.exist? File.join(current_dir, "repos", "#{project}.git")
9
+ remove_dir "repos/#{project}.git"
10
+ run_simple "cp -pR #{fixture_repo} repos/#{project}.git"
11
+ end
12
+
13
+ Given 'I deleted the "$project" git repository' do |project|
14
+ run_simple "rm -rf repos/#{project}.git"
15
+ end
16
+
17
+ Given 'the "$project" git repository does not allow pushing to it' do |project|
18
+ run_simple "chmod -R a-w repos/#{project}.git"
19
+ end
20
+
21
+ Given 'a new commit is already in the "$project" git repository' do |project|
22
+ commit_to_remote project
23
+ end
24
+
25
+ Then 'the new commit should be in the workspace copy of the "$project" repository' do |project|
26
+ check_file_content "prodder-workspace/#{project}/README", 'Also read this!', true
27
+ end
28
+
29
+ Then(/^(\d+) commits? by "([^"]+)" should be in the "([^"]+)" repository$/) do |n, author, project|
30
+ in_workspace(project) do
31
+ authors = `git log --pretty='format:%an'`.split("\n")
32
+ authors.grep(/#{author}/).size.should == Integer(n)
33
+ end
34
+ end
35
+
36
+ Then 'the file "$filename" should now be tracked' do |filename|
37
+ in_current_dir do
38
+ git = Prodder::Git.new(File.expand_path("prodder-workspace/blog"), nil)
39
+ git.should be_tracked(filename)
40
+ end
41
+ end
42
+
43
+ Then 'the latest commit should have changed "$file" to contain "$content"' do |filename, content|
44
+ in_workspace('blog') do
45
+ changed = `git show --name-only HEAD | grep #{filename}`.split("\n")
46
+ changed.should_not be_empty
47
+
48
+ diff = `git show HEAD | grep '#{content}'`.split("\n")
49
+ diff.should_not be_empty
50
+ end
51
+ end
52
+
53
+ Then 'the latest commit should not have changed "$filename"' do |filename|
54
+ in_workspace('blog') do
55
+ changed = `git show --name-only HEAD | grep #{filename}`.split("\n")
56
+ changed.should be_empty
57
+ end
58
+ end
59
+
60
+ Then 'the new commit should be in the remote repository' do
61
+ in_current_dir do
62
+ latest = `git --git-dir="./repos/blog.git" log | grep prodder`.split("\n")
63
+ latest.should_not be_empty
64
+ end
65
+ end
@@ -0,0 +1,150 @@
1
+ Given 'the "store/db/name" key is missing from "$filename"' do |filename|
2
+ # Eh, good enough!
3
+ path = File.join @aruba_root, filename
4
+ contents = File.read path
5
+ File.open(path, 'w') { |f| f.write contents.sub(/^\s+name: \w+$/, '') }
6
+ end
7
+
8
+ Given 'the "$role" role can not read from the "blog" database\'s tables' do |role|
9
+ Prodder::PG.new.psql('prodder__blog_prod', 'REVOKE SELECT ON ALL TABLES IN SCHEMA public FROM prodder;')
10
+ end
11
+
12
+ Given 'I add an index to table "$table" on column "$column" in the "$project" project\'s database' do |table, column, project|
13
+ Prodder::PG.new.psql "prodder__#{project}_prod", "CREATE INDEX test_index ON #{table} (#{column});"
14
+ end
15
+
16
+ Given 'I add a customer parameter "$parameter" with value "$value" in the "$project" project\'s database' do |parameter, value, project|
17
+ Prodder::PG.new.psql "prodder__#{project}_prod", "ALTER DATABASE prodder__#{project}_prod SET #{parameter} = #{value};"
18
+ end
19
+
20
+ Given 'I add a foreign key from table "$table1" and column "$column1" to table "$table2" and column "$column2" in the "$project" project\'s database' do |table1, column1, table2, column2, project|
21
+ Prodder::PG.new.psql "prodder__#{project}_prod", "ALTER TABLE #{table1} ADD CONSTRAINT fk_authors FOREIGN KEY (#{column1}) REFERENCES #{table2} (#{column2});"
22
+ end
23
+
24
+ Given 'no-op versions of these bins are available on my PATH: $bins' do |bins|
25
+ paths = bins.split(/,\s*/).map { |bin|
26
+ File.join(@aruba_root, "stub-#{bin}").tap do |dir|
27
+ FileUtils.mkdir_p dir
28
+ File.open(File.join(dir, bin), 'w') { |f| f.write 'foo' }
29
+ end
30
+ }.join(File::PATH_SEPARATOR)
31
+
32
+ set_env 'PATH', "#{paths}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
33
+ end
34
+
35
+ Given '"$bin" is not available on my PATH' do |bin|
36
+ path = ENV['PATH'].split(File::PATH_SEPARATOR)
37
+ dirs = path.select { |dir| File.exist? File.join(dir, bin) }
38
+ set_env 'PATH', path.reject { |dir| dirs.include?(dir) }.join(File::PATH_SEPARATOR)
39
+ end
40
+
41
+ When 'I create a new table "$table" in the "$project" database' do |table, project|
42
+ pg = Prodder::PG.new
43
+ pg.psql "prodder__#{project}_prod", "CREATE TABLE #{table} ( id SERIAL PRIMARY KEY );"
44
+ end
45
+
46
+ When 'I add a new author "$author" to the "$project" database' do |author, project|
47
+ pg = Prodder::PG.new
48
+ pg.psql "prodder__#{project}_prod", "INSERT INTO authors (name) VALUES ('#{author}');"
49
+ end
50
+
51
+ When 'I add a "$name" schema to the "$project" project\'s database' do |name, project|
52
+ pg = Prodder::PG.new
53
+ pg.psql "prodder__#{project}_prod", "CREATE SCHEMA #{name} AUTHORIZATION prodder CREATE TABLE #{name}.providers ( id SERIAL PRIMARY KEY );"
54
+ end
55
+
56
+ When 'I grant all permissions on table "$table" in the "$project" database to "$role"' do |table, project, role|
57
+ pg = Prodder::PG.new
58
+ pg.psql "prodder__#{project}_prod", "GRANT ALL ON #{table} TO #{role}"
59
+ end
60
+
61
+ Then 'the output should contain the example config contents' do
62
+ assert_partial_output Prodder::Config.example_contents, all_output
63
+ end
64
+
65
+ Then /^the workspace file "([^"]*)" should match \/([^\/]*)\/$/ do |file, partial_content|
66
+ check_file_content("prodder-workspace/#{file}", /#{partial_content}/, true)
67
+ end
68
+
69
+ Then /^the workspace file "([^"]*)" should not match \/([^\/]*)\/$/ do |file, partial_content|
70
+ check_file_content("prodder-workspace/#{file}", /#{partial_content}/, false)
71
+ end
72
+
73
+ Then /^the workspace file "([^"]*)" should not exist$/ do |file|
74
+ check_file_presence(["prodder-workspace/#{file}"], false)
75
+ end
76
+
77
+ Given(/a prodder config in "([^"]*)" with projects?: (.*)/) do |filename, projects|
78
+ contents = projects.split(/,\s*/).map { |name|
79
+ strip_leading <<-EOF
80
+ #{name}:
81
+ structure_file: db/structure.sql
82
+ seed_file: db/seeds.sql
83
+ quality_check_file: db/quality_checks.sql
84
+ permissions:
85
+ file: db/permissions.sql
86
+ included_users: prodder, include_this
87
+ git:
88
+ origin: ./repos/#{name}.git
89
+ author: prodder auto-commit <pd+prodder@krh.me>
90
+ db:
91
+ name: prodder__#{name}_prod
92
+ host: localhost
93
+ user: prodder
94
+ tables:
95
+ - posts
96
+ - authors
97
+ EOF
98
+ }.join("\n")
99
+
100
+ write_file filename, contents
101
+ end
102
+
103
+ Given 'the "$project" file "$filename" contains:' do |project, filename, contents|
104
+ write_file "prodder-workspace/#{project}/#{filename}", contents
105
+ end
106
+
107
+ Given 'the prodder config in "$filename" says to read the "$project" seed tables from "$seeds"' do |filename, project, seeds|
108
+ update_config filename do |config|
109
+ config[project]['db']['tables'] = seeds
110
+ end
111
+ end
112
+
113
+ Given 'the prodder config in "$filename" excludes the table "$table" from the dump of "$project"' do |filename, table, project|
114
+ update_config filename do |config|
115
+ config[project]['db']['exclude_tables'] ||= []
116
+ config[project]['db']['exclude_tables'].push table
117
+ end
118
+ end
119
+
120
+ Given 'the prodder config in "$filename" excludes the schema "$schema" from the dump of "$project"' do |filename, schema, project|
121
+ update_config filename do |config|
122
+ config[project]['db']['exclude_schemas'] ||= []
123
+ config[project]['db']['exclude_schemas'].push schema
124
+ end
125
+ end
126
+
127
+ Given 'the prodder config in "$filename" does not include a quality check file for the "$project" project' do |filename, project|
128
+ update_config filename do |config|
129
+ config[project].delete 'quality_check_file'
130
+ end
131
+ end
132
+
133
+ Given 'the prodder config in "$filename" does not include a permissions file for the "$project" project' do |filename, project|
134
+ update_config filename do |config|
135
+ config[project]['permissions'].delete 'file'
136
+ end
137
+ end
138
+
139
+ Given 'the prodder config in "$filename" does not include permissions for the "$project" project' do |filename, project|
140
+ update_config filename do |config|
141
+ config[project].delete 'permissions'
142
+ end
143
+ end
144
+
145
+ Given 'the "$project" file "$filename" does not exist' do |project, filename|
146
+ begin
147
+ remove_file "prodder-workspace/#{project}/#{filename}"
148
+ rescue Errno::ENOENT
149
+ end
150
+ end
@@ -0,0 +1,116 @@
1
+ require 'cucumber'
2
+ require 'aruba/cucumber'
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../../lib', File.dirname(__FILE__))
5
+ require 'prodder'
6
+
7
+ module ProdderHelpers
8
+ def strip_leading(string)
9
+ leading = string.scan(/^\s*/).min_by &:length
10
+ string.gsub /^#{leading}/, ''
11
+ end
12
+
13
+ def in_workspace(name, &block)
14
+ in_current_dir { Dir.chdir("prodder-workspace/#{name}", &block) }
15
+ end
16
+
17
+ def commit_to_remote(project)
18
+ @dirs = ['tmp', 'aruba']
19
+ run_simple "git clone repos/#{project}.git tmp-extra-commit-#{project}"
20
+ cd "tmp-extra-commit-#{project}"
21
+
22
+ append_to_file "README", 'Also read this!'
23
+ run_simple 'git add README'
24
+ run_simple 'git commit -m "Second commit"'
25
+ run_simple "git push origin master"
26
+
27
+ cd '..'
28
+ run_simple "rm -rf tmp-extra-commit-#{project}"
29
+ end
30
+
31
+ def update_config(filename, &block)
32
+ config = with_file_content(filename) do |contents|
33
+ config = YAML.load contents
34
+ block.call(config)
35
+ config
36
+ end
37
+
38
+ write_file filename, config.to_yaml
39
+ end
40
+
41
+ def self.setup!
42
+ pg = Prodder::PG.new
43
+ pg.create_role 'prodder'
44
+ pg.create_role 'include_this', ['--no-login']
45
+ pg.create_role 'exclude_this'
46
+ pg.create_role 'prodder__blog_prod:read_only', ['--no-login']
47
+ pg.create_role 'prodder__blog_prod:read_write', ['--no-login']
48
+ pg.create_role 'prodder__blog_prod:permissions_test:read_only', ['--no-login']
49
+ pg.create_role 'prodder__blog_prod:permissions_test:read_write', ['--no-login']
50
+ pg.create_role '_90enva', ['--no-login']
51
+ pg.create_role '_91se', ['--no-login']
52
+ pg.create_role '_91qa', ['--no-login']
53
+ pg.create_role '_91b', ['--no-login']
54
+ pg.create_role '_92b', ['--no-login']
55
+ pg.create_role '_92se', ['--no-login']
56
+ pg.create_role '_92qa', ['--no-login']
57
+ pg.create_role '_93se', ['--no-login']
58
+ pg.create_role '_93b', ['--no-login']
59
+ pg.create_role '_94se', ['--no-login']
60
+
61
+ fixture_dbs.each do |name, contents|
62
+ pg.create_db name
63
+ pg.psql name, "ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO prodder;"
64
+ pg.psql name, "ALTER DEFAULT PRIVILEGES GRANT SELECT ON SEQUENCES TO prodder;"
65
+ pg.psql name, contents
66
+ end
67
+ end
68
+
69
+ def self.teardown!
70
+ pg = Prodder::PG.new
71
+ fixture_dbs.each { |name, contents| pg.drop_db name }
72
+ pg.drop_role 'prodder'
73
+ pg.drop_role 'include_this'
74
+ pg.drop_role 'exclude_this'
75
+ pg.drop_role "prodder__blog_prod:read_only"
76
+ pg.drop_role "prodder__blog_prod:read_write"
77
+ pg.drop_role "prodder__blog_prod:permissions_test:read_only"
78
+ pg.drop_role "prodder__blog_prod:permissions_test:read_write"
79
+ pg.drop_role '_90enva'
80
+ pg.drop_role '_91se'
81
+ pg.drop_role '_91qa'
82
+ pg.drop_role '_91b'
83
+ pg.drop_role '_92se'
84
+ pg.drop_role '_92b'
85
+ pg.drop_role '_92qa'
86
+ pg.drop_role '_93se'
87
+ pg.drop_role '_93b'
88
+ pg.drop_role '_94se'
89
+
90
+ end
91
+
92
+ def self.fixture_dbs
93
+ @fixture_dbs ||= Dir[File.join(File.dirname(__FILE__), '*.sql')].map do |sql|
94
+ db = File.basename(sql).sub('.sql', '')
95
+ [db, File.read(sql)]
96
+ end
97
+ end
98
+ end
99
+
100
+ World ProdderHelpers
101
+
102
+ Before do
103
+ @prodder_root = File.expand_path('../..', File.dirname(__FILE__))
104
+ @aruba_root = File.join(@prodder_root, 'tmp', 'aruba')
105
+ @aruba_timeout_seconds = 10
106
+ Dir.chdir @prodder_root
107
+ end
108
+
109
+ After('@restore-perms') do
110
+ pg = Prodder::PG.new
111
+ pg.psql 'prodder__blog_prod', "GRANT SELECT ON ALL TABLES IN SCHEMA public TO prodder;"
112
+ end
113
+
114
+ ## Bootstrap the test role, databases, git repos.
115
+ ProdderHelpers.setup!
116
+ at_exit { ProdderHelpers.teardown! }
@@ -0,0 +1,153 @@
1
+ ALTER DATABASE prodder__blog_prod SET custom.parameter = 1;
2
+
3
+ CREATE TABLE authors (
4
+ author_id serial primary key,
5
+ name text
6
+ );
7
+
8
+ CREATE TABLE posts (
9
+ post_id serial primary key,
10
+ author_id integer,
11
+ body text
12
+ );
13
+
14
+ CREATE TABLE comments (
15
+ comment_id serial primary key,
16
+ post_id integer,
17
+ author_id integer,
18
+ body text
19
+ );
20
+
21
+ INSERT INTO authors (name) VALUES ('Kyle');
22
+ INSERT INTO authors (name) VALUES ('Josh');
23
+
24
+ INSERT INTO posts (author_id, body) VALUES (1, 'Thoughts');
25
+ INSERT INTO posts (author_id, body) VALUES (2, 'Other thoughts');
26
+
27
+ INSERT INTO comments (post_id, author_id, body) VALUES (1, 2, 'I agree');
28
+
29
+ CREATE FUNCTION test () RETURNS TRIGGER LANGUAGE PLPGSQL AS $$
30
+ BEGIN
31
+ NEW.body='test function trigger collusion';
32
+ RETURN NEW;
33
+ END;
34
+ $$
35
+ ;
36
+
37
+ CREATE TRIGGER test_comments BEFORE UPDATE OR INSERT ON comments FOR EACH ROW EXECUTE PROCEDURE test();
38
+
39
+ CREATE OR REPLACE FUNCTION create_role_if_not_exists(rolename VARCHAR)
40
+ RETURNS VOID
41
+ AS
42
+ $create_role_if_not_exists$
43
+ DECLARE
44
+ BEGIN
45
+ IF NOT EXISTS (
46
+ SELECT *
47
+ FROM pg_catalog.pg_roles
48
+ WHERE rolname = rolename) THEN
49
+ EXECUTE 'CREATE ROLE ' || quote_ident(rolename) || ' ;';
50
+ END IF;
51
+ END;
52
+ $create_role_if_not_exists$
53
+ LANGUAGE PLPGSQL;
54
+
55
+ SELECT create_role_if_not_exists('_90enva');
56
+ SELECT create_role_if_not_exists('_91se');
57
+ SELECT create_role_if_not_exists('_91qa');
58
+ SELECT create_role_if_not_exists('_91b');
59
+ SELECT create_role_if_not_exists('_92se');
60
+ SELECT create_role_if_not_exists('_92qa');
61
+ SELECT create_role_if_not_exists('_92b');
62
+ SELECT create_role_if_not_exists('_93se');
63
+ SELECT create_role_if_not_exists('_93qa');
64
+ SELECT create_role_if_not_exists('_93b');
65
+ SELECT create_role_if_not_exists('_94se');
66
+ SELECT create_role_if_not_exists('_94qa');
67
+ SELECT create_role_if_not_exists('_94b');
68
+ SELECT create_role_if_not_exists('include_this');
69
+ SELECT create_role_if_not_exists('exclude_this');
70
+ SELECT create_role_if_not_exists('prodder__blog_prod:permissions_test:read_write');
71
+ SELECT create_role_if_not_exists('prodder__blog_prod:permissions_test:read_only');
72
+ SELECT create_role_if_not_exists('prodder__blog_prod:read_write');
73
+ SELECT create_role_if_not_exists('prodder__blog_prod:read_only');
74
+
75
+ GRANT "_90enva" TO "_91se";
76
+ GRANT "_90enva" TO "_91qa";
77
+ GRANT "_90enva" TO "_91b";
78
+
79
+ GRANT "_91se" TO "_92se";
80
+ GRANT "_91qa" TO "_92se";
81
+ GRANT "_91qa" TO "_92qa";
82
+ GRANT "_91b" TO "_92qa";
83
+ GRANT "_91qa" TO "_94se";
84
+
85
+ GRANT "_92se" TO "_93se";
86
+ GRANT "_93se" TO "_94se";
87
+
88
+ GRANT "_92b" TO "_93b";
89
+
90
+ GRANT "prodder__blog_prod:permissions_test:read_write" TO "_92se";
91
+
92
+ GRANT "prodder__blog_prod:permissions_test:read_write" TO include_this;
93
+ GRANT "prodder__blog_prod:permissions_test:read_only" TO exclude_this;
94
+
95
+ GRANT "prodder__blog_prod:permissions_test:read_only" TO "prodder__blog_prod:read_only";
96
+ GRANT "prodder__blog_prod:permissions_test:read_write" TO "prodder__blog_prod:read_write";
97
+ ALTER ROLE "include_this" VALID UNTIL '2222-08-11 00:00:00-05';
98
+
99
+ GRANT "prodder__blog_prod:read_only" TO prodder;
100
+
101
+ CREATE SCHEMA permissions_test;
102
+ GRANT USAGE ON SCHEMA permissions_test TO include_this;
103
+ GRANT USAGE ON SCHEMA permissions_test TO exclude_this;
104
+ GRANT USAGE ON SCHEMA permissions_test TO "prodder__blog_prod:permissions_test:read_only";
105
+ GRANT USAGE ON SCHEMA permissions_test TO "prodder__blog_prod:permissions_test:read_write";
106
+
107
+ GRANT SELECT, USAGE, UPDATE ON ALL SEQUENCES IN SCHEMA permissions_test TO include_this;
108
+ GRANT SELECT, USAGE, UPDATE ON ALL SEQUENCES IN SCHEMA permissions_test TO exclude_this;
109
+ GRANT SELECT, USAGE, UPDATE ON ALL SEQUENCES IN SCHEMA permissions_test TO "prodder__blog_prod:permissions_test:read_only";
110
+ GRANT SELECT, USAGE, UPDATE ON ALL SEQUENCES IN SCHEMA permissions_test TO "prodder__blog_prod:permissions_test:read_write";
111
+
112
+ CREATE TABLE permissions_test.standard_acl (
113
+ standard_acl_id serial primary key,
114
+ value text
115
+ );
116
+
117
+ REVOKE ALL ON permissions_test.standard_acl FROM PUBLIC;
118
+ REVOKE ALL ON permissions_test.standard_acl FROM include_this;
119
+ REVOKE ALL ON permissions_test.standard_acl FROM exclude_this;
120
+ REVOKE ALL ON permissions_test.standard_acl FROM "prodder__blog_prod:permissions_test:read_only";
121
+ REVOKE ALL ON permissions_test.standard_acl FROM "prodder__blog_prod:permissions_test:read_write";
122
+ GRANT SELECT ON permissions_test.standard_acl TO "prodder__blog_prod:permissions_test:read_only";
123
+ GRANT SELECT, INSERT, UPDATE, DELETE ON permissions_test.standard_acl TO "prodder__blog_prod:permissions_test:read_write";
124
+
125
+ CREATE TABLE permissions_test.column_acl (
126
+ column_acl_id serial primary key,
127
+ non_acl_column integer,
128
+ acl_column integer
129
+ );
130
+
131
+ REVOKE ALL ON permissions_test.column_acl FROM PUBLIC;
132
+ REVOKE ALL ON permissions_test.column_acl FROM include_this;
133
+ REVOKE ALL ON permissions_test.column_acl FROM exclude_this;
134
+ REVOKE ALL ON permissions_test.column_acl FROM "prodder__blog_prod:permissions_test:read_only";
135
+ REVOKE ALL ON permissions_test.column_acl FROM "prodder__blog_prod:permissions_test:read_write";
136
+ GRANT SELECT (acl_column) ON permissions_test.column_acl TO "prodder__blog_prod:permissions_test:read_only";
137
+ GRANT SELECT,INSERT, UPDATE (acl_column) ON permissions_test.column_acl TO "prodder__blog_prod:permissions_test:read_write";
138
+
139
+ CREATE OR REPLACE FUNCTION permissions_test.does_absolutely_nothing()
140
+ RETURNS VOID
141
+ AS
142
+ $$
143
+ BEGIN
144
+ PERFORM 'SELECT 1';
145
+ END;
146
+ $$
147
+ SECURITY DEFINER
148
+ LANGUAGE PLPGSQL;
149
+
150
+ ALTER DEFAULT PRIVILEGES FOR ROLE prodder IN SCHEMA permissions_test GRANT SELECT ON TABLES TO "prodder__blog_prod:permissions_test:read_only";
151
+ ALTER DEFAULT PRIVILEGES FOR ROLE prodder IN SCHEMA permissions_test GRANT SELECT, USAGE ON SEQUENCES TO "prodder__blog_prod:permissions_test:read_only";
152
+ ALTER DEFAULT PRIVILEGES FOR ROLE prodder IN SCHEMA permissions_test GRANT SELECT, UPDATE, INSERT, DELETE ON TABLES TO "prodder__blog_prod:permissions_test:read_write";
153
+ ALTER DEFAULT PRIVILEGES FOR ROLE prodder IN SCHEMA permissions_test GRANT SELECT, UPDATE, USAGE ON SEQUENCES TO "prodder__blog_prod:permissions_test:read_write";