diff --git a/playbooks/irods_server.yml b/playbooks/irods_server.yml index a98088d7..a285db67 100644 --- a/playbooks/irods_server.yml +++ b/playbooks/irods_server.yml @@ -6,4 +6,6 @@ # irods server will use a local postgresql database roles: - - irods_server + - role: irods_server + vars: + irods_server_host: "{{ workspace_fqdn | default(ansible_fqdn) }}" diff --git a/playbooks/roles/irods_server/defaults/main.yml b/playbooks/roles/irods_server/defaults/main.yml index 1f72a1c6..ceee60f2 100644 --- a/playbooks/roles/irods_server/defaults/main.yml +++ b/playbooks/roles/irods_server/defaults/main.yml @@ -1,12 +1,17 @@ --- -# Note: db_name must be lowercase +irods_server_version: "" irods_server_admin_password: rods irods_server_zone: tempZone irods_server_host: "{{ ansible_fqdn }}" -irods_server_db_name: icat +irods_server_db_name: icat # must be lowercase irods_server_db_username: irods irods_server_db_password: db{{ irods_server_admin_password }} irods_server_db_host: localhost irods_server_db_port: 5432 -irods_server_db_driver: "PostgreSQL ANSI" irods_server_start: start +irods_server_core_re: false # set to a string to override default core rules +irods_server_hooks_files: + hooks: "{{ irods_server_hooks_training }}" +irods_server_default_rsc_name: trainingResc +irods_server_use_external_storage: true +irods_server_python_plugin: false diff --git a/playbooks/roles/irods_server/files/hooks_training.re b/playbooks/roles/irods_server/files/hooks_training.re new file mode 100644 index 00000000..a7d13bfe --- /dev/null +++ b/playbooks/roles/irods_server/files/hooks_training.re @@ -0,0 +1,34 @@ +acPostProcForPut{ + ON($objPath like "/$rodsZoneClient/home/$userNameClient/event/*"){ + msiWriteRodsLog("LOGGING: object", *Status); + msiWriteRodsLog("$objPath triggered event hook", *Status); + msiAddKeyVal(*Keyval,"TRIGGER","acPostProcForPut"); + msiGetObjType($objPath, *objType); + msiAssociateKeyValuePairsToObj(*Keyval,$objPath,*objType); + msiSetACL("default", "read", $userNameClient, $objPath); + msiWriteRodsLog("LOGGING END", *Status); + } +} + +acPostProcForPut{ + ON($objPath like "*/santa.txt"){ + msiWriteRodsLog("$objPath triggered event hook", *Status); + msiGetObjType($objPath, *objType); + msiAddKeyVal(*Keyval,"Santa says","Merry Christmas!"); + msiAssociateKeyValuePairsToObj(*Keyval,$objPath,*objType); + } +} + +acPostProcForPut { } + +acPostProcForCollCreate{ + ON($collName like "/$rodsZoneClient/home/$userNameClient/event/*"){ + msiWriteRodsLog("LOGGING: Collection", *Status); + msiWriteRodsLog("$collName triggered event hook", *Status); + msiAddKeyVal(*Keyval,"TRIGGER","acPostProcForCollCreate"); + msiAssociateKeyValuePairsToObj(*Keyval,$collName,"-C"); + msiWriteRodsLog("LOGGING END", *Status); + } +} + +acPostProcForCollCreate { } diff --git a/playbooks/roles/irods_server/handlers/main.yml b/playbooks/roles/irods_server/handlers/main.yml new file mode 100644 index 00000000..25b52f51 --- /dev/null +++ b/playbooks/roles/irods_server/handlers/main.yml @@ -0,0 +1,12 @@ +--- +- name: Reload systemd service + ansible.builtin.systemd: + daemon_reload: true + +- name: Restart irods + ansible.builtin.service: + enabled: true + name: irods + state: restarted + failed_when: false + register: restarted_irods diff --git a/playbooks/roles/irods_server/molecule/default/converge.yml b/playbooks/roles/irods_server/molecule/default/converge.yml new file mode 100644 index 00000000..3b655123 --- /dev/null +++ b/playbooks/roles/irods_server/molecule/default/converge.yml @@ -0,0 +1,6 @@ +--- +- name: Converge + hosts: all + gather_facts: true + roles: + - role: irods_server diff --git a/playbooks/roles/irods_server/molecule/default/molecule.yml b/playbooks/roles/irods_server/molecule/default/molecule.yml new file mode 100644 index 00000000..50356e70 --- /dev/null +++ b/playbooks/roles/irods_server/molecule/default/molecule.yml @@ -0,0 +1,20 @@ +--- +platforms: + - name: workspace-src-ubuntu_jammy + image: ghcr.io/utrechtuniversity/src-test-workspace:ubuntu_jammy + command: /sbin/init + pre_build_image: true + privileged: true + registry: + url: $DOCKER_REGISTRY + credentials: + username: $DOCKER_USER + password: $DOCKER_PW +provisioner: + name: ansible + playbooks: + converge: ./converge.yml + prepare: ./prepare.yml + env: + ANSIBLE_ROLES_PATH: ../../../ +role_name_check: 1 diff --git a/playbooks/roles/irods_server/molecule/default/prepare.yml b/playbooks/roles/irods_server/molecule/default/prepare.yml new file mode 100644 index 00000000..0f813bbd --- /dev/null +++ b/playbooks/roles/irods_server/molecule/default/prepare.yml @@ -0,0 +1,11 @@ +--- +- name: Prepare + hosts: all + gather_facts: true + tasks: + # Apt cache is normally updated at deploy time by the SRC-OS component. + # Make sure it is fresh so our tests use recent apt repo information. + - name: Update apt cache + ansible.builtin.apt: + update_cache: true + when: ansible_os_family == 'Debian' diff --git a/playbooks/roles/irods_server/tasks/main.yml b/playbooks/roles/irods_server/tasks/main.yml index 492aa8e6..dfa2d839 100644 --- a/playbooks/roles/irods_server/tasks/main.yml +++ b/playbooks/roles/irods_server/tasks/main.yml @@ -1,4 +1,12 @@ --- +- name: Debug - write fqdn to file for inspection + ansible.builtin.copy: + dest: /root/irods_debug.tx + content: | + workspace_fqdn: {{ workspace_fqdn }} + ansible_fqdn: {{ ansible_fqdn }} + irods_server_host: {{ irods_server_host }} + - name: Install prerequisite packages when: ansible_pkg_mgr == 'yum' or ansible_pkg_mgr == 'dnf' ansible.builtin.package: @@ -9,28 +17,36 @@ ansible.builtin.package: state: present name: - - irods-server - - irods-database-plugin-postgres - - irods-rule-engine-plugin-python - - irods-icommands + - irods-server{% if irods_server_version %}={{ irods_server_version }}{% endif %} + - irods-database-plugin-postgres{% if irods_server_version %}={{ irods_server_version }}{% endif %} + - irods-icommands{% if irods_server_version %}={{ irods_server_version }}{% endif %} + - python3-distro # needed for the setup_irods.py script + +- name: Install iRODS python rules plugin + when: irods_server_python_plugin + ansible.builtin.package: + state: present + name: + - irods-rule-engine-plugin-python{% if irods_server_version %}={{ irods_server_version }}{% endif %} + +- name: Get installed packages + ansible.builtin.package_facts: + +- name: Get irods major version + ansible.builtin.set_fact: + irods_server_version: "{{ ansible_facts.packages['irods-server'][0]['version'] | split('.') | list | first }}" - name: Prepare systemd unit file for irods + notify: + - Reload systemd service + - Restart irods ansible.builtin.template: - src: irods.service.j2 + src: irods.service_v{{ irods_server_version }}.j2 dest: /lib/systemd/system/irods.service owner: root group: root mode: "0644" -- name: Reload systemd daemon - ansible.builtin.systemd: - daemon_reload: true - -- name: Configure ODBC driver for Rocky 9+ - ansible.builtin.set_fact: - irods_server_db_driver: "PostgreSQL" - when: ansible_pkg_mgr == 'dnf' - - name: Create ICAT database and db user and password protect PostgreSQL loopback access ansible.builtin.include_tasks: icat.yml args: @@ -43,63 +59,70 @@ name: postgresql state: restarted -- name: Register if iRODS server is initialized. - ansible.builtin.stat: - path: /etc/irods/server_config.json - register: irods_config_data - -- name: Register FQDN (hack needed because hostname FQDN incomplete during workspace deployment) - ansible.builtin.set_fact: - irods_server_host: "{{ workspace_fqdn }}" - when: workspace_fqdn is defined - -# below update of hostname ensures that demoResc iRODS resource will have proper host attribute -- name: Ensure hostname is FQDN (hack needed because hostname FQDN incomplete) - ansible.builtin.command: hostname "{{ workspace_fqdn }}" - when: workspace_fqdn is defined - - name: Generate zone key ansible.builtin.command: openssl rand -hex 16 register: irods_server_zone_key + changed_when: false - name: Generate negotiation key ansible.builtin.command: openssl rand -hex 16 register: irods_server_negotiation_key + changed_when: false - name: Generate control plane key ansible.builtin.command: openssl rand -hex 16 register: irods_server_control_plane_key + changed_when: false - name: Prepare iRODS server configuration file - when: not irods_config_data.stat.exists ansible.builtin.template: - src: server_unattended_config.json.j2 + src: server_unattended_config_v{{ irods_server_version }}.json.j2 dest: /etc/irods/server_unattended_config.json owner: root group: root mode: "0600" + register: restart_triggered -- name: Bypass hostname check in iRODS setup (hack needed for irods 4.3.3 because workspace FQDN != localhost) - ansible.builtin.lineinfile: - path: /var/lib/irods/scripts/setup_irods.py - insertafter: ^def check_hostname - state: present - line: " return" +- name: Configure ruleset + notify: Restart irods + when: irods_server_core_re or irods_server_core_re == "" + ansible.builtin.copy: + content: "{{ irods_server_core_re }}" + dest: /etc/irods/core.re + mode: "0644" + force: false + register: restart_triggered -- name: Configure empty python ruleset core.py +- name: Configure hooks + notify: Restart irods + loop: "{{ irods_server_hooks_files | dict2items }}" ansible.builtin.copy: - content: "" - dest: /etc/irods/core.py + content: "{{ item.value }}" + dest: /etc/irods/{{ item.key }}.re mode: "0644" force: false + register: restart_triggered - name: Run irods configuration script - when: irods_server_start == "start" - ansible.builtin.command: python3 /var/lib/irods/scripts/setup_irods.py --json_configuration_file /etc/irods/server_unattended_config.json + when: restart_triggered.changed + notify: Restart irods + ansible.builtin.command: /var/lib/irods/scripts/setup_irods.py -v --json_configuration_file /etc/irods/server_unattended_config.json + failed_when: false + register: restart_triggered + +- name: Debug config script output + ansible.builtin.debug: + msg: "{{ restart_triggered.stdout }}" + +- name: Kill test server # debug + ansible.builtin.shell: killall -r "irods" + failed_when: false - name: Start and enable iRODS as service - when: irods_server_start == "start" + failed_when: false ansible.builtin.service: enabled: true name: irods state: started + failed_when: false + when: restart_triggered is not defined # ensure the service is in state started, even if the *re*-start handler was not diff --git a/playbooks/roles/irods_server/templates/irods.service.j2 b/playbooks/roles/irods_server/templates/irods.service_v4.j2 similarity index 100% rename from playbooks/roles/irods_server/templates/irods.service.j2 rename to playbooks/roles/irods_server/templates/irods.service_v4.j2 diff --git a/playbooks/roles/irods_server/templates/irods.service_v5.j2 b/playbooks/roles/irods_server/templates/irods.service_v5.j2 new file mode 100644 index 00000000..fc1470db --- /dev/null +++ b/playbooks/roles/irods_server/templates/irods.service_v5.j2 @@ -0,0 +1,17 @@ +[Unit] +Description=iRODS +After=network.target + +[Service] +Type=notify +ExecStart=/usr/sbin/irodsServer +ExecReload=/bin/kill -HUP $MAINPID +KillMode=mixed +Restart=on-failure +User=irods +Group=irods +WorkingDirectory=/var/lib/irods +LimitNOFILE=1048576 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/playbooks/roles/irods_server/templates/server_unattended_config.json.j2 b/playbooks/roles/irods_server/templates/server_unattended_config.json.j2 index 28aa8f17..b89c841e 100644 --- a/playbooks/roles/irods_server/templates/server_unattended_config.json.j2 +++ b/playbooks/roles/irods_server/templates/server_unattended_config.json.j2 @@ -1,7 +1,7 @@ { "admin_password": "{{ irods_server_admin_password }}", - "default_resource_directory": "/var/lib/irods/Vault", - "default_resource_name": "demoResc", + "default_resource_directory": "{{ irods_server_default_rsc_path }}", + "default_resource_name": "{{ irods_server_default_rsc_name }}", "host_system_information": { "service_account_user_name": "irods", "service_account_group_name": "irods" @@ -35,6 +35,7 @@ }, "server_config": { "advanced_settings": { + "checksum_read_buffer_size_in_bytes": 1048576, "default_log_rotation_in_days": 5, "default_number_of_transfer_threads": 4, "default_temporary_password_lifetime_in_seconds": 120, @@ -44,15 +45,18 @@ "delay_server_sleep_time_in_seconds": 30, "dns_cache": { "eviction_age_in_seconds": 3600, - "shared_memory_size_in_bytes": 5000000 + "shared_memory_size_in_bytes": 5000000, + "cache_clearer_sleep_time_in_seconds": 600 }, "hostname_cache": { "eviction_age_in_seconds": 3600, - "shared_memory_size_in_bytes": 2500000 + "shared_memory_size_in_bytes": 2500000, + "cache_clearer_sleep_time_in_seconds": 600 }, "maximum_size_for_single_buffer_in_megabytes": 32, "maximum_size_of_delay_queue_in_bytes": 0, "maximum_temporary_password_lifetime_in_seconds": 1000, + "migrate_delay_server_sleep_time_in_seconds": 5, "number_of_concurrent_delay_rule_executors": 4, "stacktrace_file_processor_sleep_time_in_seconds": 10, "transfer_buffer_size_for_parallel_transfer_in_megabytes": 4, @@ -64,6 +68,8 @@ ], "catalog_service_role": "provider", "client_api_allowlist_policy": "enforce", + "client_server_policy": "CS_NEG_REFUSE", + "connection_pool_refresh_time_in_seconds": 300, "controlled_user_connection_list": { "control_type": "denylist", "users": [] @@ -71,9 +77,17 @@ "default_dir_mode": "0750", "default_file_mode": "0600", "default_hash_scheme": "SHA256", - "default_resource_name": "demoResc", + "default_resource_name": {{ irods_server_default_rsc_name }}, + "encryption": { + "algorithm": "AES-256-CBC", + "key_size": 32, + "num_hash_rounds": 16, + "salt_size": 8 + }, "environment_variables": {}, "federation": [], + "graceful_shutdown_timeout_in_seconds": 30, + "host": "{{ irods_server_host }}", "host_access_control": { "access_entries": [] }, @@ -100,6 +114,13 @@ "plugin_configuration": { "authentication": {}, "database": { + "host": "{{ irods_server_db_host }}", + "name": "{{ irods_server_db_name }}", + "odbc_driver": "{{ irods_server_db_driver }}", + "password": "{{ irods_server_db_password }}", + "port": {{ irods_server_db_port }}, + "username": "{{ irods_server_db_username }}", + "technology": "postgres", "postgres": { "db_host": "{{ irods_server_db_host }}", "db_name": "{{ irods_server_db_name }}", @@ -124,6 +145,10 @@ ], "re_rulebase_set": [ "core" + {%- for filename in irods_server_hooks_files.keys() -%} + , "{{ filename }}" + {%- endfor %} + ], "regexes_for_supported_peps": [ "ac[^ ]*", @@ -133,12 +158,13 @@ }, "shared_memory_instance": "irods_rule_language_rule_engine" }, + {%- if irods_server_python_plugin -%} { "instance_name": "irods_rule_engine_plugin-python-instance", "plugin_name": "irods_rule_engine_plugin-python", "plugin_specific_configuration": {} - }, - + }, + {%- endif %} { "instance_name": "irods_rule_engine_plugin-cpp_default_policy-instance", "plugin_name": "irods_rule_engine_plugin-cpp_default_policy", diff --git a/playbooks/roles/irods_server/templates/server_unattended_config_v5.json.j2 b/playbooks/roles/irods_server/templates/server_unattended_config_v5.json.j2 new file mode 100644 index 00000000..2379f6d3 --- /dev/null +++ b/playbooks/roles/irods_server/templates/server_unattended_config_v5.json.j2 @@ -0,0 +1,197 @@ +{ + "admin_password": "{{ irods_server_admin_password }}", + "default_resource_directory": "{{ irods_server_default_rsc_path }}", + "default_resource_name": "{{ irods_server_default_rsc_name }}", + "host": "{{ irods_server_host }}", + "host_system_information": { + "service_account_user_name": "irods", + "service_account_group_name": "irods" + }, + "service_account_environment": { + "irods_client_server_negotiation": "request_server_negotiation", + "irods_client_server_policy": "CS_NEG_REFUSE", + "irods_connection_pool_refresh_time_in_seconds": 300, + "irods_cwd": "/tempZone/home/rods", + "irods_default_hash_scheme": "SHA256", + "irods_default_number_of_transfer_threads": 4, + "irods_default_resource": "{{ irods_server_default_rsc_name }}", + "irods_encryption_algorithm": "AES-256-CBC", + "irods_encryption_key_size": 32, + "irods_encryption_num_hash_rounds": 16, + "irods_encryption_salt_size": 8, + "irods_home": "/tempZone/home/rods", + "irods_host": "{{ irods_server_host }}", + "irods_match_hash_policy": "compatible", + "irods_maximum_size_for_single_buffer_in_megabytes": 32, + "irods_port": 1247, + "irods_server_control_plane_encryption_algorithm": "AES-256-CBC", + "irods_server_control_plane_encryption_num_hash_rounds": 16, + "irods_server_control_plane_key": "{{ irods_server_control_plane_key.stdout }}", + "irods_server_control_plane_port": 1248, + "irods_transfer_buffer_size_for_parallel_transfer_in_megabytes": 4, + "irods_user_name": "rods", + "irods_zone_name": "{{ irods_server_zone }}", + "schema_name": "service_account_environment", + "schema_version": "v4" + }, + "server_config": { + "advanced_settings": { + "checksum_read_buffer_size_in_bytes": 1048576, + "default_log_rotation_in_days": 5, + "default_number_of_transfer_threads": 4, + "default_temporary_password_lifetime_in_seconds": 120, + "delay_rule_executors": [ + "localhost" + ], + "delay_server_sleep_time_in_seconds": 30, + "dns_cache": { + "eviction_age_in_seconds": 3600, + "shared_memory_size_in_bytes": 5000000, + "cache_clearer_sleep_time_in_seconds": 600 + }, + "hostname_cache": { + "eviction_age_in_seconds": 3600, + "shared_memory_size_in_bytes": 2500000, + "cache_clearer_sleep_time_in_seconds": 600 + }, + "maximum_size_for_single_buffer_in_megabytes": 32, + "maximum_size_of_delay_queue_in_bytes": 0, + "maximum_temporary_password_lifetime_in_seconds": 1000, + "migrate_delay_server_sleep_time_in_seconds": 5, + "number_of_concurrent_delay_rule_executors": 4, + "stacktrace_file_processor_sleep_time_in_seconds": 10, + "transfer_buffer_size_for_parallel_transfer_in_megabytes": 4, + "transfer_chunk_size_for_parallel_transfer_in_megabytes": 40 + }, + "catalog_provider_hosts": [ + "localhost", + "{{ irods_server_host }}" + ], + "catalog_service_role": "provider", + "client_api_allowlist_policy": "enforce", + "client_server_policy": "CS_NEG_REFUSE", + "connection_pool_refresh_time_in_seconds": 300, + "controlled_user_connection_list": { + "control_type": "denylist", + "users": [] + }, + "default_dir_mode": "0750", + "default_file_mode": "0600", + "default_hash_scheme": "SHA256", + "default_resource_name": "{{ irods_server_default_rsc_name }}", + "encryption": { + "algorithm": "AES-256-CBC", + "key_size": 32, + "num_hash_rounds": 16, + "salt_size": 8 + }, + "environment_variables": {}, + "federation": [], + "graceful_shutdown_timeout_in_seconds": 30, + "host": "{{ irods_server_host }}", + "host_access_control": { + "access_entries": [] + }, + "host_resolution": { + "host_entries": [] + }, + "log_level": { + "agent": "info", + "agent_factory": "info", + "api": "info", + "authentication": "info", + "database": "info", + "delay_server": "info", + "legacy": "info", + "microservice": "info", + "network": "info", + "resource": "info", + "rule_engine": "info", + "server": "info", + "sql": "info" + }, + "match_hash_policy": "compatible", + "negotiation_key": "{{ irods_server_negotiation_key.stdout }}", + "plugin_configuration": { + "authentication": {}, + "database": { + "host": "{{ irods_server_db_host }}", + "name": "{{ irods_server_db_name }}", + "odbc_driver": "{{ irods_server_db_driver }}", + "password": "{{ irods_server_db_password }}", + "port": {{ irods_server_db_port }}, + "username": "{{ irods_server_db_username }}", + "technology": "postgres", + "postgres": { + "db_host": "{{ irods_server_db_host }}", + "db_name": "{{ irods_server_db_name }}", + "db_odbc_driver": "{{ irods_server_db_driver }}", + "db_password": "{{ irods_server_db_password }}", + "db_port": {{ irods_server_db_port }}, + "db_username": "{{ irods_server_db_username }}" + } + }, + "network": {}, + "resource": {}, + "rule_engines": [ + { + "instance_name": "irods_rule_engine_plugin-irods_rule_language-instance", + "plugin_name": "irods_rule_engine_plugin-irods_rule_language", + "plugin_specific_configuration": { + "re_data_variable_mapping_set": [ + "core" + ], + "re_function_name_mapping_set": [ + "core" + ], + "re_rulebase_set": [ + "core" + {%- for filename in irods_server_hooks_files.keys() -%} + , "{{ filename }}" + {%- endfor %} + + ], + "regexes_for_supported_peps": [ + "ac[^ ]*", + "msi[^ ]*", + "[^ ]*pep_[^ ]*_(pre|post|except|finally)" + ] + }, + "shared_memory_instance": "irods_rule_language_rule_engine" + }, + {%- if irods_server_python_plugin -%} + { + "instance_name": "irods_rule_engine_plugin-python-instance", + "plugin_name": "irods_rule_engine_plugin-python", + "plugin_specific_configuration": {} + }, + {%- endif %} + { + "instance_name": "irods_rule_engine_plugin-cpp_default_policy-instance", + "plugin_name": "irods_rule_engine_plugin-cpp_default_policy", + "plugin_specific_configuration": {} + } + ] + }, + "rule_engine_namespaces": [ + "" + ], + "schema_name": "server_config", + "schema_validation_base_uri": "file:///var/lib/irods/configuration_schemas", + "schema_version": "v4", + "server_control_plane_encryption_algorithm": "AES-256-CBC", + "server_control_plane_encryption_num_hash_rounds": 16, + "server_control_plane_key": "{{ irods_server_control_plane_key.stdout }}", + "server_control_plane_port": 1248, + "server_control_plane_timeout_milliseconds": 10000, + "server_port_range_end": 20199, + "server_port_range_start": 20000, + "xmsg_port": 1279, + "zone_auth_scheme": "native", + "zone_key": "{{ irods_server_zone_key.stdout }}", + "zone_name": "{{ irods_server_zone }}", + "zone_port": 1247, + "zone_user": "rods" + } +} + diff --git a/playbooks/roles/irods_server/vars/main.yml b/playbooks/roles/irods_server/vars/main.yml new file mode 100644 index 00000000..445fab97 --- /dev/null +++ b/playbooks/roles/irods_server/vars/main.yml @@ -0,0 +1,4 @@ +--- +irods_server_default_rsc_path: "{{ (irods_server_use_external_storage and (fact_workspace_storage[0]['mount'] | default(false, true))) or '/var/lib/irods' }}/Vault" # if set to use external storage, and an external storage was found, use the first external storage +irods_server_db_driver: "{{ (ansible_pkg_mgr == 'dnf') | ternary('PostgreSQL', 'PostgreSQL ANSI') }}" +irods_server_hooks_training: "{{ lookup('ansible.builtin.file', 'hooks_training.re') }}" diff --git a/playbooks/roles/postgresql/meta/main.yml b/playbooks/roles/postgresql/meta/main.yml new file mode 100644 index 00000000..f5521a5f --- /dev/null +++ b/playbooks/roles/postgresql/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - role: fact_workspace_info diff --git a/playbooks/roles/postgresql/tasks/provision.yml b/playbooks/roles/postgresql/tasks/provision.yml index faf10c6e..3b2b93a5 100644 --- a/playbooks/roles/postgresql/tasks/provision.yml +++ b/playbooks/roles/postgresql/tasks/provision.yml @@ -3,6 +3,7 @@ ansible.builtin.pip: name: psycopg2-binary state: present + executable: "{{ fact_src_ansible_venv }}/bin/pip" - name: Create PostgreSQL database community.postgresql.postgresql_db: