Merge branch 'devel' into feature_web-task-split

This commit is contained in:
Hao Liu
2023-02-22 21:40:59 -05:00
9 changed files with 207 additions and 102 deletions

View File

@@ -28,7 +28,7 @@ from rest_framework import generics
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rest_framework import views from rest_framework import views
from rest_framework.permissions import AllowAny from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import StaticHTMLRenderer from rest_framework.renderers import StaticHTMLRenderer
from rest_framework.negotiation import DefaultContentNegotiation from rest_framework.negotiation import DefaultContentNegotiation
@@ -822,7 +822,7 @@ def trigger_delayed_deep_copy(*args, **kwargs):
class CopyAPIView(GenericAPIView): class CopyAPIView(GenericAPIView):
serializer_class = CopySerializer serializer_class = CopySerializer
permission_classes = (AllowAny,) permission_classes = (IsAuthenticated,)
copy_return_serializer_class = None copy_return_serializer_class = None
new_in_330 = True new_in_330 = True
new_in_api_v2 = True new_in_api_v2 = True

View File

@@ -4288,7 +4288,7 @@ class WorkflowApprovalTemplateJobsList(SubListAPIView):
parent_key = 'workflow_approval_template' parent_key = 'workflow_approval_template'
class WorkflowApprovalList(ListCreateAPIView): class WorkflowApprovalList(ListAPIView):
model = models.WorkflowApproval model = models.WorkflowApproval
serializer_class = serializers.WorkflowApprovalListSerializer serializer_class = serializers.WorkflowApprovalListSerializer

View File

