prodder 1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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";