diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..192c301 --- /dev/null +++ b/Gemfile @@ -0,0 +1,8 @@ +source 'https://rubygems.org' + +gem 'test-kitchen', '1.8.0' +gem 'kitchen-docker' +gem 'kitchen-ansible' +gem 'kitchen-verifier-serverspec', git: 'git@github.com:neillturner/kitchen-verifier-serverspec.git' +gem 'net-ssh' +gem 'serverspec' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..84c0164 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,79 @@ +GIT + remote: git@github.com:neillturner/kitchen-verifier-serverspec.git + revision: 4b27a164ade9993b0201c3341b2c636c07ac3966 + specs: + kitchen-verifier-serverspec (0.6.0) + net-ssh (~> 3) + test-kitchen (~> 1.4) + +GEM + remote: https://rubygems.org/ + specs: + artifactory (2.5.0) + diff-lcs (1.2.5) + kitchen-ansible (0.45.4) + net-ssh (~> 3.0) + test-kitchen (~> 1.4) + kitchen-docker (2.6.0) + test-kitchen (>= 1.0.0) + mixlib-install (1.2.3) + artifactory + mixlib-shellout + mixlib-versioning + mixlib-shellout (2.2.7) + mixlib-versioning (1.1.0) + multi_json (1.12.1) + net-scp (1.2.1) + net-ssh (>= 2.6.5) + net-ssh (3.2.0) + net-telnet (0.1.1) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-its (1.2.0) + rspec-core (>= 3.0.0) + rspec-expectations (>= 3.0.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + safe_yaml (1.0.4) + serverspec (2.37.2) + multi_json + rspec (~> 3.0) + rspec-its + specinfra (~> 2.53) + sfl (2.3) + specinfra (2.64.0) + net-scp + net-ssh (>= 2.7, < 4.0) + net-telnet + sfl + test-kitchen (1.8.0) + mixlib-install (~> 1.0, >= 1.0.4) + mixlib-shellout (>= 1.2, < 3.0) + net-scp (~> 1.1) + net-ssh (>= 2.9, < 4.0) + safe_yaml (~> 1.0) + thor (~> 0.18) + thor (0.19.1) + +PLATFORMS + ruby + +DEPENDENCIES + kitchen-ansible + kitchen-docker + kitchen-verifier-serverspec! + net-ssh + serverspec + test-kitchen (= 1.8.0) + +BUNDLED WITH + 1.13.6 diff --git a/README.md b/README.md index 0a1b6dd..8ecf215 100644 --- a/README.md +++ b/README.md @@ -27,15 +27,6 @@ Python [`psycopg2`][psycopg2] module. This can easily be installed via a pre-task in the same play as this role: - hosts: dbservers - pre_tasks: - - name: Install psycopg2 - apt: pkg=python-psycopg2 state=installed - sudo: yes - when: ansible_os_family = 'Debian' - - name: Install psycopg2 - yum: pkg=python-psycopg2 state=installed - sudo: yes - when: ansible_os_family = 'RedHat' roles: - postgresql_objects @@ -72,7 +63,7 @@ Additional variables (`postgresql_objects_login_host`, `postgresql_objects_port`) control how to connect to the database as a user that has privileges to perform the requested changes. However, these can be left unset if you use a system user with administrative privileges in -PostgreSQL, (such as with `sudo: yes` and `sudo_user: postgres` in your play). +PostgreSQL, (such as with `become: yes` and `become_user: postgres` in your play). **`postgresql_objects_groups` caveats**: Managing group membership **does not use Ansible modules** because the existing set of provided modules do not @@ -110,8 +101,8 @@ mechanism to control what database to connect to as the login user): owner: pgadmin roles: - role: postgresql_objects - sudo: yes - sudo_user: postgres + become: yes + become_user: postgres Create a passwordless user (for use with unix domain socket connections) with a database of the same name: @@ -145,8 +136,8 @@ SELECT privileges to `baz` on sequence `bar_quux_seq` in database `foo`: privs: SELECT roles: - role: postgresql_objects - sudo: yes - sudo_user: postgres + become: yes + become_user: postgres Create a group `plugh` and add `foo` and `baz` to this group: @@ -159,8 +150,8 @@ Create a group `plugh` and add `foo` and `baz` to this group: - name: baz roles: - role: postgresql_objects - sudo: yes - sudo_user: postgres + become: yes + become_user: postgres Revoke specific privileges for user `foo`, remove user `baz` from group `plugh`, and delete user `baz`: @@ -195,8 +186,8 @@ Revoke specific privileges for user `foo`, remove user `baz` from group state: absent roles: - role: postgresql_objects - sudo: yes - sudo_user: postgres + become: yes + become_user: postgres Dependencies ------------ diff --git a/defaults/main.yml b/defaults/main.yml index 6186c91..790268d 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -27,3 +27,5 @@ postgresql_objects_users: [] postgresql_objects_groups: [] postgresql_objects_databases: [] postgresql_objects_privileges: [] + +postgresql_obejcts_install_pretasks: true diff --git a/meta/main.yml b/meta/main.yml index 3a9d420..89d48d0 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -4,7 +4,7 @@ galaxy_info: description: "Configure PostgreSQL objects: users (roles), databases, and privileges" company: Penn State University license: AFL v3.0 - min_ansible_version: 1.8 + min_ansible_version: 2.1.2.0 platforms: - name: EL versions: diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..2fbf0ff --- /dev/null +++ b/requirements.yml @@ -0,0 +1 @@ +--- {} diff --git a/tasks/main.yml b/tasks/main.yml index 524254d..35d164a 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,7 +1,11 @@ --- -# tasks file for natefoo.postgresql-objects -- name: Revoke extra privileges +# pre-tasks file for natefoo.postgresql-objects +- include: pretasks.yml + when: postgresql_obejcts_install_pretasks + +# tasks file for natefoo.postgresql-objects +- name: PostgresQL Objects | Revoke extra privileges postgresql_privs: database={{ item.database }} roles={{ item.roles }} type={{ item.type | default( omit ) }} @@ -14,13 +18,13 @@ login_user={{ postgresql_objects_login_user | default( omit ) }} login_password={{ postgresql_objects_login_password | default( omit ) }} port={{ postgresql_objects_port | default( omit ) }} - with_items: postgresql_objects_privileges + with_items: "{{ postgresql_objects_privileges }}" register: revoke failed_when: "postgresql_objects_ignore_revoke_failure and revoke.failed is defined and ((revoke.failed and 'does not exist' not in revoke.msg) or (revoke.failed and ',' in item.roles))" when: item.state is defined and item.state == 'absent' # Drop databases first so later user drop can succeed -- name: Drop databases +- name: PostgresQL Objects | Drop databases postgresql_db: name={{ item.name }} owner={{ item.owner | default( omit ) }} template={{ item.template | default( omit ) }} @@ -32,10 +36,10 @@ login_user={{ postgresql_objects_login_user | default( omit ) }} login_password={{ postgresql_objects_login_password | default( omit ) }} port={{ postgresql_objects_port | default( omit ) }} - with_items: postgresql_objects_databases + with_items: "{{ postgresql_objects_databases }}" when: item.state is defined and item.state == 'absent' -- name: Create and drop users +- name: PostgresQL Objects | Create and drop users postgresql_user: name={{ item.name }} password={{ item.password | default( omit ) }} role_attr_flags={{ item.role_attr_flags | default( omit ) }} @@ -45,9 +49,9 @@ login_user={{ postgresql_objects_login_user | default( omit ) }} login_password={{ postgresql_objects_login_password | default( omit ) }} port={{ postgresql_objects_port | default( omit ) }} - with_items: postgresql_objects_users + with_items: "{{ postgresql_objects_users }}" -- name: Create groups +- name: PostgresQL Objects | Create groups postgresql_user: name={{ item.name }} password={{ item.password | default( omit ) }} role_attr_flags={{ item.role_attr_flags | default( omit ) }} @@ -56,29 +60,29 @@ login_user={{ postgresql_objects_login_user | default( omit ) }} login_password={{ postgresql_objects_login_password | default( omit ) }} port={{ postgresql_objects_port | default( omit ) }} - with_items: postgresql_objects_groups + with_items: "{{ postgresql_objects_groups }}" when: item.state is not defined or (item.state is defined and item.state == 'present') -- name: Add or remove users from groups +- name: PostgresQL Objects | Add or remove users from groups shell: echo {{ 'REVOKE' if item.1.state is defined and item.1.state == 'absent' else 'GRANT' }} '{{ item.0.name }}' {{ 'FROM' if item.1.state is defined and item.1.state == 'absent' else 'TO' }} '{{ item.1.name }}' | psql -w {{ '-h ' ~ postgresql_objects_login_host if postgresql_objects_login_host is defined else '' }} {{ '-U ' ~ postgresql_objects_login_user if postgresql_objects_login_user is defined else '' }} {{ '-p ' ~ postgresql_objects_port if postgresql_objects_port is defined else '' }} postgres register: psql failed_when: psql.stderr.startswith( 'ERROR:' ) changed_when: psql.stderr == '' with_subelements: - - postgresql_objects_groups + - "{{ postgresql_objects_groups }}" - users -- name: Drop groups +- name: PostgresQL Objects | Drop groups postgresql_user: name={{ item.name }} state={{ item.state }} login_host={{ postgresql_objects_login_host | default( omit ) }} login_user={{ postgresql_objects_login_user | default( omit ) }} login_password={{ postgresql_objects_login_password | default( omit ) }} port={{ postgresql_objects_port | default( omit ) }} - with_items: postgresql_objects_groups + with_items: "{{ postgresql_objects_groups }}" when: item.state is defined and item.state == 'absent' -- name: Create databases +- name: PostgresQL Objects | Create databases postgresql_db: name={{ item.name }} owner={{ item.owner | default( omit ) }} template={{ item.template | default( omit ) }} @@ -90,10 +94,10 @@ login_user={{ postgresql_objects_login_user | default( omit ) }} login_password={{ postgresql_objects_login_password | default( omit ) }} port={{ postgresql_objects_port | default( omit ) }} - with_items: postgresql_objects_databases + with_items: "{{ postgresql_objects_databases }}" when: item.state is not defined or (item.state is defined and item.state == 'present') -- name: Grant user database privileges +- name: PostgresQL Objects | Grant user database privileges postgresql_user: name={{ item.name }} db={{ item.db }} priv={{ item.priv }} @@ -101,10 +105,10 @@ login_user={{ postgresql_objects_login_user | default( omit ) }} login_password={{ postgresql_objects_login_password | default( omit ) }} port={{ postgresql_objects_port | default( omit ) }} - with_items: postgresql_objects_users + with_items: "{{ postgresql_objects_users }}" when: item.db is defined and item.priv is defined and not (item.state is defined and item.state == 'absent') -- name: Grant extra privileges +- name: PostgresQL Objects | Grant extra privileges postgresql_privs: database={{ item.database | default( omit ) }} roles={{ item.roles }} type={{ item.type | default( omit ) }} @@ -117,5 +121,5 @@ login_user={{ postgresql_objects_login_user | default( omit ) }} login_password={{ postgresql_objects_login_password | default( omit ) }} port={{ postgresql_objects_port | default( omit ) }} - with_items: postgresql_objects_privileges + with_items: "{{ postgresql_objects_privileges }}" when: (item.state is not defined or (item.state is defined and item.state == 'present')) diff --git a/tasks/pretasks.yml b/tasks/pretasks.yml new file mode 100644 index 0000000..c191863 --- /dev/null +++ b/tasks/pretasks.yml @@ -0,0 +1,10 @@ +--- +# pre-tasks file for natefoo.postgresql-objects +- name: Install psycopg2 + apt: pkg=python-psycopg2 state=installed + become: yes + when: ansible_os_family == 'Debian' +- name: Install psycopg2 + yum: pkg=python-psycopg2 state=installed + become: yes + when: ansible_os_family == 'RedHat' diff --git a/test/integration/default/default.yml b/test/integration/default/default.yml new file mode 100644 index 0000000..f0e3279 --- /dev/null +++ b/test/integration/default/default.yml @@ -0,0 +1,5 @@ +--- +- hosts: test_servers + roles: + - { role: postgresql_objects, + tags: ["postgresql_objects"] } diff --git a/test/integration/default/serverspec/postgresql_objects_spec.rb b/test/integration/default/serverspec/postgresql_objects_spec.rb new file mode 100644 index 0000000..f8ec369 --- /dev/null +++ b/test/integration/default/serverspec/postgresql_objects_spec.rb @@ -0,0 +1 @@ +require 'spec_helper' diff --git a/test/integration/default/serverspec/spec_helper.rb b/test/integration/default/serverspec/spec_helper.rb new file mode 100644 index 0000000..befab12 --- /dev/null +++ b/test/integration/default/serverspec/spec_helper.rb @@ -0,0 +1,15 @@ +require 'serverspec' +require 'net/ssh' + +RSpec.configure do |config| + set :backend, :ssh + set :request_pty, true + set :ssh_options, { + host_name: ENV['KITCHEN_HOSTNAME'], + user: ENV['KITCHEN_USERNAME'], + keys: [ ENV['KITCHEN_SSH_KEY'] ], + port: ENV['KITCHEN_PORT'], + keys_only: true, + paranoid: false + } +end diff --git a/test/integration/inventory b/test/integration/inventory new file mode 100644 index 0000000..431f2e9 --- /dev/null +++ b/test/integration/inventory @@ -0,0 +1,4 @@ +# Kitchen Test Environment + +[test_servers] +test_machine1 ansible_host=10.0.0.2