@@ -25,42 +25,47 @@
connection: local connection: local
name: Update source tree if necessary name: Update source tree if necessary
tasks: tasks:
- name: Delete project directory before update
- name: delete project directory before update ansible.builtin.shell: set -o pipefail && find . -delete -print | head -2 # volume mounted, cannot delete folder itself
command: "find -delete" # volume mounted, cannot delete folder itself register: reg
changed_when: reg.stdout_lines | length > 1
args: args:
chdir: "{{ project_path }}" chdir: "{{ project_path }}"
tags: tags:
- delete - delete
- block: - name: Update project using git
- name: update project using git tags:
git: - update_git
dest: "{{project_path|quote}}" block:
repo: "{{scm_url}}" - name: Update project using git
version: "{{scm_branch|quote}}" ansible.builtin.git:
refspec: "{{scm_refspec|default(omit)}}" dest: "{{ project_path | quote }}"
force: "{{scm_clean}}" repo: "{{ scm_url }}"
track_submodules: "{{scm_track_submodules|default(omit)}}" version: "{{ scm_branch | quote }}"
accept_hostkey: "{{scm_accept_hostkey|default(omit)}}" refspec: "{{ scm_refspec | default(omit) }}"
force: "{{ scm_clean }}"
track_submodules: "{{ scm_track_submodules | default(omit) }}"
accept_hostkey: "{{ scm_accept_hostkey | default(omit) }}"
register: git_result register: git_result
- name: Set the git repository version - name: Set the git repository version
set_fact: ansible.builtin.set_fact:
scm_version: "{{ git_result['after'] }}" scm_version: "{{ git_result['after'] }}"
when: "'after' in git_result" when: "'after' in git_result"
tags:
- update_git
- block: - name: Update project using svn
- name: update project using svn tags:
subversion: - update_svn
dest: "{{project_path|quote}}" block:
repo: "{{scm_url|quote}}" - name: Update project using svn
revision: "{{scm_branch|quote}}" ansible.builtin.subversion:
force: "{{scm_clean}}" dest: "{{ project_path | quote }}"
username: "{{scm_username|default(omit)}}" repo: "{{ scm_url | quote }}"
password: "{{scm_password|default(omit)}}" revision: "{{ scm_branch | quote }}"
force: "{{ scm_clean }}"
username: "{{ scm_username | default(omit) }}"
password: "{{ scm_password | default(omit) }}"
# must be in_place because folder pre-existing, because it is mounted # must be in_place because folder pre-existing, because it is mounted
in_place: true in_place: true
environment: environment:
@@ -68,85 +73,90 @@
register: svn_result register: svn_result
- name: Set the svn repository version - name: Set the svn repository version
set_fact: ansible.builtin.set_fact:
scm_version: "{{ svn_result['after'] }}" scm_version: "{{ svn_result['after'] }}"
when: "'after' in svn_result" when: "'after' in svn_result"
- name: parse subversion version string properly - name: Parse subversion version string properly
set_fact: ansible.builtin.set_fact:
scm_version: "{{scm_version|regex_replace('^.*Revision: ([0-9]+).*$', '\\1')}}" scm_version: "{{ scm_version | regex_replace('^.*Revision: ([0-9]+).*$', '\\1') }}"
tags:
- update_svn
- block:
- name: Project update for Insights
tags:
- update_insights
block:
- name: Ensure the project directory is present - name: Ensure the project directory is present
file: ansible.builtin.file:
dest: "{{project_path|quote}}" dest: "{{ project_path | quote }}"
state: directory state: directory
mode: '0755'
- name: Fetch Insights Playbook(s) - name: Fetch Insights Playbook(s)
insights: insights:
insights_url: "{{insights_url}}" insights_url: "{{ insights_url }}"
username: "{{scm_username}}" username: "{{ scm_username }}"
password: "{{scm_password}}" password: "{{ scm_password }}"
project_path: "{{project_path}}" project_path: "{{ project_path }}"
awx_license_type: "{{awx_license_type}}" awx_license_type: "{{ awx_license_type }}"
awx_version: "{{awx_version}}" awx_version: "{{ awx_version }}"
register: results register: results
- name: Save Insights Version - name: Save Insights Version
set_fact: ansible.builtin.set_fact:
scm_version: "{{results.version}}" scm_version: "{{ results.version }}"
when: results is defined when: results is defined
tags:
- update_insights
- block:
- name: Update project using archive
tags:
- update_archive
block:
- name: Ensure the project archive directory is present - name: Ensure the project archive directory is present
file: ansible.builtin.file:
dest: "{{ project_path|quote }}/.archive" dest: "{{ project_path | quote }}/.archive"
state: directory state: directory
mode: '0755'
- name: Get archive from url - name: Get archive from url
get_url: ansible.builtin.get_url:
url: "{{ scm_url|quote }}" url: "{{ scm_url | quote }}"
dest: "{{ project_path|quote }}/.archive/" dest: "{{ project_path | quote }}/.archive/"
url_username: "{{ scm_username|default(omit) }}" url_username: "{{ scm_username | default(omit) }}"
url_password: "{{ scm_password|default(omit) }}" url_password: "{{ scm_password | default(omit) }}"
force_basic_auth: true force_basic_auth: true
mode: '0755'
register: get_archive register: get_archive
- name: Unpack archive - name: Unpack archive
project_archive: project_archive:
src: "{{ get_archive.dest }}" src: "{{ get_archive.dest }}"
project_path: "{{ project_path|quote }}" project_path: "{{ project_path | quote }}"
force: "{{ scm_clean }}" force: "{{ scm_clean }}"
when: get_archive.changed or scm_clean when: get_archive.changed or scm_clean
register: unarchived register: unarchived
- name: Find previous archives - name: Find previous archives
find: ansible.builtin.find:
paths: "{{ project_path|quote }}/.archive/" paths: "{{ project_path | quote }}/.archive/"
excludes: excludes:
- "{{ get_archive.dest|basename }}" - "{{ get_archive.dest | basename }}"
when: unarchived.changed when: unarchived.changed
register: previous_archive register: previous_archive
- name: Remove previous archives - name: Remove previous archives
file: ansible.builtin.file:
path: "{{ item.path }}" path: "{{ item.path }}"
state: absent state: absent
loop: "{{ previous_archive.files }}" loop: "{{ previous_archive.files }}"
when: previous_archive.files|default([]) when: previous_archive.files | default([])
- name: Set scm_version to archive sha1 checksum - name: Set scm_version to archive sha1 checksum
set_fact: ansible.builtin.set_fact:
scm_version: "{{ get_archive.checksum_src }}" scm_version: "{{ get_archive.checksum_src }}"
tags:
- update_archive
- name: Repository Version - name: Repository Version
debug: ansible.builtin.debug:
msg: "Repository Version {{ scm_version }}" msg: "Repository Version {{ scm_version }}"
tags: tags:
- update_git - update_git
@@ -183,60 +193,59 @@
additional_collections_env: additional_collections_env:
# These environment variables are used for installing collections, in addition to galaxy_task_env # These environment variables are used for installing collections, in addition to galaxy_task_env
# setting the collections paths silences warnings # setting the collections paths silences warnings
ANSIBLE_COLLECTIONS_PATHS: "{{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_collections" ANSIBLE_COLLECTIONS_PATHS: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_collections"
# Put the local tmp directory in same volume as collection destination # Put the local tmp directory in same volume as collection destination
# otherwise, files cannot be moved accross volumes and will cause error # otherwise, files cannot be moved accross volumes and will cause error
ANSIBLE_LOCAL_TEMP: "{{projects_root}}/.__awx_cache/{{local_path}}/stage/tmp" ANSIBLE_LOCAL_TEMP: "{{ projects_root }}/.__awx_cache/{{ local_path }}/stage/tmp"
tasks: tasks:
- name: Check content sync settings - name: Check content sync settings
block: when: not roles_enabled | bool and not collections_enabled | bool
- debug:
msg: >
Collection and role syncing disabled. Check the AWX_ROLES_ENABLED and
AWX_COLLECTIONS_ENABLED settings and Galaxy credentials on the project's organization.
- meta: end_play
when: not roles_enabled|bool and not collections_enabled|bool
tags: tags:
- install_roles - install_roles
- install_collections - install_collections
block:
- name: Warn about disabled content sync
ansible.builtin.debug:
msg: >
Collection and role syncing disabled. Check the AWX_ROLES_ENABLED and
AWX_COLLECTIONS_ENABLED settings and Galaxy credentials on the project's organization.
- name: End play due to disabled content sync
ansible.builtin.meta: end_play
- name: fetch galaxy roles from requirements.(yml/yaml) - name: Fetch galaxy roles from requirements.(yml/yaml)
command: > ansible.builtin.command: >
ansible-galaxy role install -r {{ item }} ansible-galaxy role install -r {{ item }}
--roles-path {{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_roles --roles-path {{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_roles
{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} {{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }}
args: args:
chdir: "{{project_path|quote}}" chdir: "{{ project_path | quote }}"
register: galaxy_result register: galaxy_result
with_fileglob: with_fileglob:
- "{{project_path|quote}}/roles/requirements.yaml" - "{{ project_path | quote }}/roles/requirements.yaml"
- "{{project_path|quote}}/roles/requirements.yml" - "{{ project_path | quote }}/roles/requirements.yml"
changed_when: "'was installed successfully' in galaxy_result.stdout" changed_when: "'was installed successfully' in galaxy_result.stdout"
environment: "{{ galaxy_task_env }}" environment: "{{ galaxy_task_env }}"
when: roles_enabled|bool when: roles_enabled | bool
tags: tags:
- install_roles - install_roles
- name: fetch galaxy collections from collections/requirements.(yml/yaml) - name: Fetch galaxy collections from collections/requirements.(yml/yaml)
command: > ansible.builtin.command: >
ansible-galaxy collection install -r {{ item }} ansible-galaxy collection install -r {{ item }}
--collections-path {{projects_root}}/.__awx_cache/{{local_path}}/stage/requirements_collections --collections-path {{ projects_root }}/.__awx_cache/{{ local_path }}/stage/requirements_collections
{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} {{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }}
args: args:
chdir: "{{project_path|quote}}" chdir: "{{ project_path | quote }}"
register: galaxy_collection_result register: galaxy_collection_result
with_fileglob: with_fileglob:
- "{{project_path|quote}}/collections/requirements.yaml" - "{{ project_path | quote }}/collections/requirements.yaml"
- "{{project_path|quote}}/collections/requirements.yml" - "{{ project_path | quote }}/collections/requirements.yml"
- "{{project_path|quote}}/requirements.yaml" - "{{ project_path | quote }}/requirements.yaml"
- "{{project_path|quote}}/requirements.yml" - "{{ project_path | quote }}/requirements.yml"
changed_when: "'Installing ' in galaxy_collection_result.stdout" changed_when: "'Installing ' in galaxy_collection_result.stdout"
environment: "{{ additional_collections_env | combine(galaxy_task_env) }}" environment: "{{ additional_collections_env | combine(galaxy_task_env) }}"
when: when:
- "ansible_version.full is version_compare('2.9', '>=')" - "ansible_version.full is version_compare('2.9', '>=')"
- collections_enabled|bool - collections_enabled | bool
tags: tags:
- install_collections - install_collections

View File

@@ -57,7 +57,15 @@ extends_documentation_fragment: awx.awx.auth
EXAMPLES = """ EXAMPLES = """
- name: Launch a workflow with a timeout of 10 seconds - name: Create a workflow approval node
workflow_job_template_node:
identifier: approval_test
approval_node:
name: approval_jt_name
timeout: 900
workflow: "Test Workflow"
- name: Launch the workflow with a timeout of 10 seconds
workflow_launch: workflow_launch:
workflow_template: "Test Workflow" workflow_template: "Test Workflow"
wait: False wait: False
@@ -66,7 +74,7 @@ EXAMPLES = """
- name: Wait for approval node to activate and approve - name: Wait for approval node to activate and approve
workflow_approval: workflow_approval:
workflow_job_id: "{{ workflow.id }}" workflow_job_id: "{{ workflow.id }}"
name: Approve Me name: approval_jt_name
interval: 10 interval: 10
timeout: 20 timeout: 20
action: deny action: deny

View File

@@ -183,7 +183,21 @@ options:
inventory: inventory:
description: description:
- Inventory applied as a prompt, if job template prompts for inventory - Inventory applied as a prompt, if job template prompts for inventory
type: str type: dict
suboptions:
name:
description:
- Name Inventory to be applied to job as launch-time prompts.
type: str
organization:
description:
- Name of key for use in model for organizational reference
type: dict
suboptions:
name:
description:
- The organization of the credentials exists in.
type: str
scm_branch: scm_branch:
description: description:
- SCM branch applied as a prompt, if job template prompts for SCM branch - SCM branch applied as a prompt, if job template prompts for SCM branch
@@ -544,6 +558,10 @@ EXAMPLES = '''
type: job_template type: job_template
execution_environment: execution_environment:
name: My EE name: My EE
inventory:
name: Test inventory
organization:
name: Default
related: related:
credentials: credentials:
- name: cyberark - name: cyberark
@@ -613,10 +631,6 @@ def create_workflow_nodes(module, response, workflow_nodes, workflow_id):
if workflow_node['unified_job_template']['type'] != 'workflow_approval': if workflow_node['unified_job_template']['type'] != 'workflow_approval':
module.fail_json(msg="Unable to Find unified_job_template: {0}".format(search_fields)) module.fail_json(msg="Unable to Find unified_job_template: {0}".format(search_fields))
inventory = workflow_node.get('inventory')
if inventory:
workflow_node_fields['inventory'] = module.resolve_name_to_id('inventories', inventory)
# Lookup Values for other fields # Lookup Values for other fields
for field_name in ( for field_name in (
@@ -645,6 +659,17 @@ def create_workflow_nodes(module, response, workflow_nodes, workflow_id):
'execution_environments', name_or_id=workflow_node['execution_environment']['name'] 'execution_environments', name_or_id=workflow_node['execution_environment']['name']
)['id'] )['id']
# Two lookup methods are used based on a fix added in 21.11.0, and the awx export model
if 'inventory' in workflow_node:
if 'name' in workflow_node['inventory']:
inv_lookup_data = {}
if 'organization' in workflow_node['inventory']:
inv_lookup_data['organization'] = module.resolve_name_to_id('organizations', workflow_node['inventory']['organization']['name'])
workflow_node_fields['inventory'] = module.get_one(
'inventories', name_or_id=workflow_node['inventory']['name'], data=inv_lookup_data)['id']
else:
workflow_node_fields['inventory'] = module.get_one('inventories', name_or_id=workflow_node['inventory'])['id']
# Set Search fields # Set Search fields
search_fields['workflow_job_template'] = workflow_node_fields['workflow_job_template'] = workflow_id search_fields['workflow_job_template'] = workflow_node_fields['workflow_job_template'] = workflow_id

View File

@@ -16,7 +16,7 @@ import glob
# Normally a read-only endpoint should not have a module (i.e. /api/v2/me) but sometimes we reuse a name # Normally a read-only endpoint should not have a module (i.e. /api/v2/me) but sometimes we reuse a name
# For example, we have a role module but /api/v2/roles is a read only endpoint. # For example, we have a role module but /api/v2/roles is a read only endpoint.
# This list indicates which read-only endpoints have associated modules with them. # This list indicates which read-only endpoints have associated modules with them.
read_only_endpoints_with_modules = ['settings', 'role', 'project_update'] read_only_endpoints_with_modules = ['settings', 'role', 'project_update', 'workflow_approval']
# If a module should not be created for an endpoint and the endpoint is not read-only add it here # If a module should not be created for an endpoint and the endpoint is not read-only add it here
# THINK HARD ABOUT DOING THIS # THINK HARD ABOUT DOING THIS

View File

@@ -0,0 +1,57 @@
---
- name: Generate a random string for names
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
test_prefix: AWX-Collection-tests-workflow_approval
- name: Generate random names for test objects
set_fact:
org_name: "{{ test_prefix }}-org-{{ test_id }}"
approval_node_name: "{{ test_prefix }}-node-{{ test_id }}"
wfjt_name: "{{ test_prefix }}-wfjt-{{ test_id }}"
- block:
- name: Create a new organization for test isolation
organization:
name: "{{ org_name }}"
- name: Create a workflow job template
workflow_job_template:
name: "{{ wfjt_name }}"
organization: "{{ org_name }}"
- name: Create approval node
workflow_job_template_node:
identifier: approval_test
approval_node:
name: "{{ approval_node_name }}" # Referenced later on
timeout: 900
workflow: "{{ wfjt_name }}"
# Launch and approve the workflow
- name: Launch the workflow
workflow_launch:
workflow_template: "{{ wfjt_name }}"
wait: False
register: workflow_job
- name: Wait for approval node to activate and approve
workflow_approval:
workflow_job_id: "{{ workflow_job.id }}"
name: "{{ approval_node_name }}"
interval: 10
timeout: 20
action: approve
register: result
- assert:
that:
- "result is changed"
- "result is not failed"
always:
- name: Delete the workflow job template
workflow_job_template:
name: "{{ wfjt_name }}"
state: absent
ignore_errors: True

View File

@@ -493,6 +493,7 @@
workflow_job_template: workflow_job_template:
name: "copy_{{ wfjt_name }}" name: "copy_{{ wfjt_name }}"
organization: Default organization: Default
ask_inventory_on_launch: true
survey_spec: survey_spec:
name: Basic Survey name: Basic Survey
description: Basic Survey description: Basic Survey
@@ -737,6 +738,10 @@
timeout: 23 timeout: 23
execution_environment: execution_environment:
name: "{{ ee1 }}" name: "{{ ee1 }}"
inventory:
name: Test inventory
organization:
name: Default
related: related:
credentials: credentials:
- name: "{{ scm_cred_name }}" - name: "{{ scm_cred_name }}"

View File

@@ -75,7 +75,8 @@ In the root of awx-operator:
-e image_version=devel \ -e image_version=devel \
-e image_pull_policy=Always \ -e image_pull_policy=Always \
-e service_type=nodeport \ -e service_type=nodeport \
-e namespace=awx -e namespace=awx \
-e nodeport_port=30080
``` ```
Check the operator with the following commands: Check the operator with the following commands: