Compare commits

..

485 Commits
9.0.0 ... 9.1.0

Author SHA1 Message Date
Shane McDonald
b23856f126 Bump VERSION to 9.1.0 2019-12-17 11:38:32 -05:00
softwarefactory-project-zuul[bot]
c062728359 Merge pull request #5512 from jakemcdermott/ui-next-fix-template-tab-unload
Don't reload template panel on tab change

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-17 00:16:25 +00:00
softwarefactory-project-zuul[bot]
85d185cc8b Merge pull request #5505 from jakemcdermott/fix-5485
Use job details status to test status

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 23:45:45 +00:00
softwarefactory-project-zuul[bot]
77b8f345ae Merge pull request #5511 from jakemcdermott/ui-next-fix-inv-list-select-all-state
Initialize inventory list with none selected

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 23:14:37 +00:00
softwarefactory-project-zuul[bot]
0052967aee Merge pull request #5489 from fosterseth/fix-proj-update-blocking
Remove project update blocking

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 19:51:51 +00:00
Seth Foster
63e9aed601 Prevent running jobs from blocking project updates
A running job that has a project update will block
that update from running. This fix removes
the block.
Adds a functional test that sets up a job in "running" state, and
starts a project update that is in "pending" state. Assert that
the task manager and dependency graph .is_job_blocked methods both
return False.

issue #5153
2019-12-16 13:43:42 -05:00
softwarefactory-project-zuul[bot]
d4be8c8168 Merge pull request #5516 from shanemcd/downstream-changes
A few downstream changes

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 17:49:03 +00:00
softwarefactory-project-zuul[bot]
cdf4b0d1ed Merge pull request #5510 from jakemcdermott/ui-next-fix-user-link-in
Fix access list and navigation user link-ins

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 17:48:58 +00:00
softwarefactory-project-zuul[bot]
c43a59e475 Merge pull request #5391 from wenottingham/really-we-would-remove-the-drummer-before-slash
Don't error on a trailing slash, just fix it up

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 17:19:34 +00:00
Jake McDermott
014520ee2b Initialize list with none selected 2019-12-16 11:46:53 -05:00
Shane McDonald
c1abc56753 Merge remote-tracking branch 'downstream/release_3.6.2' into downstream-changes 2019-12-16 11:13:10 -05:00
softwarefactory-project-zuul[bot]
c5b4681bf4 Merge pull request #5491 from marshmalien/inv-host-add
Add Inventory Host Add form 

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 16:06:24 +00:00
softwarefactory-project-zuul[bot]
00b7d6571a Merge pull request #5509 from jakemcdermott/ui-next-webpack-4.41.2
Update webpack to 4.41.2

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 15:55:29 +00:00
Jake McDermott
9ed2534ac5 Don't reload panel on tab change 2019-12-16 09:25:12 -05:00
Jake McDermott
a3bc3986bb Fix resource access list user links 2019-12-16 08:51:10 -05:00
Jake McDermott
230933744c Fix nav user links 2019-12-16 08:50:59 -05:00
Jake McDermott
227a90006e Update webpack to 4.41.2 2019-12-16 08:36:24 -05:00
softwarefactory-project-zuul[bot]
112f89660b Merge pull request #5239 from AlanCoding/migration_cache
Reduce API response times by 30% by using memcache migration flag

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-16 04:33:29 +00:00
AlanCoding
a0910eb6de Apply migration flag check to task manager 2019-12-15 22:56:57 -05:00
AlanCoding
5433af6716 Reduce API response times by caching migration flag 2019-12-15 22:56:57 -05:00
Jake McDermott
9744b89737 Use job details status to test status 2019-12-13 17:32:23 -05:00
softwarefactory-project-zuul[bot]
04c535e3f9 Merge pull request #5424 from AlexSCorey/InventoryGroupAdd/Edit
Adds Inventory Groups

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-13 22:23:14 +00:00
Alex Corey
259e53f59d Fixes failing zuul test 2019-12-13 16:38:59 -05:00
Bill Nottingham
ac9bf1afcf Don't error on a trailing slash, just fix it up 2019-12-13 15:59:40 -05:00
Alex Corey
4b62d77015 Moves inventoryGroupForm into shared directory
Updates InventoryGroups tests
Adds ContentError functionalist to catch a case where a use might navigate to an Inventory
that isn't associated to the shown inventoryGroup.
2019-12-13 14:20:56 -05:00
Alex Corey
ef5ce0b082 Flattens Inventory File Structure Remove 2019-12-13 14:20:56 -05:00
Alex Corey
1942be7dc3 Checks for modified and create in Inv Group Deets
Also includes refactoring for css over style prop and removed some
unnecessary loading checks
2019-12-13 14:20:56 -05:00
Alex Corey
210f9577b0 Fixed filename typo 2019-12-13 14:20:56 -05:00
Alex Corey
87a05a5b2e Testing Improvements and Refactoring 2019-12-13 14:20:56 -05:00
Alex Corey
f8a754cf44 Adds Alert Modal, Breadcrumb, Nested Tabs and Refactors PR. 2019-12-13 14:20:56 -05:00
Alex Corey
3ea37e1c79 Addresses PR issues
Adds Delete Modal for deleting from Details view
Adds test for delete modal
Addresses styling for Variables label
Removes X close button from form
2019-12-13 14:20:56 -05:00
Alex Corey
c997fcfc2c Adds Inventory Groups routing --sort of
Adds Inventory Groups Add
Adds Inventory Groups Edit
Adds Inventory Groups Form
Adds api module for Groups
Adds placeholder file for InventoryGroupsList.  This was added to refine routing.  Tgere are no tests for this file yet.
2019-12-13 14:20:56 -05:00
softwarefactory-project-zuul[bot]
4dd4928aab Merge pull request #5495 from shanemcd/less-is-more-secure
Remove some unused stuff from k8s secret

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-13 14:06:50 +00:00
softwarefactory-project-zuul[bot]
5aeaabaceb Merge pull request #5438 from jainnikhil30/devel
adding instance consumed and remaining capacity to metrics

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-13 08:27:38 +00:00
Nikhil Jain
0d4e6d7e0b do the sum once and store it in a variable which will be reused 2019-12-13 13:21:49 +05:30
Nikhil Jain
9ae038868c adding instance consumed and remaining capacity to metrics 2019-12-13 13:21:49 +05:30
Shane McDonald
0b4ae74698 Remove some unused stuff from k8s secret 2019-12-12 19:36:56 -05:00
softwarefactory-project-zuul[bot]
0d248a12bc Merge pull request #5493 from ryanpetrello/rekey-command
add an awx-manage command for re-generating SECRET_KEY

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-12 22:07:23 +00:00
Ryan Petrello
7396e2e7ac add an awx-manage command for re-generating SECRET_KEY 2019-12-12 16:19:20 -05:00
Ryan Petrello
cac3bece56 Merge pull request #4032 from ghjm/configurable_login_3.6.2
[3.6.2] Configurable login redirect
2019-12-12 15:43:58 -05:00
Ryan Petrello
e4145b580c fix a flake8 nit 2019-12-12 15:43:23 -05:00
Ryan Petrello
74076b99d6 Merge pull request #3998 from ryanpetrello/rekey-secret-key
[3.6.2] add an awx-manage command for re-generating SECRET_KEY
2019-12-12 15:25:15 -05:00
Marliana Lara
5d35506b0c Add Inventory Host Add form 2019-12-12 13:58:22 -05:00
softwarefactory-project-zuul[bot]
e646b46a2c Merge pull request #5479 from ghjm/configurable_login
Configurable login redirect

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-12 14:50:19 +00:00
mabashian
475e2605d4 Changes redirect logic slightly to lean on a global var to store the config response rather than a constant on the awApp module. This should allow us to avoid test changes. 2019-12-11 21:12:38 -05:00
mabashian
c16ad89ff9 Fix linting error (unused var) 2019-12-11 21:12:33 -05:00
mabashian
425d1168b9 Adds trailing slash to /api request 2019-12-11 21:12:26 -05:00
mabashian
7ceaa9ec4a Changes redirect logic slightly to lean on a global var to store the config response rather than a constant on the awApp module. This should allow us to avoid test changes. 2019-12-11 17:21:02 -05:00
mabashian
4b3d3537b4 Fix linting error (unused var) 2019-12-11 17:21:02 -05:00
mabashian
efbff24528 Adds trailing slash to /api request 2019-12-11 17:21:02 -05:00
mabashian
1d9ce6cc15 Moves config request out to block of code that gets executed before the app is bootstrapped. This should allow us to redirect to the override url before the app begins to render, improving the UX. 2019-12-11 17:21:02 -05:00
mabashian
794ce96b17 Reverts changes to logout logic. We don't want to redirect to an override url if the user explicitly logs out. 2019-12-11 17:21:02 -05:00
mabashian
181421a2ee Adds logic to redirect unauthenticated user if LOGIN_REDIRECT_OVERRIDE is set as long as the user is not navigating to /login or /#/login. Also redirects on logout if LOGIN_REDIRECT_OVERRIDE is set. 2019-12-11 17:21:02 -05:00
Graham Mainwaring
9c9496a683 Expose login redirect URL in unauthenticated /api view 2019-12-11 17:21:02 -05:00
Graham Mainwaring
2b111c81df Add /login convenience URL 2019-12-11 17:21:02 -05:00
mabashian
f467e26842 Adds login redirect override field to the System (Misc System) Settings interface 2019-12-11 17:21:02 -05:00
Graham Mainwaring
7700050d10 Add default for LOGIN_REDIRECT_OVERRIDE 2019-12-11 17:21:02 -05:00
Graham Mainwaring
a8d34b46fb Add setting for configurable login redirect URL 2019-12-11 17:21:02 -05:00
mabashian
bf6c16197c Moves config request out to block of code that gets executed before the app is bootstrapped. This should allow us to redirect to the override url before the app begins to render, improving the UX. 2019-12-11 11:39:11 -05:00
mabashian
25cc341888 Reverts changes to logout logic. We don't want to redirect to an override url if the user explicitly logs out. 2019-12-11 11:39:02 -05:00
mabashian
d899e75ad7 Adds logic to redirect unauthenticated user if LOGIN_REDIRECT_OVERRIDE is set as long as the user is not navigating to /login or /#/login. Also redirects on logout if LOGIN_REDIRECT_OVERRIDE is set. 2019-12-11 11:38:55 -05:00
Graham Mainwaring
732da52239 Expose login redirect URL in unauthenticated /api view 2019-12-11 11:38:45 -05:00
Graham Mainwaring
ab2f212b04 Add /login convenience URL 2019-12-11 11:38:36 -05:00
mabashian
f94438cf9b Adds login redirect override field to the System (Misc System) Settings interface 2019-12-11 11:38:31 -05:00
Graham Mainwaring
2569ec4f4f Add default for LOGIN_REDIRECT_OVERRIDE 2019-12-11 11:38:21 -05:00
Graham Mainwaring
b58bff4686 Add setting for configurable login redirect URL 2019-12-11 11:38:07 -05:00
softwarefactory-project-zuul[bot]
6fab3590ae Merge pull request #5408 from keithjgrant/5065-lookup-c
Lookup refactor

Reviewed-by: Jake McDermott <yo@jakemcdermott.me>
             https://github.com/jakemcdermott
2019-12-10 22:01:50 +00:00
Keith Grant
846fd67618 de-lint 2019-12-10 12:13:22 -08:00
Ryan Petrello
6254129f0d Merge pull request #4021 from beeankha/custom_email_approve_deny_fix
[3.6.2 Backport] Enable Approval-Related Email Notifications to Send Properly
2019-12-10 14:31:58 -05:00
Keith Grant
3409d39150 fix ProjectLookup re-renders 2019-12-10 11:09:11 -08:00
Keith Grant
9de165a676 revert MultiCredentialLookup loading jank fix 2019-12-10 11:09:11 -08:00
Keith Grant
f54616912d de-lint 2019-12-10 11:09:11 -08:00
Keith Grant
c003e89ea9 fix loading jank in MultiCredentialLookup 2019-12-10 11:09:11 -08:00
Keith Grant
6e64b5c070 clean up act() errors in form tests after Lookup changes 2019-12-10 11:09:11 -08:00
softwarefactory-project-zuul[bot]
fcfc34fef1 Merge pull request #5413 from beeankha/custom_email_approve_deny_fix
Enable Approval-Related Email Notifications to Send Properly

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-10 19:04:41 +00:00
Keith Grant
75b7d74f91 Lookup tweaks/bug fixes 2019-12-10 10:58:33 -08:00
Keith Grant
569b5bc533 clean up multiple test 'act()' warnings 2019-12-10 10:58:33 -08:00
Keith Grant
9ab9c6961b update Lookup tests, add OptionsList tests 2019-12-10 10:58:33 -08:00
Keith Grant
2e525f8922 update tests for CredentialLookup, OrgLookup, ProjectLookup 2019-12-10 10:58:33 -08:00
Keith Grant
9c6300c2de update MultiCredentialsLookup tests 2019-12-10 10:58:33 -08:00
Keith Grant
f8153393b1 fix minor lookup bugs 2019-12-10 10:58:33 -08:00
Keith Grant
cb07e9c757 convert all lookups to use new Lookup component 2019-12-10 10:58:33 -08:00
Keith Grant
639b297027 fix credential chips in SelectedList, MultiCredential cleanup 2019-12-10 10:58:32 -08:00
Keith Grant
4341d67fb0 add MultiCredentialsLookup select/deselect logic 2019-12-10 10:58:32 -08:00
Keith Grant
6260633974 flushing out new approach to MultiCredentialsLookup 2019-12-10 10:58:32 -08:00
Keith Grant
8ec856f3b6 start Lookup reducer 2019-12-10 10:58:32 -08:00
Keith Grant
5a207f155e WIP split Lookup into Lookup & CategoryLookup 2019-12-10 10:58:32 -08:00
Alex Corey
2a722ba8d0 Refactors Lookup 2019-12-10 10:58:32 -08:00
softwarefactory-project-zuul[bot]
efbd2177a5 Merge pull request #5449 from marshmalien/inv-groups-list
Add Inventory Groups and Groups Delete Modal

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-10 18:12:47 +00:00
softwarefactory-project-zuul[bot]
989e1ca5d6 Merge pull request #5477 from ryanpetrello/oauth-500
provide better HTTP responses for certain OAuth2 error scenarios

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-10 17:41:12 +00:00
Marliana Lara
4c89568d71 Apply radio selection to ALL selected groups in modal
* Use semantic html to describe modal list
* Move nested try/catch block
* Remove deprecated type fields
* If delete fails, keep selected list checked
2019-12-10 12:29:33 -05:00
softwarefactory-project-zuul[bot]
a9688ac805 Merge pull request #5471 from mabashian/5349-upgrade-angular-scheduler
Pull in latest version of our angular-scheduler fork to fix Tueday/Tuesday typo

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-10 17:25:35 +00:00
Ryan Petrello
12a8793ddb provide a better error for OAuth2 logins for external accounts
attempting to use an OAuth2 token as an externally authenticated user throws an HTTP 500 error when external oauth is disabled - this change improves that so it's a 401 Unauthorized instead.
2019-12-10 11:52:25 -05:00
softwarefactory-project-zuul[bot]
7bbf640389 Merge pull request #5450 from beeankha/tower_job_template_extra_vars
Add extra_vars Parameter to tower_job_template.py Module

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-10 16:32:24 +00:00
softwarefactory-project-zuul[bot]
cb6688c685 Merge pull request #5412 from appuk/apurva-new-ids
Add dataCy attribute

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-09 20:22:08 +00:00
Alan Rominger
266a4e71c5 Merge pull request #4027 from AlanCoding/ryan_hosts_36
[alan] use a computed inventory field for task impact math
2019-12-09 14:51:02 -05:00
softwarefactory-project-zuul[bot]
c29afce54d Merge pull request #5472 from AlanCoding/ryan_hosts
Use total_hosts for task impact for performance

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-09 18:07:58 +00:00
Apurva Bakshi
eddee456b3 Add dataCy attribute to select-user-role and select-team-role buttons 2019-12-09 12:16:27 -05:00
AlanCoding
be5a12a318 Compute fields in smart task_impact tests 2019-12-09 11:46:12 -05:00
softwarefactory-project-zuul[bot]
e131e8c151 Merge pull request #5466 from jakemcdermott/hooks-organization-add
Migrate organization add to functional component

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-09 16:07:50 +00:00
softwarefactory-project-zuul[bot]
d30ecaa7e3 Merge pull request #5468 from Spredzy/playbook_modern_syntax
ansible playbook: move to modern syntax

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-09 16:07:46 +00:00
AlanCoding
dfc4a0c0e0 Compute fields in smart task_impact tests 2019-12-09 10:59:59 -05:00
mabashian
86ba1639c3 Pull in latest version of our angular-scheduler fork to fix Tueday/Tuesday typo 2019-12-09 10:43:15 -05:00
Ryan Petrello
f1b4e24833 use a computed inventory field for task impact math
see: https://github.com/ansible/tower/issues/4022
2019-12-09 09:38:24 -05:00
Yanis Guenane
35d36a71c7 ansible playbook: move to modern syntax
Signed-off-by: Yanis Guenane <yguenane@redhat.com>
2019-12-09 09:01:49 +01:00
Apurva Bakshi
eadcbe1ce9 Add the data-Cy attribute for having better selector for tests 2019-12-06 16:14:01 -05:00
softwarefactory-project-zuul[bot]
f0198105c4 Merge pull request #5460 from AlanCoding/fix_detached
Fix project sync errors when project branch is commit

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-06 20:04:21 +00:00
Jake McDermott
e34c7acdc4 Migrate organization add to functional component 2019-12-06 14:54:29 -05:00
Ryan Petrello
001d469bd0 Merge pull request #4026 from AlanCoding/fix_detached_36
[3.6.2] Fix project sync errors when project branch is commit
2019-12-06 14:33:10 -05:00
softwarefactory-project-zuul[bot]
a9e5981cfe Merge pull request #5464 from ryanpetrello/iso-check-timeout
fix a bug in isolated check timeout handling

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-06 19:21:08 +00:00
Ryan Petrello
b36b6978fb Merge pull request #4024 from ryanpetrello/iso-check-timeout-362
fix a bug in isolated check timeout handling
2019-12-06 14:00:05 -05:00
AlanCoding
55a19ffe6a Fix project sync errors when project branch is commit 2019-12-06 13:04:04 -05:00
Ryan Petrello
c4d358b870 use a computed inventory field for task impact math
see: https://github.com/ansible/tower/issues/4022
2019-12-06 12:56:18 -05:00
Ryan Petrello
5ae7df7757 fix a bug in isolated check timeout handling 2019-12-06 12:51:24 -05:00
Ryan Petrello
220168f5ee fix a bug in isolated check timeout handling 2019-12-06 12:44:50 -05:00
beeankha
3cc9139c6d Add a more specific assertion statement 2019-12-05 16:49:23 -05:00
softwarefactory-project-zuul[bot]
01161c7afd Merge pull request #5447 from jakemcdermott/fix-job-tab-placement
Update job redirects and tab placement

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-05 21:24:06 +00:00
beeankha
6d595cbda6 Add extra_vars parameter to tower_job_template module 2019-12-05 15:37:00 -05:00
AlanCoding
419d32d3e3 Fix project sync errors when project branch is commit 2019-12-05 14:26:24 -05:00
beeankha
334c63388b Revert original quotation mark configuration for non-approval default msg and body 2019-12-05 12:11:56 -05:00
beeankha
32f6f87463 Change quotation mark format for sconsistency and also to comply with qa tests 2019-12-05 12:11:50 -05:00
beeankha
0d92b2e703 Enable approval-related email notifications to send properly 2019-12-05 12:11:43 -05:00
Jake McDermott
bc6d879976 Merge pull request #4015 from mabashian/4013-backport-angular-upgrade
[3.6.2 backport] Upgrades angular and auxiliary deps to 1.7.9
2019-12-05 11:34:28 -05:00
Ryan Petrello
9bae9d32c7 Merge pull request #4020 from ryanpetrello/always_release_lock-3-6-2
fix situation were error happened before lock was released
2019-12-05 11:32:38 -05:00
Ryan Petrello
b5724adae5 fix situation were error happened before lock was released 2019-12-05 11:30:12 -05:00
softwarefactory-project-zuul[bot]
1048baa98c Merge pull request #5457 from AlanCoding/always_release_lock
Fix situation were error happened before lock was released

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-05 16:17:28 +00:00
AlanCoding
922ea67541 Fix situation were error happened before lock was released 2019-12-05 10:41:23 -05:00
softwarefactory-project-zuul[bot]
3d105e3b7a Merge pull request #5442 from rooftopcellist/translations
Remove outdated Zanata script as part of migration to Memsource

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-05 15:06:45 +00:00
softwarefactory-project-zuul[bot]
aceef98601 Merge pull request #5444 from dsesami/add-crypto-akit
Added cryptography to awxkit deps

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-05 14:46:49 +00:00
Christian Adams
d41322c63c Remove outdated Zanata translation script as part of migration to Memsource
- adds requirement for gettext to dev container for use by Memsource automation
2019-12-05 09:29:33 -05:00
beeankha
d6e5eb356b Revert original quotation mark configuration for non-approval default msg and body 2019-12-05 09:25:32 -05:00
beeankha
b46a2b43b0 Change quotation mark format for sconsistency and also to comply with qa tests 2019-12-05 09:25:23 -05:00
beeankha
6f54044cc6 Enable approval-related email notifications to send properly 2019-12-05 09:25:10 -05:00
Marliana Lara
5d1f322cd1 Add Inventory Groups list 2019-12-05 08:55:50 -05:00
Daniel Sami
c11a8b8ae1 added cryptography as optional awxkit dep
added cryptography as optional dep
2019-12-05 08:52:59 -05:00
mabashian
2d4df3d50e Fixes bug where permissions checkboxes had inverse effect after upgrading angular to 1.7.x. 2019-12-05 07:34:14 -05:00
softwarefactory-project-zuul[bot]
715483c669 Merge pull request #5379 from mabashian/ui-next-workflows
Workflows pt 1: The Rendering

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-04 23:52:06 +00:00
softwarefactory-project-zuul[bot]
30f65f38a7 Merge pull request #5436 from mabashian/upgrade-angular-1.7.9
Upgrades angular and auxiliary deps to 1.7.9

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-04 23:33:58 +00:00
softwarefactory-project-zuul[bot]
aaf093b0e0 Merge pull request #5451 from shanemcd/fix-project-dir-perms
Fix permissions on projects directory by pre-creating it

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-04 23:20:57 +00:00
mabashian
bd7248d21c Properly show template add button and options based on OPTIONS response on both job_templates and workflow_job_templates endpoints. 2019-12-04 17:58:56 -05:00
mabashian
9bdd49bec5 Adds translations for missing strings in Toolbar.jsx 2019-12-04 17:58:56 -05:00
mabashian
2506db88f2 Ellipsis workflow node names that are too long to fit on the node 2019-12-04 17:58:56 -05:00
mabashian
61c38eabf8 Revert inadvertent variable name changes in old ui app 2019-12-04 17:58:56 -05:00
mabashian
37a1e5d9b0 First pass workflow viz 2019-12-04 17:58:56 -05:00
Shane McDonald
c439a1ec8f Fix permissions on projects directory by pre-creating it
This broke after
f78c9f357d
but people seem to like this functionality so instead of reverting it we can do this.
2019-12-04 17:39:38 -05:00
softwarefactory-project-zuul[bot]
a1d110aac7 Merge pull request #5249 from wenottingham/BSD-stands-for-berkeley-system-d
Move to using systemd for service management.

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-04 21:53:31 +00:00
Jake McDermott
c5e22f9aa3 Update job redirects and tab placement
Change display order of the output tab so that it is second and make
it the default for redirects and job launches.
2019-12-04 14:37:15 -05:00
softwarefactory-project-zuul[bot]
6dc5f91a0f Merge pull request #5445 from Spredzy/test_new_linter
Linters: test new linter job

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-04 19:31:15 +00:00
mabashian
8a9ebe2086 Fixes bug where permissions checkboxes had inverse effect after upgrading angular to 1.7.x. 2019-12-04 13:54:02 -05:00
mabashian
a859ecfbde Upgrades angular and auxiliary deps to 1.7.9 2019-12-04 13:54:02 -05:00
Bill Nottingham
02fd26520d Move to using systemd for service management. 2019-12-04 13:40:21 -05:00
Yanis Guenane
f8b2bcbae7 Linters: test new linter job
Depends-On: https://github.com/ansible/zuul-jobs/pull/40
2019-12-04 19:05:17 +01:00
softwarefactory-project-zuul[bot]
f5157784c4 Merge pull request #5440 from routenull0/inventory_misspelling
'install' was misspelled as 'isntall'

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-04 16:04:22 +00:00
mabashian
93b49f314d Upgrades angular and auxiliary deps to 1.7.9 2019-12-04 11:03:24 -05:00
Ryan Petrello
55d81cf74d Merge pull request #4012 from ryanpetrello/monkey-patch-oauth2-side-effect-362
fix an nuanced bug which can cause OAuth2 migrations to fail
2019-12-04 10:59:18 -05:00
softwarefactory-project-zuul[bot]
f629822596 Merge pull request #5437 from ryanpetrello/monkey-patch-oauth2-side-effect
fix an nuanced bug which can cause OAuth2 migrations to fail

Reviewed-by: Seth Foster
             https://github.com/fosterseth
2019-12-04 15:32:39 +00:00
routenull0
bf2a4d1a2c 'install' was misspelled as 'isntall' 2019-12-04 09:09:24 -06:00
Ryan Petrello
afadfa939d fix an nuanced bug which can cause OAuth2 migrations to fail 2019-12-04 09:55:55 -05:00
softwarefactory-project-zuul[bot]
02c3e1c32f Merge pull request #5420 from Spredzy/yamllint
yamllint: Make all files in awx pass yamllint

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-04 13:42:56 +00:00
Ryan Petrello
a0d20a5d50 fix an nuanced bug which can cause OAuth2 migrations to fail 2019-12-04 08:38:22 -05:00
softwarefactory-project-zuul[bot]
b8d27d53b8 Merge pull request #4988 from sjha4/inventory_foreman
Update foreman inventory to use foreman's inventory report

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-04 01:11:23 +00:00
softwarefactory-project-zuul[bot]
878659cded Merge pull request #5434 from jakemcdermott/fix-3882-3
Only show cred type test button to superusers.

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-03 21:19:52 +00:00
softwarefactory-project-zuul[bot]
027ce7fbdb Merge pull request #5431 from chrismeyersfsu/revive-updater.sh
keep *-devel package in the dev container

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-03 20:38:21 +00:00
Jake McDermott
540f8ab7d6 Only show cred type test button to superusers.
Without a credential instance, plugin tests go through the credential
type (not credential) api. Since this endpoint is for superusers only,
we hide the test button when non-superusers are adding a new external
credential.
2019-12-03 15:23:02 -05:00
softwarefactory-project-zuul[bot]
0362c88e48 Merge pull request #5095 from jakemcdermott/fix-3882-cred-test-perms
Allow some non-superusers to test credential plugins

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-03 19:56:44 +00:00
chris meyers
129374a1c2 keep *-devel package in the dev container
* requirements/updater.sh does pip magic. In doing this magic, devel
system packages are required to download/install/build. This change
ensures those dev packages are available.
2019-12-03 14:31:09 -05:00
Jake McDermott
63fd546f44 Let cred admins and users test credential plugins 2019-12-03 13:36:18 -05:00
Ryan Petrello
9856c9154e Merge pull request #3999 from ryanpetrello/fix-system-jobs-362
fix a few bugs with the session and oauth2 cleanup scheduled jobs
2019-12-03 12:08:03 -05:00
Michael Abashian
e7a712394a Merge pull request #4001 from mabashian/backport-awx-5411
[3.6.2] Backport WFJT inventory rbac fix
2019-12-03 10:23:30 -05:00
Ryan Petrello
208e36f83b add an awx-manage command for re-generating SECRET_KEY 2019-12-02 18:07:22 -05:00
Shane McDonald
68a6984fcd Merge pull request #5422 from shanemcd/oc-ugh
Generic method of detecting k8s api version
2019-12-02 12:49:40 -05:00
Shane McDonald
a90e0e8834 Merge pull request #5425 from shanemcd/memcached-hostname
Fix memcached_hostname bug in k8s installs
2019-12-02 12:45:04 -05:00
Shane McDonald
8ab6a79b37 Fix memcached_hostname bug in k8s installs
Fallout from https://github.com/ansible/awx/pull/5340.
2019-12-02 12:36:57 -05:00
Shane McDonald
e68d576fd2 Generic method of detecting k8s api version
Related: https://github.com/ansible/awx/issues/5388
2019-12-02 11:45:20 -05:00
Yanis Guenane
ca247182df yamllint: Make all files in awx pass yamllint
This commit updates all files that weren't passing yamllint for them to
pass.

A new yamllint target has been added. One can run `tox -e yamllint` or
`yamllint -s .` locally to ensure yaml files are still passing.

This check will be enabled in the CI so it can get on every new
contributions, and prevent merging non-compliant code.

Signed-off-by: Yanis Guenane <yguenane@redhat.com>
2019-12-02 15:12:51 +01:00
softwarefactory-project-zuul[bot]
1f628778bb Merge pull request #5418 from shanemcd/downstream-k8s-changes
Pull in downstream changes to k8s installer

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-12-02 13:44:29 +00:00
Shane McDonald
dcbb2813b5 Pull in downstream changes to k8s installer 2019-12-02 08:12:51 -05:00
Marliana Lara
9cdb281f06 Check inventory use permissions to disable workflow inventory lookup 2019-11-27 16:42:57 -05:00
softwarefactory-project-zuul[bot]
8116ec8e1f Merge pull request #5411 from marshmalien/5338-wf-inventory-lookup-permissions
Check inventory use permissions to disable workflow inventory lookup

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-27 19:18:55 +00:00
Ryan Petrello
c373420982 fix incorrect SystemJob.job_type choices 2019-11-27 12:37:24 -05:00
mabashian
93a9a0354f Adds missing semicolons to make linter happy 2019-11-27 12:37:21 -05:00
mabashian
ee6e28e066 Only show the days to keep input on the scheduler for system jobs that require it. Hides this field for cleaning up tokens and sessions. 2019-11-27 12:37:16 -05:00
Ryan Petrello
ea5d429399 fix a few bugs with the session and oauth2 cleanup scheduled jobs
see: https://github.com/ansible/tower/issues/3940
2019-11-27 12:37:10 -05:00
softwarefactory-project-zuul[bot]
3b49dd78bf Merge pull request #5392 from ryanpetrello/fix-system-jobs
fix a few bugs with the session and oauth2 cleanup scheduled jobs

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-27 17:25:56 +00:00
Marliana Lara
42b019d8c8 Check inventory use permissions to disable workflow inventory lookup 2019-11-27 11:36:51 -05:00
Ryan Petrello
a1af4e1808 Merge pull request #3997 from ryanpetrello/fix-busted-tests-362
fix busted tests for 3.6.2
2019-11-27 09:22:39 -05:00
Ryan Petrello
ffdcb2f8eb fix busted tests 2019-11-27 08:37:30 -05:00
Ryan Petrello
7b5f4f51fb Merge pull request #3990 from AlanCoding/sanity_fixes_36
Fix duplicate exception sanity error
2019-11-27 08:35:52 -05:00
Ryan Petrello
25c2b9610a Merge pull request #3993 from ryanpetrello/fix-asgi-bug-362
bump asgi-amqp dependency
2019-11-27 08:03:05 -05:00
softwarefactory-project-zuul[bot]
5935583c4c Merge pull request #5409 from ryanpetrello/bump-asgi-amqp
bump asgi-amqp dependency

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-27 10:09:05 +00:00
Ryan Petrello
68f17eb370 bump asgi-amqp dependency 2019-11-26 23:33:24 -05:00
Ryan Petrello
1ad8a49155 bump asgi-amqp dependency 2019-11-26 23:29:50 -05:00
Ryan Petrello
47ed5ef848 fix incorrect SystemJob.job_type choices 2019-11-26 23:13:39 -05:00
mabashian
a56686ca77 Adds missing semicolons to make linter happy 2019-11-26 15:59:19 -05:00
softwarefactory-project-zuul[bot]
211786976d Merge pull request #5403 from AlanCoding/duplicate_except
Fix duplicate exception sanity error (and a few others)

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-26 20:14:36 +00:00
mabashian
7e82f0fad7 Only show the days to keep input on the scheduler for system jobs that require it. Hides this field for cleaning up tokens and sessions. 2019-11-26 15:05:45 -05:00
softwarefactory-project-zuul[bot]
8612bf79e8 Merge pull request #5295 from rooftopcellist/analytics_user_agent
Add User-Agent to analytics upload POST

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-26 20:04:17 +00:00
softwarefactory-project-zuul[bot]
78edf51803 Merge pull request #5397 from rooftopcellist/consolidate_canceled
Make spelling of canceled consistent

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-26 19:55:53 +00:00
Ryan Petrello
632810f3a8 fix a few bugs with the session and oauth2 cleanup scheduled jobs
see: https://github.com/ansible/tower/issues/3940
2019-11-26 13:17:46 -05:00
AlanCoding
695eab1fdd fix duplicate exception sanity error 2019-11-26 09:47:07 -05:00
AlanCoding
081a0fc04e fix other assorted sanity failures 2019-11-26 09:45:54 -05:00
AlanCoding
48f10669d6 fix duplicate exception sanity error 2019-11-26 09:39:05 -05:00
Christian Adams
4f8b624b96 Make spelling of canceled consistent 2019-11-26 00:31:15 -05:00
softwarefactory-project-zuul[bot]
c87c0aa712 Merge pull request #5395 from AlanCoding/you_are_migrating
Raise specific exception if migration in progress

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-26 03:40:43 +00:00
Christian Adams
05e6f4ab3c Add User-Agent to analytics upload POST 2019-11-25 22:22:05 -05:00
AlanCoding
1a85874964 Raise specific exception if migration in progress 2019-11-25 21:48:05 -05:00
softwarefactory-project-zuul[bot]
6f2224c8e5 Merge pull request #5393 from shanemcd/fix-version-in-dev
Fix VERSION reported in dev container

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-25 22:36:22 +00:00
softwarefactory-project-zuul[bot]
57e155f0f9 Merge pull request #5396 from wenottingham/even-cleaner
Clean up some stuff in awxkit with `make clean`

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-25 21:40:50 +00:00
softwarefactory-project-zuul[bot]
a6924c1bcf Merge pull request #5394 from beeankha/fix_error
Make Integration Tests Pass w/ Manual SCM Type

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-25 21:10:42 +00:00
Bill Nottingham
4acb28f6f5 Clean up some stuff in awxkit with make clean 2019-11-25 15:55:22 -05:00
beeankha
3ed5d6ec65 Make integrations pass even with manual SCM type 2019-11-25 15:16:41 -05:00
Shane McDonald
15bcea7301 Fix VERSION reported in dev container 2019-11-25 12:56:49 -05:00
Shane McDonald
ce8c0066d0 Fix downstream tests
I backported how we do the VERSION detection in 3.5.something. This should
already be fixed upstream.
2019-11-25 12:55:18 -05:00
softwarefactory-project-zuul[bot]
bdd63f36a8 Merge pull request #5366 from marshmalien/inv-hosts-list
Inventory Host List

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-25 17:39:33 +00:00
Shane McDonald
24abc1462f Merge pull request #3985 from shanemcd/fix-cache
Fix Docker build caching
2019-11-25 11:41:33 -05:00
Shane McDonald
12363ae175 Fix Docker build caching
The flow will need to be:

- Pre-pull image you want to use
- Re-tag as image:$(COMPOSE_TAG)
- COMPOSE_TAG=mytag make docker-compose-build
2019-11-25 11:32:41 -05:00
Marliana Lara
1b50895738 Use short circuit operator in favor of ternary conditional 2019-11-25 11:17:37 -05:00
Ryan Petrello
1fbae00e37 Merge pull request #3983 from ryanpetrello/fix-3980
Remove usage of idle_timeout when checking status of isolated / containerized jobs
2019-11-25 11:15:35 -05:00
Samir Jha
b10a71786b Update foreman inventory to use foreman's inventory report 2019-11-25 10:48:54 -05:00
Ryan Petrello
0d659b0111 Merge pull request #3984 from ryanpetrello/fix-3978
Adds link to docs on container groups add/edit forms
2019-11-25 10:42:59 -05:00
softwarefactory-project-zuul[bot]
deb8714987 Merge pull request #5390 from ryanpetrello/downstream-security
merge in a few downstream fixes

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-25 15:38:25 +00:00
mabashian
ee8775a08d Adds link to docs on container groups add/edit forms to match instance groups. Updates instance groups link. 2019-11-25 10:35:23 -05:00
Shane McDonald
31650bb0bd Remove usage of idle_timeout when checking status of isolated / containerized jobs 2019-11-25 10:31:33 -05:00
Ryan Petrello
cbf085ab43 move migrations to accomdate a downstream migration 2019-11-25 10:06:12 -05:00
Ryan Petrello
78d715efed merge downstream branch 2019-11-25 10:05:05 -05:00
softwarefactory-project-zuul[bot]
2cb5b0563b Merge pull request #5377 from dsesami/new-ids
Updated some selector attributes for E2E

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-25 14:00:50 +00:00
Daniel Sami
0eb55f5038 Updated contributing doc with notes about E2E 2019-11-22 15:24:35 -05:00
Daniel Sami
daf3bbc7ef Added E2E-friendly selectors to nav and jt details
lint

updated snapshot
2019-11-22 15:12:43 -05:00
softwarefactory-project-zuul[bot]
caa6d0c4d3 Merge pull request #5382 from shanemcd/take-your-time
Remove usage of idle_timeout when checking status of isolated / containerized jobs

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-22 18:37:14 +00:00
softwarefactory-project-zuul[bot]
bc7ae4ca46 Merge pull request #5173 from AlanCoding/collection_credentials
Finish JT.credentials functionality, add test

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-22 18:07:14 +00:00
Shane McDonald
db2316b791 Remove usage of idle_timeout when checking status of isolated / containerized jobs 2019-11-22 11:41:00 -05:00
softwarefactory-project-zuul[bot]
b7efd5a9ab Merge pull request #5332 from AlexSCorey/InventoryAdd/EditForm
Adds Add/Edit Inventory and Inventory Form

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-22 16:00:35 +00:00
Alex Corey
83caf99c58 Improves Tests and addresses other PR Issues 2019-11-22 10:22:05 -05:00
softwarefactory-project-zuul[bot]
285fb2582e Merge pull request #5378 from AlanCoding/run_notes
Add notes for ./upgrade.sh

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-22 00:49:40 +00:00
Alex Corey
19180a1bc4 Adds Add/Edit Inventory and Inventory Form 2019-11-21 17:40:28 -05:00
AlanCoding
9c86f521e9 Add notes for ./upgrade.sh 2019-11-21 15:52:47 -05:00
David Moreau Simard
2171823846 Finish JT.credentials functionality, add test
Original commit:

commit 3ec6196477135230c4b90b175310bdc2eaff36ed
Author: David Moreau Simard <dmsimard@redhat.com>
Date:   Tue Oct 23 22:21:33 2018 -0400

    Add support for "credentials" in the tower_job_template module

    Job templates might require more than one credential.
    There's credential, vault_credential, machine_credential, etc.
    "credentials" is a thing, let's support it.
2019-11-21 14:30:08 -05:00
softwarefactory-project-zuul[bot]
c4143b0111 Merge pull request #5373 from rooftopcellist/rm_analytics_values
Set default values for analytics settings

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-21 18:21:40 +00:00
Christian Adams
94fa4deab3 Set default values for analytics settings 2019-11-21 12:29:15 -05:00
softwarefactory-project-zuul[bot]
53aadd3b96 Merge pull request #5372 from mabashian/ig-docs-links
Instance groups links to docs

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-21 17:08:21 +00:00
Marliana Lara
faa0802d97 Update breadcrumb and fetch new hosts when url changes 2019-11-21 11:36:18 -05:00
Marliana Lara
fa144aa98f Add Inventory Host list and unit tests
* Add Inventory Host Add route
* Fix host disabled loading switch bug
2019-11-21 11:36:17 -05:00
Marliana Lara
ea4e98c52a Move Switch into shared component directory and update tests 2019-11-21 11:36:12 -05:00
mabashian
8ff413efc0 Adds link to docs on container groups add/edit forms to match instance groups. Updates instance groups link. 2019-11-21 11:05:06 -05:00
softwarefactory-project-zuul[bot]
804a3c17bf Merge pull request #5354 from wenottingham/isolation-desolation--let-it-go
Remove obsolete requirements file.

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-21 13:35:09 +00:00
softwarefactory-project-zuul[bot]
da5eb710cd Merge pull request #5358 from ilijamt/patch-1
Allow deployments on Kubernetes version 1.16

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-21 13:27:15 +00:00
softwarefactory-project-zuul[bot]
57f9b31b2b Merge pull request #5163 from AlanCoding/custom_venv
Add custom virtualenv param to various modules

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-21 11:45:19 +00:00
softwarefactory-project-zuul[bot]
34ba858e3b Merge pull request #5191 from AlanCoding/tower_group_id
Make tower_group idempotent

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-21 04:26:16 +00:00
AlanCoding
5e24cee0ae Add custom_virtualenv param to inventory source and tests 2019-11-20 22:39:12 -05:00
Mathieu Mallet
a026838f77 ansible_tower: Add custom_virtualenv attribute when applicable (#60200)
In Ansible Tower/AWX, there are three kinds of objects that can be tied
to custom python virtual environment:
  - job template
  - project
  - organization

This patch updates the three ansible modules that creates those objects
so that the 'custom_virtualenv' attribute can be set if specified.

Testing Done: via a playbook, test organization, projet then template creation
without any 'custom_virtualenv' attribute specified. Check that the
resources get created and that their python env is set to default. Then
re-do the same test but this time with the 'custom_virtualenv' attribute
specified. Ensure in AWX UI that those resources have the right
'custom_virtualenv' set.
2019-11-20 22:35:25 -05:00
Ilija Matoski
e2cd86089b Allow deployments on Kubernetes version 1.16 2019-11-20 23:54:10 +01:00
softwarefactory-project-zuul[bot]
85d5387f31 Merge pull request #5350 from svenstaro/patch-1
Use more modern version of OpenShift client

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-20 21:56:42 +00:00
softwarefactory-project-zuul[bot]
cf13a1b70a Merge pull request #5359 from martin-adema/devel
Configurable image registry and service account for Postgres deployment.

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-20 21:25:19 +00:00
softwarefactory-project-zuul[bot]
fed6a86170 Merge pull request #5194 from AlanCoding/inventory_organization
Add organization parameter to tower_inventory_source (and add test logging)

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-20 18:23:27 +00:00
softwarefactory-project-zuul[bot]
c8907fb39d Merge pull request #5362 from beeankha/fix_typo
Fix Typo for Project Option

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-20 16:48:00 +00:00
softwarefactory-project-zuul[bot]
162e4aeec4 Merge pull request #5361 from mabashian/logout-id
Adds id attribute to logout button

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-20 16:15:53 +00:00
beeankha
c6d2fa86c7 Fix typo under 'options' 2019-11-20 11:15:18 -05:00
mabashian
f89db3586b Adds id attribute to logout button 2019-11-20 10:42:37 -05:00
softwarefactory-project-zuul[bot]
19742859b6 Merge pull request #5343 from beeankha/wait_for_project_sync
Make tower_project.py Wait for Project Sync

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-20 15:22:56 +00:00
beeankha
eff46dbc71 Make playbook wait for project sync...
...*before* running the associated job template.

Set "wait" default to True so CI doesn't time out

Change default back to "False", put in new "if"...

...block, explicitly set "wait" to "False" in test file.

Change if block

Update README

Update 'wait' option description
2019-11-20 08:43:19 -05:00
Martin Adema
80b75a163a Configurable image registry and service account for Postgres deployment. 2019-11-20 14:15:59 +01:00
Bill Nottingham
fe65073f3e Remove obsolete requirements file. 2019-11-19 16:44:55 -05:00
softwarefactory-project-zuul[bot]
6f2b10daf5 Merge pull request #5344 from AlanCoding/remove_git
Remove forks no longer needed from dependencies

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-19 21:31:24 +00:00
softwarefactory-project-zuul[bot]
ad3d89afd3 Merge pull request #5340 from eb4x/memcached_hostname
Consistent naming scheme; memcached_host -> memcached_hostname

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-19 16:43:20 +00:00
softwarefactory-project-zuul[bot]
e3c2c310ef Merge pull request #5352 from ryanpetrello/more-downstream-fixes
merge in a few downstream fixes

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-19 15:19:36 +00:00
Ryan Petrello
c574cdc7dc fix busted tests 2019-11-19 09:21:04 -05:00
Ryan Petrello
097b59e74a fix a bug that broken custom approval notification messages 2019-11-19 08:55:26 -05:00
Alan Rominger
8e7d607a47 Only turn off Galaxy cert verification via toggle (#3933) 2019-11-19 08:54:40 -05:00
Sven-Hendrik Haase
4c32faa448 Use more modern version of OpenShift client
3.9 is pretty old by now.
2019-11-19 13:29:06 +01:00
Ryan Petrello
17509d560d Merge pull request #3955 from AlanCoding/lets_release
Backport collection fixes
2019-11-18 16:25:04 -05:00
AlanCoding
7b1b656455 Remove forks no longer needed from dependencies 2019-11-18 15:39:39 -05:00
Ryan Petrello
268b22c550 Merge pull request #3953 from ryanpetrello/more-container-group-fixes
[3.6.1] fix a few bugs related to container group execution
2019-11-18 14:27:18 -05:00
Ryan Petrello
b525d0a6f4 Merge pull request #3967 from ryanpetrello/fix-cli-doc-typos
fix a typo in the CLI usage docs
2019-11-18 11:26:24 -05:00
softwarefactory-project-zuul[bot]
c7cabfa785 Merge pull request #5206 from keithjgrant/4966-project-auto-select
make ProjectLookup auto-select project if only one found

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-18 16:23:38 +00:00
Ryan Petrello
9c2797b34c fix a typo in the CLI usage docs 2019-11-18 11:21:34 -05:00
Ryan Petrello
732f7d2292 Merge pull request #3962 from ryanpetrello/license-pass
don't set rh_username and rh_password in the license upload
2019-11-18 11:03:36 -05:00
Erik Berg
f5fc0871fc Consistent naming scheme; memcached_host -> memcached_hostname
environment.sh uses hostname for everything, and both environment and
credentials provide a default of 'memcached', so this should also be one less
variable to care about.
2019-11-18 15:22:12 +00:00
Ryan Petrello
9458741b72 don't set rh_username and rh_password in the license upload 2019-11-18 10:02:31 -05:00
softwarefactory-project-zuul[bot]
a1f7f967e3 Merge pull request #5339 from eb4x/rabbitmq_hostname
Consistent naming scheme; rabbitmq_host -> rabbitmq_hostname

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-18 14:38:25 +00:00
Erik Berg
91c78d7137 Consistent naming scheme; rabbitmq_host -> rabbitmq_hostname
environment.sh uses hostname for everything, and both environment and
credentials provide a default of 'rabbitmq', so this should be one less
variable to care about.
2019-11-18 13:46:43 +00:00
softwarefactory-project-zuul[bot]
b88f4ce27c Merge pull request #5325 from AlanCoding/inputs_inputs
Delay inputs comparision to after field validation

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-16 01:30:36 +00:00
softwarefactory-project-zuul[bot]
e8606d9478 Merge pull request #5301 from AlanCoding/galaxy_ignore
Add ignore list for Ansible 2.10

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-16 01:22:53 +00:00
softwarefactory-project-zuul[bot]
90d38a50de Merge pull request #5313 from fosterseth/fix-5044-azureinventory
Fix filtering azure inventory based on user-specified tags

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-15 21:51:09 +00:00
softwarefactory-project-zuul[bot]
a83164cca6 Merge pull request #5336 from shanemcd/note-on-upgrading
Add note on upgrading to install.md

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-15 20:58:05 +00:00
Seth Foster
e1e7e9047d Fix filtering azure inventory based on user-specified tags
Users can specify a list of keys, or a list of key:value pairs under
source variables. e.g. tags: Creator, peanutbutter or tags:
Creator:jmarshall, peanutbutter:jelly. If provided, only hosts that have
all keys or key:value pairs in the list will be returned.

inventory.py sets up the azure_rm.yml that provides
exclude_host_filters. This code adds a line for each key in the list, as
well as an additional line in the case of a key:value pair.

e.g.

exclude_host_filters:
- "'Creator' not in tags.keys()"
- tags['Creator'] != 'jmarshall'

Each line is a conditional, and if any conditionals is true, then the
host is filtered out.

fix for issue # 5044
2019-11-15 14:18:20 -05:00
Shane McDonald
094eef635d Fix typo 2019-11-15 14:17:08 -05:00
Shane McDonald
56bb82e303 Fix ordering 2019-11-15 14:14:54 -05:00
Shane McDonald
0290dd3246 Regenerate table of contents 2019-11-15 14:11:10 -05:00
Shane McDonald
de8c46cab0 Remove obsolote docker-compose instructions 2019-11-15 14:04:05 -05:00
Shane McDonald
9028a48ab2 Add a note on upgrading 2019-11-15 14:03:46 -05:00
Shane McDonald
709fa74070 Fix verbiage in INSTALL.md 2019-11-15 14:01:28 -05:00
softwarefactory-project-zuul[bot]
5342faa997 Merge pull request #5333 from m33k/JoeR/docker_module_update
CHANGED: docker_service to docker_compose

Reviewed-by: Ryan Petrello
             https://github.com/ryanpetrello
2019-11-15 17:31:16 +00:00
Joe
f0865d69f0 CHANGED: docker_service to docker_compose
Signed-off-by: Joe <11597133+m33k@users.noreply.github.com>
2019-11-15 16:28:05 +00:00
AlanCoding
ddf9fd581e Run and fix all sanity tests 2019-11-14 20:56:06 -05:00
James Vornhagen
b6745db4b8 update documentation example
missing k in workflow.

- Docs Pull Request

+label: docsite_pr
2019-11-14 20:56:05 -05:00
AlanCoding
35a565d09f In tower_job_wait intentionally fail module for failure 2019-11-14 20:56:05 -05:00
AlanCoding
b878aed400 Add test coverage for launch with multiple prompted creds 2019-11-14 20:56:04 -05:00
AlanCoding
1961a8ba15 Fix and test for warning when creating project 2019-11-14 20:56:04 -05:00
Hideki Saito
b76018d6e0 Fix multibyte character handling issue for tower_job_wait #55585
Add multibyte hostname handling test as an integration test

Signed-off-by: Hideki Saito <saito@fgrep.org>
2019-11-14 20:56:04 -05:00
AlanCoding
a40398e6a1 Remove sanity exceptions no longer needed 2019-11-14 20:56:03 -05:00
AlanCoding
97e2fbbe27 Add collection test coverage for creating vault credential 2019-11-14 20:56:03 -05:00
Mathieu Mallet
bcbad06c10 tower_credential: Missing 'kind' attribute (#61324)
In the 'tower_credential' module, when the credential 'kind' is set to
'vault', the code expects the other parameter 'vault_id' to be set.
Unfortunately, in the module 'credential_type_for_v1_kind' method, the
'kind' parameter is popped, i.e. remove from the module dict of
parameters leading to the following error:

> Parameter 'vault_id' is only valid if parameter 'kind' is specified as
'vault'

Fixes: #45644, #61324

Testing Done: Manually create a playbook with a task as follow
  - name: Create vault with ID 'bar' exists
    tower_credential:
      name: Foobar vault
      organization: Foobar
      kind: vault
      vault_id: bar
      vault_password: foobar
2019-11-14 20:55:59 -05:00
AlanCoding
1c74773eac Delay inputs comparision to after field validation 2019-11-14 20:28:33 -05:00
softwarefactory-project-zuul[bot]
9701ac1804 Merge pull request #5329 from AlanCoding/rm_another_ignore
Run and fix all sanity tests

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-14 21:25:44 +00:00
softwarefactory-project-zuul[bot]
3d90c6dfcf Merge pull request #5319 from AlanCoding/just_one_letter
Update documentation example

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-14 19:48:26 +00:00
AlanCoding
1402a2c8a5 Run and fix all sanity tests 2019-11-14 14:45:16 -05:00
softwarefactory-project-zuul[bot]
6567ad612c Merge pull request #5170 from AlanCoding/py2_fix2
Fix multibyte character handling issue for tower_job_wait #55585

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-14 19:33:50 +00:00
Ryan Petrello
a15bf9ee41 fix a few bugs related to container group execution
see: https://github.com/ansible/awx/issues/5326
2019-11-14 14:00:48 -05:00
softwarefactory-project-zuul[bot]
da448f6a0b Merge pull request #5327 from ryanpetrello/more-container-group-fixes
fix a few bugs related to container group execution

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-14 18:59:06 +00:00
Ryan Petrello
513f54a422 fix a few bugs related to container group execution
see: https://github.com/ansible/awx/issues/5326
2019-11-14 13:23:38 -05:00
softwarefactory-project-zuul[bot]
05d9220b21 Merge pull request #5312 from ryanpetrello/322-migration-cleanup
remove a number of now-unnecessary 3.2 migrations

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-14 17:34:28 +00:00
softwarefactory-project-zuul[bot]
9bb9bc682f Merge pull request #5320 from shanemcd/rabbitmq-bump
Bump RabbitMQ version in k8s/openshift installs

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-14 13:43:23 +00:00
Shane McDonald
1d6f116687 Bump RabbitMQ version in k8s/openshift installs
See https://github.com/ansible/awx-rabbitmq/pull/13
2019-11-14 08:01:35 -05:00
James Vornhagen
9a9d53d17a update documentation example
missing k in workflow.

- Docs Pull Request

+label: docsite_pr
2019-11-13 22:00:18 -05:00
softwarefactory-project-zuul[bot]
755ffc9844 Merge pull request #5309 from marshmalien/remove-awx-pf-chip
Swap our Chip & ChipGroup components out for PatternFly components

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-13 21:04:30 +00:00
softwarefactory-project-zuul[bot]
0ffbb06427 Merge pull request #5315 from ryanpetrello/hostname-prevent-jinja
prevent the creation of Host names that contain Jinja

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-13 19:08:08 +00:00
Marliana Lara
c13c5b6c13 Hide overflow chip in filter tags component 2019-11-13 13:45:50 -05:00
Ryan Petrello
70979df36a prevent the creation of Host names that contain Jinja 2019-11-13 13:15:36 -05:00
Ryan Petrello
83ee39cabd remove a number of unnecessary 3.2 migrations 2019-11-13 11:46:24 -05:00
softwarefactory-project-zuul[bot]
b0d31a64aa Merge pull request #5288 from AlexSCorey/5270-InventoryAddLinks
Adds AddDropDownButton removes TemplateAddButton

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-12 21:08:56 +00:00
softwarefactory-project-zuul[bot]
06c53c14be Merge pull request #5233 from rebeccahhh/devel
Set policy variables to defaults when an instance group is containerized

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-12 19:55:41 +00:00
softwarefactory-project-zuul[bot]
a63778e40e Merge pull request #5212 from AlanCoding/project_tags
Use tags to reduce project update output

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-12 19:15:11 +00:00
Rebeccah
6f38edf9a3 add in testing to see that when an instance group is containerized that the policy field values are set to default 2019-11-12 13:13:34 -05:00
Rebeccah
1f05372ac9 change the logic to not break existing policy_instance testing 2019-11-12 13:13:34 -05:00
Rebeccah
d0327fc044 added onto the when saved function for instance groups that sets policy variables to their default. 2019-11-12 13:13:34 -05:00
Jake McDermott
068dab14d4 Increase async test timeout
When our CI system is overloaded, tests start running slower.
2019-11-12 13:04:00 -05:00
AlanCoding
f64d0dde5a Use tags to reduce project update output
Handle folder deletion as tag

remove -v use by default

Change meaning of roles_enabled playbook var to
 value of AWX global setting
2019-11-12 12:52:39 -05:00
Marliana Lara
7cc0041aa8 Remove our implementation of Chip and ChipGroup in favor of PatternFly's component 2019-11-12 12:44:56 -05:00
Shane McDonald
f66f24eb83 Merge pull request #4107 from gizero/docker-compose-fix-create_preload_data
ensure "create_preload_data" is honored in docker-compose deployments
2019-11-12 11:44:15 -05:00
Alex Corey
e3ee3c5a00 Fixes failing tests 2019-11-12 11:11:17 -05:00
Andrea Galbusera
1198c067b2 ensure "create_preload_data" is honored in docker-compose deployments
Use a templated version of launch_awx_task.sh which conditionally preloads
sample data according to create_preload_data value.
2019-11-12 10:44:27 -05:00
softwarefactory-project-zuul[bot]
d3ea09d60c Merge pull request #4819 from dgiorgio/devel
Fix docker-compose - Use variables to set docker postgres tag

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-12 15:34:46 +00:00
softwarefactory-project-zuul[bot]
c0abb063f9 Merge pull request #5001 from khawaga/kubernetes_ingress_tls
Kubernetes Ingress TLS support

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-12 15:34:40 +00:00
Alex Corey
95cdddd670 Adds Proptypes to AddDropDownButton Component
Also refactors to use the url that is passed in as the key for the link.
This means that we don't have to pass in a key value.
2019-11-12 10:21:10 -05:00
Alex Corey
d91aa8c6cf Allows AddDropDownButton components to accept array of dropdownItems 2019-11-12 10:21:10 -05:00
Alex Corey
052f101a70 Adds AddDropDownButton removes TemplateDropDown Button
Both Inventory List and Template List use the same add button that has a drop down.
I decided to make a component that both can use.
This also addresses a typo in a InventoryList test.
2019-11-12 10:21:10 -05:00
AlanCoding
c96e88877f Add ignore list for Ansible 2.10 2019-11-11 22:07:22 -05:00
softwarefactory-project-zuul[bot]
1564dfc80f Merge pull request #5272 from marshmalien/project-manual-subform
Add project manual subform

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-11 21:43:58 +00:00
softwarefactory-project-zuul[bot]
b0cb3ca9da Merge pull request #5297 from mabashian/eslint-plugin-react-hooks
Adds eslint-plugin-react-hooks as a dev dep

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-11 21:05:27 +00:00
softwarefactory-project-zuul[bot]
84b5fb89a3 Merge pull request #5296 from rayterrill/patch-1
Update custom_virtualenvs.md

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-11 20:59:09 +00:00
Marliana Lara
5319659d58 Address PR feedback
* Set local_path default value to empty string
* Make playbook directory required
* Update unit tests
2019-11-11 15:38:52 -05:00
softwarefactory-project-zuul[bot]
5d27c28b47 Merge pull request #5255 from mabashian/ui-next-users-list
Adds Users list, forms and details

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-11 20:04:05 +00:00
mabashian
68a6315626 Adds react-hooks eslint rules and fixes a few warnings 2019-11-11 14:56:25 -05:00
mabashian
8bfbd85cf9 Adds eslint-plugin-react-hooks as a dev dep 2019-11-11 14:25:04 -05:00
Ray Terrill
f7b6d9fdff Update custom_virtualenvs.md
Adding trailing slashes to API calls + directories. This caused me a ton of wasted time as the API call silently returns but does nothing without the trailing slash.
2019-11-11 11:05:16 -08:00
mabashian
ab4fba7ce9 Address PR feedback. Refactors a bit of unit test coverage to move away from testing state. Re-organized some of the structure of the user list tests to be slightly more efficient. 2019-11-11 11:57:39 -05:00
mabashian
deb6e58397 Adds Users list, forms and details. Adds password form field. 2019-11-11 10:43:56 -05:00
softwarefactory-project-zuul[bot]
4746bc7c09 Merge pull request #5119 from mabashian/ui-next-teams
Adds basic teams list and add/edit forms

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-08 21:28:04 +00:00
mabashian
823a74c98b Prettier 2019-11-08 14:18:44 -05:00
mabashian
c294a63f32 Updates a couple of team list checkbox tests to test the UI rather than state/props internals. Fixes bug where select all was selected on load. 2019-11-08 12:44:45 -05:00
mabashian
84bce530dc Adds organization field to team form. Adds edit button to team list items. 2019-11-08 12:44:45 -05:00
mabashian
6acd3c98b7 Updates stale copy pasta. Org -> Team 2019-11-08 12:44:45 -05:00
mabashian
1e80b2e295 Adds basic teams list and add/edit forms. The edit button on the list rows and the org lookup in the form are both missing and will be added in a later commit. 2019-11-08 12:44:45 -05:00
Marliana Lara
e4721d7722 Add project manual scm type subform 2019-11-07 13:30:30 -05:00
softwarefactory-project-zuul[bot]
0cea8121bb Merge pull request #5204 from AlexSCorey/5106-MissingOrDeletedFields
Adds `Deleted` text to missing resources in JT Details View

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-07 15:41:35 +00:00
Shane McDonald
eaac54040c Merge pull request #5192 from shanemcd/local-projects
Mount local projects directory inside of dev container.
2019-11-07 07:35:45 -05:00
Shane McDonald
763ac25b2e Merge pull request #5253 from shanemcd/stop-requiring-pg-admin-when-we-dont-need-it
Dont require pg_admin_password unless we're using it.
2019-11-06 14:20:55 -05:00
Shane McDonald
922723cf39 Merge pull request #5220 from JensPfeifle/patch-1
Update required Ansible version to 2.8+
2019-11-06 11:36:37 -05:00
softwarefactory-project-zuul[bot]
f216c8f90f Merge pull request #5180 from mabashian/ui-next-hosts
Add host list, add/edit forms, and details

Reviewed-by: Michael Abashian
             https://github.com/mabashian
2019-11-06 15:23:06 +00:00
mabashian
4e31bdd2d2 Removes changes to serializer that added type to host recent jobs. Addresses Switch styling issues on host list items. 2019-11-06 09:35:45 -05:00
mabashian
d5e9716ceb Move CardHeader styled component(s) outside of render functions. Refactors host options calls out to it's own function. 2019-11-06 09:35:45 -05:00
mabashian
01963b0ee7 Add host list, add/edit forms, and details 2019-11-06 09:35:45 -05:00
softwarefactory-project-zuul[bot]
a353f2a807 Merge pull request #5250 from jakemcdermott/fix-5234
fix flakey test

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-06 14:29:33 +00:00
Jake McDermott
69205c5f6b try a higher timeout ¯\_(ツ)_/¯
https://github.com/ansible/awx/issues/5234
https://github.com/ansible/awx/pull/5250
2019-11-06 08:24:35 -05:00
softwarefactory-project-zuul[bot]
941bba2ae0 Merge pull request #5252 from marshmalien/4299-fix-twbs-right-margin
Fix right side <body> spacing bug

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-05 23:06:08 +00:00
Ryan Petrello
ddccfaa6fe Merge pull request #5251 from ryanpetrello/py-tee-zee
update pytz to the latest version
2019-11-05 16:54:01 -05:00
Shane McDonald
24da2b78b8 Dont require pg_admin_password unless we're using it. 2019-11-05 15:26:48 -05:00
Marliana Lara
6fee0db17b Fix right margin and padding to 0px on body el 2019-11-05 14:48:04 -05:00
Ryan Petrello
b25fbc5266 update pytz to the latest version 2019-11-05 13:53:28 -05:00
softwarefactory-project-zuul[bot]
e6235a4046 Merge pull request #5225 from marshmalien/project-edit
Add Project Edit form

Reviewed-by: Marliana Lara <marliana.lara@gmail.com>
             https://github.com/marshmalien
2019-11-05 18:21:55 +00:00
Marliana Lara
0f32161df0 Pull credential api request outside of ProjectEdit 2019-11-05 12:40:34 -05:00
Alex Corey
b570c8ad2a Fixes linting issues 2019-11-04 16:26:01 -05:00
Alex Corey
9170aa184a Removes missingDetail prop and addresses a typo.
This removes `missingDetail` prop in favor of using styledComponent's className prop.
2019-11-04 14:42:00 -05:00
softwarefactory-project-zuul[bot]
68c26014cc Merge pull request #5226 from ryanpetrello/sphinx-busted
fix broken sphinx docs build

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-04 18:28:03 +00:00
Ryan Petrello
f049b61460 Merge pull request #3937 from ryanpetrello/approval-notification-bug
fix a bug that broken custom approval notification messages
2019-11-04 13:02:37 -05:00
Ryan Petrello
458ca69405 fix a bug that broken custom approval notification messages 2019-11-04 13:01:50 -05:00
Shane McDonald
8a4c85e473 Merge pull request #5231 from shanemcd/bump-version-9.0.1
Bump version to 9.0.1
2019-11-04 11:23:13 -05:00
Shane McDonald
09d883f94a Bump version to 9.0.1 2019-11-04 11:20:42 -05:00
Shane McDonald
9ef57ec510 Merge pull request #5227 from shanemcd/more-downstream-k8s-fallout-sorry-folks
More downstream k8s fallout, sorry folks
2019-11-04 11:19:15 -05:00
Shane McDonald
5be006f9d3 Merge pull request #5229 from shanemcd/setuid-bwrap
Set setuid bit on bwrap
2019-11-04 11:18:57 -05:00
Shane McDonald
089bafa5d4 Set setuid bit on bwrap
Related: https://github.com/ansible/awx/issues/5224
2019-11-04 11:10:09 -05:00
Shane McDonald
fa278f83ad Fix k8s installs using external db 2019-11-04 09:35:11 -05:00
Shane McDonald
0d68ca8f14 Allow for overriding container groups image from k8s installer 2019-11-04 09:35:03 -05:00
Ryan Petrello
2ec90f17d0 fix broken sphinx docs build 2019-11-04 09:32:04 -05:00
Marliana Lara
ecf340f722 Add Project Edit test coverage 2019-11-04 09:16:31 -05:00
Shane McDonald
713079bd70 Merge pull request #5218 from shanemcd/fix-k8s-nginx
Fix broken k8s installs
2019-11-04 08:57:48 -05:00
Marliana Lara
d77040a7a9 Add Project Edit form and refactor how the form handles credentials 2019-11-03 21:28:10 -05:00
Shane McDonald
d3b137fbc4 Fix broken k8s installs
Related: https://github.com/ansible/awx/issues/5205

The following commit introduced this bug:
712b07c136 (diff-a2ef2d6347894a92a6b882e3793fb06c)
2019-11-03 16:03:19 -05:00
JensPfeifle
857faf570d Update required Ansible version to 2.8+
Attempting to build the docker image with Ansible 2.5.1 results in the following error:
```
TASK [image_build : Build sdist builder image] *******************************************************************************************
fatal: [localhost -> localhost]: FAILED! => {"changed": false, "msg": "Unsupported parameters for (docker_image) module: build, force_source, source Supported parameters include: api_version, archive_path, buildargs, cacert_path, cert_path, container_limits, debug, docker_host, dockerfile, filter_logger, force, http_timeout, key_path, load_path, name, nocache, path, pull, push, repository, rm, ssl_version, state, tag, timeout, tls, tls_hostname, tls_verify, use_tls"}                                                                                 
```
The `force_source` parameter was added to docker_image in Ansible 2.8 (![source](https://docs.ansible.com/ansible/latest/modules/docker_image_module.html)).
2019-11-03 19:03:40 +01:00
softwarefactory-project-zuul[bot]
5246c842b2 Merge pull request #5203 from keithjgrant/warnings-cleanup
Cleanup console warnings/refactor

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-01 19:29:22 +00:00
softwarefactory-project-zuul[bot]
1dca4c9098 Merge pull request #5198 from AlanCoding/no_symlink
Remove venv symlink hack no longer needed

Reviewed-by: Alan Rominger <arominge@redhat.com>
             https://github.com/AlanCoding
2019-11-01 15:36:39 +00:00
softwarefactory-project-zuul[bot]
8cb32045f0 Merge pull request #5127 from marshmalien/project-add-form
Project add form

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-01 15:13:51 +00:00
softwarefactory-project-zuul[bot]
4962b729de Merge pull request #5199 from ryanpetrello/uuid-not-unique
make the callback receiver more robust to duplicate UUIDs from ansible

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-01 15:11:22 +00:00
softwarefactory-project-zuul[bot]
ed39a127e7 Merge pull request #5200 from ryanpetrello/quiet-celery
get rid of a loud celery error in the dev environment

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-01 15:06:00 +00:00
AlanCoding
c4b4a4c21a Remove venv symlink hack no longer needed 2019-11-01 09:46:27 -04:00
Ryan Petrello
bd81fda05c get rid of a loud celery error in the dev environment 2019-11-01 09:25:41 -04:00
Ryan Petrello
83550eeba0 make the callback receiver more robust to duplicate UUIDs from ansible 2019-11-01 09:24:52 -04:00
softwarefactory-project-zuul[bot]
4540cb653e Merge pull request #5208 from shanemcd/locale-fix
Install missing locales in dev container image.

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-11-01 13:24:11 +00:00
Shane McDonald
69597c5654 Sync Dockerfiles 2019-11-01 08:38:37 -04:00
Shane McDonald
fa61aef194 Install missing locales in dev container image. 2019-11-01 08:29:11 -04:00
Alan Rominger
871d87374b Only turn off Galaxy cert verification via toggle (#3933) 2019-10-31 22:56:48 -04:00
softwarefactory-project-zuul[bot]
e35f6b2acb Merge pull request #5202 from shanemcd/fix-docker-cache
Fix docker cache

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-10-31 23:32:11 +00:00
Keith Grant
b3e056fe55 make ProjectLookup auto-select project if only one found 2019-10-31 16:01:33 -07:00
Marliana Lara
a8140e86d7 Encapsulate each scm type subform in its own component 2019-10-31 19:00:55 -04:00
Alex Corey
e5b76c6427 Adds Deleted text to missing resources in JT Detials View
The usecase of this change is if a user deletes an Inventory, or a Project
that is used by a JT they need to know that those resources are missing.

The only time that `Deleted` won't be shown for a missing resource is for
Inventory if it has been marked Prompt on Launch then nothing is shown. in that field.

Also adds icon to indicate that a JT is missing resources on the JT List.
2019-10-31 18:02:13 -04:00
Shane McDonald
4d4ae84e32 Fix docker cache
This was causing the cache to miss on some docker versions, in addition to
throwing a warning that says it will break soon.
2019-10-31 16:57:17 -04:00
Marliana Lara
ae349addfe Resolve credential type id and retrieve scm_type choices from OPTIONS 2019-10-31 16:38:32 -04:00
Marliana Lara
31fdd5e85c Add scm refspec to project detail and remove org id from top level shared component 2019-10-31 16:38:32 -04:00
Marliana Lara
e4bde24f38 Add project add form and tests 2019-10-31 16:38:32 -04:00
Marliana Lara
9c019e1cc0 Add organization and credential lookups 2019-10-31 16:38:31 -04:00
softwarefactory-project-zuul[bot]
b3d298269b Merge pull request #5197 from wenottingham/tune-into-a-different-key
chmod the nginx cert/key/etc

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-10-31 18:53:40 +00:00
Keith Grant
21f7ca21e0 refactor dupe code to new TemplateAddButton component 2019-10-31 11:34:37 -07:00
softwarefactory-project-zuul[bot]
43bf370f8c Merge pull request #5195 from AlanCoding/job_fail_json
In tower_job_wait intentionally fail module for failure

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
2019-10-31 17:53:53 +00:00
Bill Nottingham
6057921e34 chmod the nginx cert/key/etc
EL8 OpenSSL defaults to 0600 for the key, which will not work in the
dev environment.
2019-10-31 13:53:13 -04:00
AlanCoding
5095816762 In tower_job_wait intentionally fail module for failure 2019-10-31 12:41:53 -04:00
AlanCoding
24eae09ed9 Make tower_inventory_source org optional, add tests 2019-10-31 09:46:49 -04:00
Hampus Lundqvist
a2fee252f9 fix tests and typos 2019-10-31 08:09:37 -04:00
Hampus Lundqvist
ab80c2276d support organization specific tower_inventory_source. Inventories can exists with same name across multiple organizations so we need to be able to select correct inventory, credential, project etc for the inventory_source 2019-10-31 08:09:20 -04:00
Shane McDonald
f78c9f357d Mount local projects directory inside of dev container.
Yesterday I noticed that we have awx/projects in our .gitignore. I am assuming
this pre-dates our containerized development environment. With this commit, any
project under awx/projects/ will be made available in the dev environment for
selection when creating a Manual project. This comes in super handy when
testing changes to playbooks locally.
2019-10-31 08:01:54 -04:00
AlanCoding
da1e43dc12 Make tower_group idempotent 2019-10-31 07:53:43 -04:00
Keith Grant
c1da74cbc0 fix PF switch styling 2019-10-30 15:06:42 -07:00
Hideki Saito
cb354c2ef1 Fix multibyte character handling issue for tower_job_wait #55585
Add multibyte hostname handling test as an integration test

Signed-off-by: Hideki Saito <saito@fgrep.org>
2019-10-30 14:11:49 -04:00
Ryan Petrello
4d0c567d73 Merge pull request #3911 from jbradberry/truncate-fix
Add the no_truncate parameter to the job and adhoc event sublist views
2019-10-28 16:08:29 -04:00
Jeff Bradberry
b48815d2bb Add the no_truncate parameter to the job and adhoc event sublist views
which are the ones that the CLI actually uses.
2019-10-28 12:51:14 -04:00
Jake McDermott
ad383cdb44 Merge pull request #3909 from jakemcdermott/fix-3578-comment
Improve code comment regarding handling of omitted events
2019-10-28 11:52:11 -04:00
Jake McDermott
91afa88b44 Improve accuracy of code comment 2019-10-28 11:28:57 -04:00
Ryan Petrello
b575fa4243 Merge pull request #3898 from ryanpetrello/k8s-messy-client
attempt to clean up cacert trash the k8s python client leaves behind
2019-10-28 11:09:24 -04:00
Alan Rominger
3cab73c574 Add tests for AWX collection credential fixes (#3893) 2019-10-28 09:58:00 -04:00
Ryan Petrello
b3af64d66f work around a bug in the k8s client that leaves trash in /tmp 2019-10-28 09:43:32 -04:00
Jake McDermott
1869b73826 Merge pull request #3906 from jakemcdermott/fix-5110
Fix job / skip tags not getting used on launch prompt
2019-10-28 09:15:08 -04:00
Ryan Petrello
cc47afa856 Merge pull request #3904 from rebeccahhh/release_3.6.0
pinning pytest-mock to version 1.11.1
2019-10-27 10:33:32 -04:00
Jake McDermott
841975d72b Scrape tag input state from dom and put it in vm
The tag input state lives somewhere in the associated select2 widgetry
and isn't directly tied to the vm like it is for the other inputs.
2019-10-26 20:02:41 -04:00
Rebeccah
678ce81487 pinning pytest-mock to version 1.11.1 2019-10-25 14:14:41 -04:00
Ryan Petrello
69e0f858bc Merge pull request #3819 from AlanCoding/collection_sanity_36
Make AWX collection "pass" sanity tests
2019-10-25 13:06:13 -04:00
Ryan Petrello
2b12e26b98 Merge pull request #3903 from ryanpetrello/runner-1-4-4
pin to runner==1.4.4
2019-10-25 12:51:53 -04:00
Ryan Petrello
634550fb0b pin to runner==1.4.4 2019-10-25 12:50:53 -04:00
Shane McDonald
dc89479b4c Merge pull request #3901 from ryanpetrello/syntax
fix a syntax error
2019-10-25 10:21:30 -04:00
Ryan Petrello
b51b1a959f fix a syntax error
whoopsie
2019-10-25 10:20:18 -04:00
Rebeccah Hunter
8de92b152c Merge pull request #3899 from rebeccahhh/release_3.6.0
fix serializer validation when instance not present
2019-10-25 09:27:03 -04:00
Rebeccah
95ab5327c3 added in check to see if the the current check has an instance or not to prevent nonetype errors 2019-10-25 09:20:44 -04:00
AlanCoding
e40f29092b Fully rely on error ignoring for sanity rel imports 2019-10-24 15:10:38 -04:00
AlanCoding
b394862210 Ignore import errors due to bugs in Ansible core 2019-10-24 14:58:55 -04:00
AlanCoding
0434c611f0 add release note 2019-10-24 14:50:45 -04:00
AlanCoding
201ae5f948 declare types in Ansible Tower module options 2019-10-24 14:50:45 -04:00
AlanCoding
9d93b78296 New target for sanity testing of the collection
Do not run in Zuul
2019-10-24 14:50:44 -04:00
Rebeccah Hunter
8669e87454 Merge pull request #3868 from rebeccahhh/release_3.6.0
Add api validation for policy rules in container groups
2019-10-24 11:29:10 -04:00
Jim Ladd
b2557c6fd8 Rename job_summary_dict to job_metadata
* Clarifies purpose of notification template variable
2019-10-23 17:11:05 -07:00
Alan Rominger
8f5d25a5df Fix bug where SCM inventory did not have a collections destination (#3795)
* update inventory path to be in tmp project clone

* copy project folder for inventory scm launch type

* Optionally accept inventory collection paths from ansible.cfg
2019-10-23 19:26:10 -04:00
Rebeccah
4c199b0ab2 moved filterint out policy instance values in the api browser input box into the instanceGroupDetail class where I overrode the update_raw_data function to parse out the unneeded data. Additionally added the fix for checking the value in the serializer. 2019-10-23 17:18:21 -04:00
Rebeccah
ef7b3fec94 removed policy_instance variables from container groups default values in the API put/patch view 2019-10-23 17:15:56 -04:00
Rebeccah
93bd1e6705 Added in validation for each of the 3 fields that should not be changed if the instance is a container group, defaults in the textarea persist with these 3 options 2019-10-23 17:15:56 -04:00
Jim Ladd
b13009b9a3 Update unit tests 2019-10-23 10:57:18 -07:00
Jim Ladd
fc941eda98 Return full webhook dict when serializing notif. 2019-10-23 10:57:18 -07:00
Jim Ladd
32deca2e92 Use correct notif. bodies when sending test notifs
* Notification backends now handle body of notifications differently
* .. depending on their type (webhook, email, and pagerduty) are
  currently the only three notification types that use body
* email and pagerduty expect a string
* webhooks expects a dict in string format
2019-10-23 10:57:18 -07:00
Jim Ladd
ff1a618a93 Don't use i18n for NT body string 2019-10-23 10:57:18 -07:00
Keith Grant
0af79b729e fix lint errors 2019-10-23 10:57:18 -07:00
Keith Grant
76711febd1 Add notification custom message fields for workflow pause/approval 2019-10-23 10:57:18 -07:00
Jim Ladd
81e545b720 NotificationSerializer should gracefully handle webhook/pagerduty bodies 2019-10-23 10:57:18 -07:00
Jim Ladd
d985b1215a In awxkit, add support for wf approval notification templates 2019-10-23 10:57:18 -07:00
Jim Ladd
157bec1777 Render WF approval notifications w/ custom templates 2019-10-23 10:57:18 -07:00
Jim Ladd
1754076a56 Refactor notification backends to use CustomNotificationBase 2019-10-23 10:57:18 -07:00
Jim Ladd
d3132820a5 Render default notifications using Jinja templates 2019-10-23 10:57:18 -07:00
Jim Ladd
9f4d65891c Remove unused build_notification_message method 2019-10-23 10:57:18 -07:00
Alan Rominger
653ec0ffab RBAC relaunch 403 updates (#3835)
* RBAC relaunch 403 updates

Addresses 2 things

1. If WFJ relaunch is attempted, and relaunch is denied
  because the WFJ had encrypted survey answers,
  a generic message was shown, this changes it to show
  a specific error message

2. Org admins are banned from relaunching a job
  if the job has encrypted survey answers

* update tests to raises access pattern

* catch PermissionDenied for user_capabilities
2019-10-23 10:59:35 -04:00
Alan Rominger
28228a3b57 Disable activity stream and speed up host group bulk deletion (#3817) 2019-10-23 08:25:00 -04:00
Ryan Petrello
e2470200da Merge pull request #3881 from ansible/k8s-cleanup
improve cleanup of anonymous kubeconfig files
2019-10-22 18:12:52 -04:00
Ryan Petrello
cfd7946097 improve cleanup of anonymous kubeconfig files 2019-10-22 17:47:09 -04:00
Alan Rominger
fb67b8edf9 Add support for credential_type in tower_credential module (#3820)
* Add support for credential_type

* Finish up credential_type parameter with tests

* make inputs mutually exclusive with other params

* Test credential type with dict input
2019-10-22 14:39:27 -04:00
Ryan Petrello
7af2bcc9b0 Merge pull request #3878 from ansible/fix-5062
fix a bug that breaks webhook launches when a survey is in use
2019-10-22 13:35:30 -04:00
Ryan Petrello
8e83c86d88 Merge pull request #3879 from ansible/whoopsie
fix a bug introduced upstream with settings.LOG_AGGREGATOR_AUDIT
2019-10-22 13:34:37 -04:00
Alex Corey
53cf6cf17c Instance Groups Instances List styling fixes (#3846)
* Instance Groups Instances slider renders properly, and that list wraps properly.

* Instance Groups responds properly

* assorted container groups ui fixes
updated responsiveness of instance groups and instances list
fix layout of container group form
update help text for container group form elements
update text for tech preview top bar

* update container group doclink

* list styling updates based on feedback
2019-10-22 13:31:53 -04:00
Ryan Petrello
8701f83922 fix a bug introduced upstream with settings.LOG_AGGREGATOR_AUDIT 2019-10-22 12:17:20 -04:00
Ryan Petrello
7344ee23ef fix a bug that breaks webhook launches when a survey is in use
see: https://github.com/ansible/awx/issues/5062
2019-10-22 09:08:14 -04:00
Ryan Petrello
a0bf3459eb Merge pull request #3877 from ansible/backport-external-log-fix
backport a fix to external logging aggregation
2019-10-22 07:43:35 -04:00
Martin Juhl
facec0fe76 Update handlers.py
The setFormatter tries to create the external.log file.. So we should check if LOG_AGGREGATOR_AUDIT is active here as well
2019-10-22 07:25:01 -04:00
Ryan Petrello
6fb09d73b1 Merge pull request #3801 from ansible/fix-jinja-host-name
[3.6.0] fix jinja host name
2019-10-22 06:54:01 -04:00
Ryan Petrello
a3519ce1df Merge pull request #3866 from ansible/reap-k8s-jobs
reap k8s-based jobs when the dispatcher restarts
2019-10-21 17:50:59 -04:00
Ryan Petrello
812d00f490 reap k8s-based jobs when the dispatcher restarts 2019-10-21 16:32:59 -04:00
Marliana Lara
5ac2211ef4 Merge pull request #3875 from marshmalien/5048-missing-job-details
Handle undefined schedule value in job detail component
2019-10-21 16:23:19 -04:00
Marliana Lara
9c9bf0ed84 Handle undefined schedule value in job detail component 2019-10-21 16:13:52 -04:00
Graham Mainwaring
c013d656c8 Add UI toggle to disable public Galaxy (#3867) 2019-10-21 16:10:25 -04:00
Khaled Elkhawaga
e38ed6574c update comments for kubernetes ingress
Signed-off-by: Khaled Elkhawaga <k.elkhawaga@gmail.com>
2019-10-11 01:51:38 +02:00
Khaled Elkhawaga
267e297eca add variable to set tls secret for kubernetes ingress
Signed-off-by: Khaled Elkhawaga <k.elkhawaga@gmail.com>
2019-10-11 00:52:24 +02:00
Ryan Petrello
a733a59b8d prevent the creation of Host names that contain Jinja 2019-10-10 14:46:03 -04:00
dgiorgio
1f76a88656 Fix postgres docker-compose, add postgresql_image var 2019-10-09 12:43:29 -04:00
456 changed files with 19077 additions and 4503 deletions

1
.github/BOTMETA.yml vendored
View File

@@ -1,3 +1,4 @@
---
files:
awx/ui/:
labels: component:ui

12
.yamllint Normal file
View File

@@ -0,0 +1,12 @@
---
ignore: |
.tox
awx/main/tests/data/inventory/plugins/**
# vault files
awx/main/tests/data/ansible_utils/playbooks/valid/vault.yml
awx/ui/test/e2e/tests/smoke-vars.yml
extends: default
rules:
line-length: disable

View File

@@ -4,41 +4,45 @@ This document provides a guide for installing AWX.
## Table of contents
- [Getting started](#getting-started)
- [Clone the repo](#clone-the-repo)
- [AWX branding](#awx-branding)
- [Prerequisites](#prerequisites)
- [System Requirements](#system-requirements)
- [AWX Tunables](#awx-tunables)
- [Choose a deployment platform](#choose-a-deployment-platform)
- [Official vs Building Images](#official-vs-building-images)
- [OpenShift](#openshift)
- [Prerequisites](#prerequisites-1)
- [Deploying to Minishift](#deploying-to-minishift)
- [Pre-build steps](#pre-build-steps)
- [PostgreSQL](#postgresql)
- [Start the build](#start-the-build)
- [Post build](#post-build)
- [Accessing AWX](#accessing-awx)
- [Kubernetes](#kubernetes)
- [Prerequisites](#prerequisites-2)
- [Pre-build steps](#pre-build-steps-1)
- [Configuring Helm](#configuring-helm)
- [Start the build](#start-the-build-1)
- [Accessing AWX](#accessing-awx-1)
- [SSL Termination](#ssl-termination)
- [Docker Compose](#docker-compose)
- [Prerequisites](#prerequisites-3)
- [Pre-build steps](#pre-build-steps-2)
- [Deploying to a remote host](#deploying-to-a-remote-host)
- [Inventory variables](#inventory-variables)
- [Installing AWX](#installing-awx)
* [Getting started](#getting-started)
+ [Clone the repo](#clone-the-repo)
+ [AWX branding](#awx-branding)
+ [Prerequisites](#prerequisites)
+ [System Requirements](#system-requirements)
+ [AWX Tunables](#awx-tunables)
+ [Choose a deployment platform](#choose-a-deployment-platform)
+ [Official vs Building Images](#official-vs-building-images)
* [Upgrading from previous versions](#upgrading-from-previous-versions)
* [OpenShift](#openshift)
+ [Prerequisites](#prerequisites-1)
+ [Pre-install steps](#pre-install-steps)
- [Deploying to Minishift](#deploying-to-minishift)
- [PostgreSQL](#postgresql)
+ [Run the installer](#run-the-installer)
+ [Post-install](#post-install)
+ [Accessing AWX](#accessing-awx)
* [Kubernetes](#kubernetes)
+ [Prerequisites](#prerequisites-2)
+ [Pre-install steps](#pre-install-steps-1)
+ [Configuring Helm](#configuring-helm)
+ [Run the installer](#run-the-installer-1)
+ [Post-install](#post-install-1)
+ [Accessing AWX](#accessing-awx-1)
+ [SSL Termination](#ssl-termination)
* [Docker-Compose](#docker-compose)
+ [Prerequisites](#prerequisites-3)
+ [Pre-install steps](#pre-install-steps-2)
- [Deploying to a remote host](#deploying-to-a-remote-host)
- [Inventory variables](#inventory-variables)
- [Docker registry](#docker-registry)
- [PostgreSQL](#postgresql-1)
- [Proxy settings](#proxy-settings)
- [Start the build](#start-the-build-2)
- [Post build](#post-build-2)
- [Accessing AWX](#accessing-awx-2)
- [PostgreSQL](#postgresql-1)
+ [Run the installer](#run-the-installer-2)
+ [Post-install](#post-install-2)
+ [Accessing AWX](#accessing-awx-2)
## Getting started
### Clone the repo
@@ -57,7 +61,7 @@ To install the assets, clone the `awx-logos` repo so that it is next to your `aw
Before you can run a deployment, you'll need the following installed in your local environment:
- [Ansible](http://docs.ansible.com/ansible/latest/intro_installation.html) Requires Version 2.4+
- [Ansible](http://docs.ansible.com/ansible/latest/intro_installation.html) Requires Version 2.8+
- [Docker](https://docs.docker.com/engine/installation/)
+ A recent version
- [docker](https://pypi.org/project/docker/) Python module
@@ -114,6 +118,26 @@ If these variables are present then all deployments will use these hosted images
> Multiple versions are provided. `latest` always pulls the most recent. You may also select version numbers at different granularities: 1, 1.0, 1.0.1, 1.0.0.123
## Upgrading from previous versions
Upgrading AWX involves rerunning the install playbook. Download a newer release from [https://github.com/ansible/awx/releases](https://github.com/ansible/awx/releases) and re-populate the inventory file with your customized variables.
For convenience, you can create a file called `vars.yml`:
```
admin_password: 'adminpass'
pg_password: 'pgpass'
rabbitmq_password: 'rabbitpass'
secret_key: 'mysupersecret'
```
And pass it to the installer:
```
$ ansible-playbook -i inventory install.yml -e @vars.yml
```
## OpenShift
### Prerequisites
@@ -133,9 +157,9 @@ This can be tuned by overriding the variables found in [/installer/roles/kuberne
For more detail on how resource requests are formed see: [https://docs.openshift.com/container-platform/latest/dev_guide/compute_resources.html#dev-compute-resources](https://docs.openshift.com/container-platform/latest/dev_guide/compute_resources.html#dev-compute-resources)
### Pre-build steps
### Pre-install steps
Before starting the build process, review the [inventory](./installer/inventory) file, and uncomment and provide values for the following variables found in the `[all:vars]` section:
Before starting the install, review the [inventory](./installer/inventory) file, and uncomment and provide values for the following variables found in the `[all:vars]` section:
*openshift_host*
@@ -197,20 +221,20 @@ By default, AWX will deploy a PostgreSQL pod inside of your cluster. You will ne
If you wish to use an external database, in the inventory file, set the value of `pg_hostname`, and update `pg_username`, `pg_password`, `pg_admin_password`, `pg_database`, and `pg_port` with the connection information. When setting `pg_hostname` the installer will assume you have configured the database in that location and will not launch the postgresql pod.
### Start the build
### Run the installer
To start the build, you will pass two *extra* variables on the command line. The first is *openshift_password*, which is the password for the *openshift_user*, and the second is *docker_registry_password*, which is the password associated with *docker_registry_username*.
To start the install, you will pass two *extra* variables on the command line. The first is *openshift_password*, which is the password for the *openshift_user*, and the second is *docker_registry_password*, which is the password associated with *docker_registry_username*.
If you're using the OpenShift internal registry, then you'll pass an access token for the *docker_registry_password* value, rather than a password. The `oc whoami -t` command will generate the required token, as long as you're logged into the cluster via `oc cluster login`.
To start the build and deployment, run the following (docker_registry_password is optional if using official images):
Run the following command (docker_registry_password is optional if using official images):
```bash
# Start the build and deployment
# Start the install
$ ansible-playbook -i inventory install.yml -e openshift_password=developer -e docker_registry_password=$(oc whoami -t)
```
### Post build
### Post-install
After the playbook run completes, check the status of the deployment by running `oc get pods`:
@@ -327,9 +351,9 @@ This can be tuned by overriding the variables found in [/installer/roles/kuberne
For more detail on how resource requests are formed see: [https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/)
### Pre-build steps
### Pre-install steps
Before starting the build process, review the [inventory](./installer/inventory) file, and uncomment and provide values for the following variables found in the `[all:vars]` section uncommenting when necessary. Make sure the openshift and standalone docker sections are commented out:
Before starting the install process, review the [inventory](./installer/inventory) file, and uncomment and provide values for the following variables found in the `[all:vars]` section uncommenting when necessary. Make sure the openshift and standalone docker sections are commented out:
*kubernetes_context*
@@ -349,7 +373,7 @@ If you want the AWX installer to manage creating the database pod (rather than i
Newer Kubernetes clusters with RBAC enabled will need to make sure a service account is created, make sure to follow the instructions here [https://docs.helm.sh/using_helm/#role-based-access-control](https://docs.helm.sh/using_helm/#role-based-access-control)
### Start the build
### Run the installer
After making changes to the `inventory` file use `ansible-playbook` to begin the install
@@ -357,7 +381,7 @@ After making changes to the `inventory` file use `ansible-playbook` to begin the
$ ansible-playbook -i inventory install.yml
```
### Post build
### Post-install
After the playbook run completes, check the status of the deployment by running `kubectl get pods --namespace awx` (replace awx with the namespace you used):
@@ -405,7 +429,7 @@ Unlike Openshift's `Route` the Kubernetes `Ingress` doesn't yet handle SSL termi
+ This also installs the `docker` Python module, which is incompatible with `docker-py`. If you have previously installed `docker-py`, please uninstall it.
- [Docker Compose](https://docs.docker.com/compose/install/).
### Pre-build steps
### Pre-install steps
#### Deploying to a remote host
@@ -436,7 +460,7 @@ If you choose to use the official images then the remote host will be the one to
#### Inventory variables
Before starting the build process, review the [inventory](./installer/inventory) file, and uncomment and provide values for the following variables found in the `[all:vars]` section:
Before starting the install process, review the [inventory](./installer/inventory) file, and uncomment and provide values for the following variables found in the `[all:vars]` section:
*postgres_data_dir*
@@ -511,9 +535,9 @@ AWX requires access to a PostgreSQL database, and by default, one will be create
If you wish to use an external database, in the inventory file, set the value of `pg_hostname`, and update `pg_username`, `pg_password`, `pg_admin_password`, `pg_database`, and `pg_port` with the connection information.
### Start the build
### Run the installer
If you are not pushing images to a Docker registry, start the build by running the following:
If you are not pushing images to a Docker registry, start the install by running the following:
```bash
# Set the working directory to installer
@@ -533,7 +557,7 @@ $ cd installer
$ ansible-playbook -i inventory -e docker_registry_password=password install.yml
```
### Post build
### Post-install
After the playbook run completes, Docker will report up to 5 running containers. If you chose to use an existing PostgresSQL database, then it will report 4. You can view the running containers using the `docker ps` command, as follows:
@@ -610,14 +634,3 @@ Added instance awx to tower
The AWX web server is accessible on the deployment host, using the *host_port* value set in the *inventory* file. The default URL is [http://localhost](http://localhost).
You will prompted with a login dialog. The default administrator username is `admin`, and the password is `password`.
### Maintenance using docker-compose
After the installation, maintenance operations with docker-compose can be done by using the `docker-compose.yml` file created at the location pointed by `docker_compose_dir`.
Among the possible operations, you may:
- Stop AWX : `docker-compose stop`
- Upgrade AWX : `docker-compose pull && docker-compose up --force-recreate`
See the [docker-compose documentation](https://docs.docker.com/compose/) for details.

View File

@@ -100,7 +100,7 @@ clean-languages:
find . -type f -regex ".*\.mo$$" -delete
# Remove temporary build files, compiled Python files.
clean: clean-ui clean-api clean-dist
clean: clean-ui clean-api clean-awxkit clean-dist
rm -rf awx/public
rm -rf awx/lib/site-packages
rm -rf awx/job_status
@@ -116,6 +116,10 @@ clean-api:
find . -type d -name "__pycache__" -delete
rm -f awx/awx_test.sqlite3*
rm -rf requirements/vendor
rm -rf awx/projects
clean-awxkit:
rm -rf awxkit/*.egg-info awxkit/.tox
# convenience target to assert environment variables are defined
guard-%:
@@ -364,7 +368,7 @@ check: flake8 pep8 # pyflakes pylint
awx-link:
cp -R /tmp/awx.egg-info /awx_devel/ || true
sed -i "s/placeholder/$(shell git describe --long | sed 's/\./\\./g')/" /awx_devel/awx.egg-info/PKG-INFO
sed -i "s/placeholder/$(shell cat VERSION)/" /awx_devel/awx.egg-info/PKG-INFO
cp -f /tmp/awx.egg-link /venv/awx/lib/python$(PYTHON_VERSION)/site-packages/awx.egg-link
TEST_DIRS ?= awx/main/tests/unit awx/main/tests/functional awx/conf/tests awx/sso/tests
@@ -381,7 +385,6 @@ test:
prepare_collection_venv:
rm -rf $(COLLECTION_VENV)
mkdir $(COLLECTION_VENV)
ln -s /usr/lib/python2.7/site-packages/ansible $(COLLECTION_VENV)/ansible
$(VENV_BASE)/awx/bin/pip install --target=$(COLLECTION_VENV) git+https://github.com/ansible/tower-cli.git
COLLECTION_TEST_DIRS ?= awx_collection/test/awx
@@ -404,7 +407,7 @@ test_collection_sanity:
mkdir -p sanity/ansible_collections/awx
cp -Ra awx_collection sanity/ansible_collections/awx/awx # symlinks do not work
cd sanity/ansible_collections/awx/awx && git init && git add . # requires both this file structure and a git repo, so there you go
cd sanity/ansible_collections/awx/awx && ansible-test sanity --test validate-modules
cd sanity/ansible_collections/awx/awx && ansible-test sanity
build_collection:
ansible-playbook -i localhost, awx_collection/template_galaxy.yml -e collection_package=$(COLLECTION_PACKAGE) -e collection_namespace=$(COLLECTION_NAMESPACE) -e collection_version=$(VERSION)
@@ -616,28 +619,34 @@ docker-auth:
echo "$(IMAGE_REPOSITORY_AUTH)" | docker login -u oauth2accesstoken --password-stdin $(IMAGE_REPOSITORY_BASE); \
fi;
# This directory is bind-mounted inside of the development container and
# needs to be pre-created for permissions to be set correctly. Otherwise,
# Docker will create this directory as root.
awx/projects:
@mkdir -p $@
# Docker isolated rampart
docker-compose-isolated:
docker-compose-isolated: awx/projects
CURRENT_UID=$(shell id -u) TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose -f tools/docker-compose.yml -f tools/docker-isolated-override.yml up
# Docker Compose Development environment
docker-compose: docker-auth
docker-compose: docker-auth awx/projects
CURRENT_UID=$(shell id -u) OS="$(shell docker info | grep 'Operating System')" TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose -f tools/docker-compose.yml up --no-recreate awx
docker-compose-cluster: docker-auth
docker-compose-cluster: docker-auth awx/projects
CURRENT_UID=$(shell id -u) TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose -f tools/docker-compose-cluster.yml up
docker-compose-credential-plugins: docker-auth
docker-compose-credential-plugins: docker-auth awx/projects
echo -e "\033[0;31mTo generate a CyberArk Conjur API key: docker exec -it tools_conjur_1 conjurctl account create quick-start\033[0m"
CURRENT_UID=$(shell id -u) TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose -f tools/docker-compose.yml -f tools/docker-credential-plugins-override.yml up --no-recreate awx
docker-compose-test: docker-auth
docker-compose-test: docker-auth awx/projects
cd tools && CURRENT_UID=$(shell id -u) OS="$(shell docker info | grep 'Operating System')" TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose run --rm --service-ports awx /bin/bash
docker-compose-runtest:
docker-compose-runtest: awx/projects
cd tools && CURRENT_UID=$(shell id -u) TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose run --rm --service-ports awx /start_tests.sh
docker-compose-build-swagger:
docker-compose-build-swagger: awx/projects
cd tools && CURRENT_UID=$(shell id -u) TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose run --rm --service-ports awx /start_tests.sh swagger
detect-schema-change: genschema
@@ -645,7 +654,7 @@ detect-schema-change: genschema
# Ignore differences in whitespace with -b
diff -u -b reference-schema.json schema.json
docker-compose-clean:
docker-compose-clean: awx/projects
cd tools && CURRENT_UID=$(shell id -u) TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose run --rm -w /awx_devel --service-ports awx make clean
cd tools && TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose rm -sf
@@ -654,7 +663,6 @@ docker-compose-build: awx-devel-build
# Base development image build
awx-devel-build:
docker build -t ansible/awx_devel -f tools/docker-compose/Dockerfile \
--cache-from=$(DEV_DOCKER_TAG_BASE)/awx_devel:devel \
--cache-from=$(DEV_DOCKER_TAG_BASE)/awx_devel:$(COMPOSE_TAG) .
docker tag ansible/awx_devel $(DEV_DOCKER_TAG_BASE)/awx_devel:$(COMPOSE_TAG)
#docker push $(DEV_DOCKER_TAG_BASE)/awx_devel:$(COMPOSE_TAG)
@@ -674,10 +682,10 @@ docker-clean:
docker-refresh: docker-clean docker-compose
# Docker Development Environment with Elastic Stack Connected
docker-compose-elk: docker-auth
docker-compose-elk: docker-auth awx/projects
CURRENT_UID=$(shell id -u) TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose -f tools/docker-compose.yml -f tools/elastic/docker-compose.logstash-link.yml -f tools/elastic/docker-compose.elastic-override.yml up --no-recreate
docker-compose-cluster-elk: docker-auth
docker-compose-cluster-elk: docker-auth awx/projects
TAG=$(COMPOSE_TAG) DEV_DOCKER_TAG_BASE=$(DEV_DOCKER_TAG_BASE) docker-compose -f tools/docker-compose-cluster.yml -f tools/elastic/docker-compose.logstash-link-cluster.yml -f tools/elastic/docker-compose.elastic-override.yml up --no-recreate
prometheus:

View File

@@ -1 +1 @@
9.0.0
9.1.0

View File

@@ -86,7 +86,14 @@ def oauth2_getattribute(self, attr):
# Custom method to override
# oauth2_provider.settings.OAuth2ProviderSettings.__getattribute__
from django.conf import settings
val = settings.OAUTH2_PROVIDER.get(attr)
val = None
if 'migrate' not in sys.argv:
# certain Django OAuth Toolkit migrations actually reference
# setting lookups for references to model classes (e.g.,
# oauth2_settings.REFRESH_TOKEN_MODEL)
# If we're doing an OAuth2 setting lookup *while running* a migration,
# don't do our usual "Configure Tower in Tower" database setting lookup
val = settings.OAUTH2_PROVIDER.get(attr)
if val is None:
val = object.__getattribute__(self, attr)
return val

View File

@@ -62,3 +62,14 @@ register(
category=_('Authentication'),
category_slug='authentication',
)
register(
'LOGIN_REDIRECT_OVERRIDE',
field_class=fields.CharField,
allow_blank=True,
required=False,
label=_('Login redirect override URL'),
help_text=_('URL to which unauthorized users will be redirected to log in. '
'If blank, users will be sent to the Tower login page.'),
category=_('Authentication'),
category_slug='authentication',
)

View File

@@ -1472,7 +1472,7 @@ class ProjectUpdateSerializer(UnifiedJobSerializer, ProjectOptionsSerializer):
class Meta:
model = ProjectUpdate
fields = ('*', 'project', 'job_type', '-controller_node')
fields = ('*', 'project', 'job_type', 'job_tags', '-controller_node')
def get_related(self, obj):
res = super(ProjectUpdateSerializer, self).get_related(obj)
@@ -2456,12 +2456,18 @@ class CredentialTypeSerializer(BaseSerializer):
raise PermissionDenied(
detail=_("Modifications not allowed for managed credential types")
)
old_inputs = {}
if self.instance:
old_inputs = copy.deepcopy(self.instance.inputs)
ret = super(CredentialTypeSerializer, self).validate(attrs)
if self.instance and self.instance.credentials.exists():
if 'inputs' in attrs and attrs['inputs'] != self.instance.inputs:
if 'inputs' in attrs and old_inputs != self.instance.inputs:
raise PermissionDenied(
detail= _("Modifications to inputs are not allowed for credential types that are in use")
)
ret = super(CredentialTypeSerializer, self).validate(attrs)
if 'kind' in attrs and attrs['kind'] not in ('cloud', 'net'):
raise serializers.ValidationError({
@@ -4658,6 +4664,10 @@ class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSeria
def get_summary_fields(self, obj):
summary_fields = super(ScheduleSerializer, self).get_summary_fields(obj)
if isinstance(obj.unified_job_template, SystemJobTemplate):
summary_fields['unified_job_template']['job_type'] = obj.unified_job_template.job_type
if 'inventory' in summary_fields:
return summary_fields

View File

@@ -1,7 +1,7 @@
# Cancel Inventory Update
Make a GET request to this resource to determine if the inventory update can be
cancelled. The response will include the following field:
canceled. The response will include the following field:
* `can_cancel`: Indicates whether this update can be canceled (boolean,
read-only)

View File

@@ -1,7 +1,7 @@
{% ifmeth GET %}
# Determine if a Job can be cancelled
# Determine if a Job can be canceled
Make a GET request to this resource to determine if the job can be cancelled.
Make a GET request to this resource to determine if the job can be canceled.
The response will include the following field:
* `can_cancel`: Indicates whether this job can be canceled (boolean, read-only)

View File

@@ -1,7 +1,7 @@
# Cancel Project Update
Make a GET request to this resource to determine if the project update can be
cancelled. The response will include the following field:
canceled. The response will include the following field:
* `can_cancel`: Indicates whether this update can be canceled (boolean,
read-only)

View File

@@ -72,12 +72,11 @@ from awx.api.generics import (
SubListDestroyAPIView
)
from awx.api.versioning import reverse
from awx.conf.license import get_license
from awx.main import models
from awx.main.utils import (
camelcase_to_underscore,
extract_ansible_vars,
get_awx_version,
get_awx_http_client_headers,
get_object_or_400,
getattrd,
get_pk_from_dict,
@@ -1386,6 +1385,7 @@ class CredentialExternalTest(SubDetailAPIView):
model = models.Credential
serializer_class = serializers.EmptySerializer
obj_permission_type = 'use'
def post(self, request, *args, **kwargs):
obj = self.get_object()
@@ -1643,18 +1643,6 @@ class HostInsights(GenericAPIView):
return session
def _get_headers(self):
license = get_license(show_key=False).get('license_type', 'UNLICENSED')
headers = {
'Content-Type': 'application/json',
'User-Agent': '{} {} ({})'.format(
'AWX' if license == 'open' else 'Red Hat Ansible Tower',
get_awx_version(),
license
)
}
return headers
def _get_platform_info(self, host, session, headers):
url = '{}/api/inventory/v1/hosts?insights_id={}'.format(
@@ -1721,7 +1709,7 @@ class HostInsights(GenericAPIView):
username = cred.get_input('username', default='')
password = cred.get_input('password', default='')
session = self._get_session(username, password)
headers = self._get_headers()
headers = get_awx_http_client_headers()
data = self._get_insights(host, session, headers)
return Response(data, status=status.HTTP_200_OK)

View File

@@ -60,6 +60,7 @@ class ApiRootView(APIView):
data['oauth2'] = drf_reverse('api:oauth_authorization_root_view')
data['custom_logo'] = settings.CUSTOM_LOGO
data['custom_login_info'] = settings.CUSTOM_LOGIN_INFO
data['login_redirect_override'] = settings.LOGIN_REDIRECT_OVERRIDE
return Response(data)

View File

@@ -1,13 +1,12 @@
# Copyright (c) 2016 Ansible, Inc.
# All Rights Reserved.
# Tower
from awx.main.utils.common import get_licenser
__all__ = ['get_license']
def _get_validated_license_data():
from awx.main.utils.common import get_licenser
return get_licenser().validate()

View File

@@ -2,7 +2,6 @@
from __future__ import unicode_literals
from django.db import migrations
from awx.conf.migrations import _reencrypt
class Migration(migrations.Migration):
@@ -12,5 +11,8 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RunPython(_reencrypt.replace_aesecb_fernet),
# This list is intentionally empty.
# Tower 3.2 included several data migrations that are no longer
# necessary (this list is now empty because Tower 3.2 is past EOL and
# cannot be directly upgraded to modern versions of Tower)
]

View File

@@ -1,30 +1,13 @@
import base64
import hashlib
from django.utils.encoding import smart_str
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import ECB
from awx.conf import settings_registry
__all__ = ['replace_aesecb_fernet', 'get_encryption_key', 'encrypt_field',
'decrypt_value', 'decrypt_value', 'should_decrypt_field']
def replace_aesecb_fernet(apps, schema_editor):
from awx.main.utils.encryption import encrypt_field
Setting = apps.get_model('conf', 'Setting')
for setting in Setting.objects.filter().order_by('pk'):
if settings_registry.is_setting_encrypted(setting.key):
if should_decrypt_field(setting.value):
setting.value = decrypt_field(setting, 'value')
setting.value = encrypt_field(setting, 'value')
setting.save()
__all__ = ['get_encryption_key', 'decrypt_field']
def get_encryption_key(field_name, pk=None):
@@ -76,38 +59,3 @@ def decrypt_field(instance, field_name, subfield=None):
key = get_encryption_key(field_name, getattr(instance, 'pk', None))
return decrypt_value(key, value)
def encrypt_field(instance, field_name, ask=False, subfield=None, skip_utf8=False):
'''
Return content of the given instance and field name encrypted.
'''
value = getattr(instance, field_name)
if isinstance(value, dict) and subfield is not None:
value = value[subfield]
if not value or value.startswith('$encrypted$') or (ask and value == 'ASK'):
return value
if skip_utf8:
utf8 = False
else:
utf8 = type(value) == str
value = smart_str(value)
key = get_encryption_key(field_name, getattr(instance, 'pk', None))
encryptor = Cipher(AES(key), ECB(), default_backend()).encryptor()
block_size = 16
while len(value) % block_size != 0:
value += '\x00'
encrypted = encryptor.update(value) + encryptor.finalize()
b64data = base64.b64encode(encrypted)
tokens = ['$encrypted', 'AES', b64data]
if utf8:
# If the value to encrypt is utf-8, we need to add a marker so we
# know to decode the data when it's decrypted later
tokens.insert(1, 'UTF8')
return '$'.join(tokens)
def should_decrypt_field(value):
if hasattr(value, 'startswith'):
return value.startswith('$encrypted$') and '$AESCBC$' not in value
return False

View File

@@ -166,6 +166,8 @@ def instance_info(since, include_hostnames=False):
instances = models.Instance.objects.values_list('hostname').values(
'uuid', 'version', 'capacity', 'cpu', 'memory', 'managed_by_policy', 'hostname', 'last_isolated_check', 'enabled')
for instance in instances:
consumed_capacity = sum(x.task_impact for x in models.UnifiedJob.objects.filter(execution_node=instance['hostname'],
status__in=('running', 'waiting')))
instance_info = {
'uuid': instance['uuid'],
'version': instance['version'],
@@ -174,7 +176,9 @@ def instance_info(since, include_hostnames=False):
'memory': instance['memory'],
'managed_by_policy': instance['managed_by_policy'],
'last_isolated_check': _get_isolated_datetime(instance['last_isolated_check']),
'enabled': instance['enabled']
'enabled': instance['enabled'],
'consumed_capacity': consumed_capacity,
'remaining_capacity': instance['capacity'] - consumed_capacity
}
if include_hostnames is True:
instance_info['hostname'] = instance['hostname']

View File

@@ -15,6 +15,7 @@ from awx.conf.license import get_license
from awx.main.models import Job
from awx.main.access import access_registry
from awx.main.models.ha import TowerAnalyticsState
from awx.main.utils import get_awx_http_client_headers
__all__ = ['register', 'gather', 'ship', 'table_version']
@@ -165,11 +166,15 @@ def ship(path):
return logger.error('REDHAT_PASSWORD is not set')
with open(path, 'rb') as f:
files = {'file': (os.path.basename(path), f, settings.INSIGHTS_AGENT_MIME)}
response = requests.post(url,
files=files,
verify="/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
auth=(rh_user, rh_password),
timeout=(31, 31))
s = requests.Session()
s.headers = get_awx_http_client_headers()
s.headers.pop('Content-Type')
response = s.post(url,
files=files,
verify="/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
auth=(rh_user, rh_password),
headers=s.headers,
timeout=(31, 31))
if response.status_code != 202:
return logger.exception('Upload failed with status {}, {}'.format(response.status_code,
response.text))

View File

@@ -46,6 +46,8 @@ INSTANCE_MEMORY = Gauge('awx_instance_memory', 'RAM (Kb) on each node in a Tower
INSTANCE_INFO = Info('awx_instance', 'Info about each node in a Tower system', ['hostname', 'instance_uuid',])
INSTANCE_LAUNCH_TYPE = Gauge('awx_instance_launch_type_total', 'Type of Job launched', ['node', 'launch_type',])
INSTANCE_STATUS = Gauge('awx_instance_status_total', 'Status of Job launched', ['node', 'status',])
INSTANCE_CONSUMED_CAPACITY = Gauge('awx_instance_consumed_capacity', 'Consumed capacity of each node in a Tower system', ['hostname', 'instance_uuid',])
INSTANCE_REMAINING_CAPACITY = Gauge('awx_instance_remaining_capacity', 'Remaining capacity of each node in a Tower system', ['hostname', 'instance_uuid',])
LICENSE_INSTANCE_TOTAL = Gauge('awx_license_instance_total', 'Total number of managed hosts provided by your license')
LICENSE_INSTANCE_FREE = Gauge('awx_license_instance_free', 'Number of remaining managed hosts provided by your license')
@@ -104,6 +106,8 @@ def metrics():
INSTANCE_CAPACITY.labels(hostname=hostname, instance_uuid=uuid).set(instance_data[uuid]['capacity'])
INSTANCE_CPU.labels(hostname=hostname, instance_uuid=uuid).set(instance_data[uuid]['cpu'])
INSTANCE_MEMORY.labels(hostname=hostname, instance_uuid=uuid).set(instance_data[uuid]['memory'])
INSTANCE_CONSUMED_CAPACITY.labels(hostname=hostname, instance_uuid=uuid).set(instance_data[uuid]['consumed_capacity'])
INSTANCE_REMAINING_CAPACITY.labels(hostname=hostname, instance_uuid=uuid).set(instance_data[uuid]['remaining_capacity'])
INSTANCE_INFO.labels(hostname=hostname, instance_uuid=uuid).info({
'enabled': str(instance_data[uuid]['enabled']),
'last_isolated_check': getattr(instance_data[uuid], 'last_isolated_check', 'None'),

View File

@@ -1,8 +1,17 @@
from django.apps import AppConfig
from django.db.models.signals import pre_migrate
from django.utils.translation import ugettext_lazy as _
def raise_migration_flag(**kwargs):
from awx.main.tasks import set_migration_flag
set_migration_flag.delay()
class MainConfig(AppConfig):
name = 'awx.main'
verbose_name = _('Main')
def ready(self):
pre_migrate.connect(raise_migration_flag, sender=self)

View File

@@ -514,6 +514,17 @@ register(
category_slug='jobs'
)
register(
'GALAXY_IGNORE_CERTS',
field_class=fields.BooleanField,
default=False,
label=_('Ignore Ansible Galaxy SSL Certificate Verification'),
help_text=_('If set to true, certificate validation will not be done when'
'installing content from any Galaxy server.'),
category=_('Jobs'),
category_slug='jobs'
)
register(
'STDOUT_MAX_BYTES_DISPLAY',
field_class=fields.IntegerField,

View File

@@ -123,8 +123,16 @@ class PoolWorker(object):
# if any tasks were finished, removed them from the managed tasks for
# this worker
for uuid in finished:
self.messages_finished += 1
del self.managed_tasks[uuid]
try:
del self.managed_tasks[uuid]
self.messages_finished += 1
except KeyError:
# ansible _sometimes_ appears to send events w/ duplicate UUIDs;
# UUIDs for ansible events are *not* actually globally unique
# when this occurs, it's _fine_ to ignore this KeyError because
# the purpose of self.managed_tasks is to just track internal
# state of which events are *currently* being processed.
pass
@property
def current_task(self):

View File

@@ -32,15 +32,14 @@ def set_pythonpath(venv_libdir, env):
class IsolatedManager(object):
def __init__(self, cancelled_callback=None, check_callback=None, pod_manager=None):
def __init__(self, canceled_callback=None, check_callback=None, pod_manager=None):
"""
:param cancelled_callback: a callable - which returns `True` or `False`
:param canceled_callback: a callable - which returns `True` or `False`
- signifying if the job has been prematurely
cancelled
canceled
"""
self.cancelled_callback = cancelled_callback
self.canceled_callback = canceled_callback
self.check_callback = check_callback
self.idle_timeout = max(60, 2 * settings.AWX_ISOLATED_CONNECTION_TIMEOUT)
self.started_at = None
self.captured_command_artifact = False
self.instance = None
@@ -106,9 +105,8 @@ class IsolatedManager(object):
'envvars': env,
'finished_callback': finished_callback,
'verbosity': verbosity,
'cancel_callback': self.cancelled_callback,
'cancel_callback': self.canceled_callback,
'settings': {
'idle_timeout': self.idle_timeout,
'job_timeout': settings.AWX_ISOLATED_LAUNCH_TIMEOUT,
'pexpect_timeout': getattr(settings, 'PEXPECT_TIMEOUT', 5),
'suppress_ansible_output': True,
@@ -118,7 +116,7 @@ class IsolatedManager(object):
def path_to(self, *args):
return os.path.join(self.private_data_dir, *args)
def run_management_playbook(self, playbook, private_data_dir, **kw):
def run_management_playbook(self, playbook, private_data_dir, idle_timeout=None, **kw):
iso_dir = tempfile.mkdtemp(
prefix=playbook,
dir=private_data_dir
@@ -126,6 +124,10 @@ class IsolatedManager(object):
params = self.runner_params.copy()
params['playbook'] = playbook
params['private_data_dir'] = iso_dir
if idle_timeout:
params['settings']['idle_timeout'] = idle_timeout
else:
params['settings'].pop('idle_timeout', None)
params.update(**kw)
if all([
getattr(settings, 'AWX_ISOLATED_KEY_GENERATION', False) is True,
@@ -177,6 +179,7 @@ class IsolatedManager(object):
logger.debug('Starting job {} on isolated host with `run_isolated.yml` playbook.'.format(self.instance.id))
runner_obj = self.run_management_playbook('run_isolated.yml',
self.private_data_dir,
idle_timeout=max(60, 2 * settings.AWX_ISOLATED_CONNECTION_TIMEOUT),
extravars=extravars)
if runner_obj.status == 'failed':
@@ -208,14 +211,14 @@ class IsolatedManager(object):
dispatcher = CallbackQueueDispatcher()
while status == 'failed':
canceled = self.cancelled_callback() if self.cancelled_callback else False
canceled = self.canceled_callback() if self.canceled_callback else False
if not canceled and time.time() - last_check < interval:
# If the job isn't cancelled, but we haven't waited `interval` seconds, wait longer
# If the job isn't canceled, but we haven't waited `interval` seconds, wait longer
time.sleep(1)
continue
if canceled:
logger.warning('Isolated job {} was manually cancelled.'.format(self.instance.id))
logger.warning('Isolated job {} was manually canceled.'.format(self.instance.id))
logger.debug('Checking on isolated job {} with `check_isolated.yml`.'.format(self.instance.id))
runner_obj = self.run_management_playbook('check_isolated.yml',

View File

@@ -28,6 +28,7 @@ from awx.main.models.inventory import (
Host
)
from awx.main.utils.mem_inventory import MemInventory, dict_to_mem_data
from awx.main.utils.safe_yaml import sanitize_jinja
# other AWX imports
from awx.main.models.rbac import batch_role_ancestor_rebuilding
@@ -795,6 +796,10 @@ class Command(BaseCommand):
if self.instance_id_var:
instance_id = self._get_instance_id(mem_host.variables)
host_attrs['instance_id'] = instance_id
try:
sanitize_jinja(mem_host_name)
except ValueError as e:
raise ValueError(str(e) + ': {}'.format(mem_host_name))
db_host = self.inventory.hosts.update_or_create(name=mem_host_name, defaults=host_attrs)[0]
if enabled is False:
logger.debug('Host "%s" added (disabled)', mem_host_name)

View File

@@ -0,0 +1,129 @@
import base64
import json
import os
from django.core.management.base import BaseCommand
from django.conf import settings
from django.db import transaction
from django.db.models.signals import post_save
from awx.conf import settings_registry
from awx.conf.models import Setting
from awx.conf.signals import on_post_save_setting
from awx.main.models import (
UnifiedJob, Credential, NotificationTemplate, Job, JobTemplate, WorkflowJob,
WorkflowJobTemplate, OAuth2Application
)
from awx.main.utils.encryption import (
encrypt_field, decrypt_field, encrypt_value, decrypt_value, get_encryption_key
)
class Command(BaseCommand):
"""
Regenerate a new SECRET_KEY value and re-encrypt every secret in the
Tower database.
"""
@transaction.atomic
def handle(self, **options):
self.old_key = settings.SECRET_KEY
self.new_key = base64.encodebytes(os.urandom(33)).decode().rstrip()
self._notification_templates()
self._credentials()
self._unified_jobs()
self._oauth2_app_secrets()
self._settings()
self._survey_passwords()
return self.new_key
def _notification_templates(self):
for nt in NotificationTemplate.objects.iterator():
CLASS_FOR_NOTIFICATION_TYPE = dict([(x[0], x[2]) for x in NotificationTemplate.NOTIFICATION_TYPES])
notification_class = CLASS_FOR_NOTIFICATION_TYPE[nt.notification_type]
for field in filter(lambda x: notification_class.init_parameters[x]['type'] == "password",
notification_class.init_parameters):
nt.notification_configuration[field] = decrypt_field(nt, 'notification_configuration', subfield=field, secret_key=self.old_key)
nt.notification_configuration[field] = encrypt_field(nt, 'notification_configuration', subfield=field, secret_key=self.new_key)
nt.save()
def _credentials(self):
for credential in Credential.objects.iterator():
for field_name in credential.credential_type.secret_fields:
if field_name in credential.inputs:
credential.inputs[field_name] = decrypt_field(
credential,
field_name,
secret_key=self.old_key
)
credential.inputs[field_name] = encrypt_field(
credential,
field_name,
secret_key=self.new_key
)
credential.save()
def _unified_jobs(self):
for uj in UnifiedJob.objects.iterator():
if uj.start_args:
uj.start_args = decrypt_field(
uj,
'start_args',
secret_key=self.old_key
)
uj.start_args = encrypt_field(uj, 'start_args', secret_key=self.new_key)
uj.save()
def _oauth2_app_secrets(self):
for app in OAuth2Application.objects.iterator():
raw = app.client_secret
app.client_secret = raw
encrypted = encrypt_value(raw, secret_key=self.new_key)
OAuth2Application.objects.filter(pk=app.pk).update(client_secret=encrypted)
def _settings(self):
# don't update memcached, the *actual* value isn't changing
post_save.disconnect(on_post_save_setting, sender=Setting)
for setting in Setting.objects.filter().order_by('pk'):
if settings_registry.is_setting_encrypted(setting.key):
setting.value = decrypt_field(setting, 'value', secret_key=self.old_key)
setting.value = encrypt_field(setting, 'value', secret_key=self.new_key)
setting.save()
def _survey_passwords(self):
for _type in (JobTemplate, WorkflowJobTemplate):
for jt in _type.objects.exclude(survey_spec={}):
changed = False
if jt.survey_spec.get('spec', []):
for field in jt.survey_spec['spec']:
if field.get('type') == 'password' and field.get('default', ''):
raw = decrypt_value(
get_encryption_key('value', None, secret_key=self.old_key),
field['default']
)
field['default'] = encrypt_value(
raw,
pk=None,
secret_key=self.new_key
)
changed = True
if changed:
jt.save(update_fields=["survey_spec"])
for _type in (Job, WorkflowJob):
for job in _type.objects.exclude(survey_passwords={}).iterator():
changed = False
for key in job.survey_passwords:
if key in job.extra_vars:
extra_vars = json.loads(job.extra_vars)
if not extra_vars.get(key):
continue
raw = decrypt_value(
get_encryption_key('value', None, secret_key=self.old_key),
extra_vars[key]
)
extra_vars[key] = encrypt_value(raw, pk=None, secret_key=self.new_key)
job.extra_vars = json.dumps(extra_vars)
changed = True
if changed:
job.save(update_fields=["extra_vars"])

View File

@@ -13,8 +13,7 @@ import urllib.parse
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.db.migrations.executor import MigrationExecutor
from django.db import IntegrityError, connection
from django.db import IntegrityError
from django.utils.functional import curry
from django.shortcuts import get_object_or_404, redirect
from django.apps import apps
@@ -24,6 +23,7 @@ from django.urls import reverse, resolve
from awx.main.models import ActivityStream
from awx.main.utils.named_url_graph import generate_graph, GraphNode
from awx.main.utils.db import migration_in_progress_check_or_relase
from awx.conf import fields, register
@@ -213,8 +213,7 @@ class URLModificationMiddleware(MiddlewareMixin):
class MigrationRanCheckMiddleware(MiddlewareMixin):
def process_request(self, request):
executor = MigrationExecutor(connection)
plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
if bool(plan) and \
getattr(resolve(request.path), 'url_name', '') != 'migrations_notran':
if migration_in_progress_check_or_relase():
if getattr(resolve(request.path), 'url_name', '') == 'migrations_notran':
return
return redirect(reverse("ui:migrations_notran"))

View File

@@ -7,12 +7,6 @@ from django.db import migrations, models
# AWX
from awx.main.migrations import ActivityStreamDisabledMigration
from awx.main.migrations import _inventory_source as invsrc
from awx.main.migrations import _migration_utils as migration_utils
from awx.main.migrations import _reencrypt as reencrypt
from awx.main.migrations import _scan_jobs as scan_jobs
from awx.main.migrations import _credentialtypes as credentialtypes
from awx.main.migrations import _azure_credentials as azurecreds
import awx.main.fields
@@ -23,16 +17,8 @@ class Migration(ActivityStreamDisabledMigration):
]
operations = [
# Inventory Refresh
migrations.RunPython(migration_utils.set_current_apps_for_migrations),
migrations.RunPython(invsrc.remove_rax_inventory_sources),
migrations.RunPython(azurecreds.remove_azure_credentials),
migrations.RunPython(invsrc.remove_azure_inventory_sources),
migrations.RunPython(invsrc.remove_inventory_source_with_no_inventory_link),
migrations.RunPython(invsrc.rename_inventory_sources),
migrations.RunPython(reencrypt.replace_aesecb_fernet),
migrations.RunPython(scan_jobs.migrate_scan_job_templates),
migrations.RunPython(credentialtypes.migrate_to_v2_credentials),
migrations.RunPython(credentialtypes.migrate_job_credentials),
# This list is intentionally empty.
# Tower 3.2 included several data migrations that are no longer
# necessary (this list is now empty because Tower 3.2 is past EOL and
# cannot be directly upgraded to modern versions of Tower)
]

View File

@@ -15,8 +15,6 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RunPython(migration_utils.set_current_apps_for_migrations),
migrations.RunPython(credentialtypes.create_rhv_tower_credtype),
migrations.AlterField(
model_name='inventorysource',
name='source',

View File

@@ -3,8 +3,6 @@ from __future__ import unicode_literals
from django.db import migrations
from awx.main.migrations import ActivityStreamDisabledMigration
from awx.main.migrations import _reencrypt as reencrypt
from awx.main.migrations import _migration_utils as migration_utils
class Migration(ActivityStreamDisabledMigration):
@@ -14,6 +12,8 @@ class Migration(ActivityStreamDisabledMigration):
]
operations = [
migrations.RunPython(migration_utils.set_current_apps_for_migrations),
migrations.RunPython(reencrypt.encrypt_survey_passwords),
# This list is intentionally empty.
# Tower 3.2 included several data migrations that are no longer
# necessary (this list is now empty because Tower 3.2 is past EOL and
# cannot be directly upgraded to modern versions of Tower)
]

View File

@@ -1,10 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# AWX
from awx.main.migrations import _migration_utils as migration_utils
from awx.main.migrations import _credentialtypes as credentialtypes
from django.db import migrations
@@ -15,6 +11,8 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RunPython(migration_utils.set_current_apps_for_migrations),
migrations.RunPython(credentialtypes.add_azure_cloud_environment_field),
# This list is intentionally empty.
# Tower 3.2 included several data migrations that are no longer
# necessary (this list is now empty because Tower 3.2 is past EOL and
# cannot be directly upgraded to modern versions of Tower)
]

View File

@@ -19,11 +19,11 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='systemjob',
name='job_type',
field=models.CharField(blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('clearsessions', 'Removes expired browser sessions from the database'), ('cleartokens', 'Removes expired OAuth 2 access tokens and refresh tokens')], default='', max_length=32),
field=models.CharField(blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('cleanup_sessions', 'Removes expired browser sessions from the database'), ('cleanup_tokens', 'Removes expired OAuth 2 access tokens and refresh tokens')], default='', max_length=32),
),
migrations.AlterField(
model_name='systemjobtemplate',
name='job_type',
field=models.CharField(blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('clearsessions', 'Removes expired browser sessions from the database'), ('cleartokens', 'Removes expired OAuth 2 access tokens and refresh tokens')], default='', max_length=32),
field=models.CharField(blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('cleanup_sessions', 'Removes expired browser sessions from the database'), ('cleanup_tokens', 'Removes expired OAuth 2 access tokens and refresh tokens')], default='', max_length=32),
),
]

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
def _cleanup_license_setting(apps, schema_editor):
Setting = apps.get_model('conf', 'Setting')
for license in Setting.objects.filter(key='LICENSE').all():
for k in ('rh_username', 'rh_password'):
license.value.pop(k, None)
license.save()
class Migration(migrations.Migration):
dependencies = [
('main', '0098_v360_rename_cyberark_aim_credential_type'),
]
operations = [migrations.RunPython(_cleanup_license_setting)]

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-11-01 18:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0099_v361_license_cleanup'),
]
operations = [
migrations.AddField(
model_name='projectupdate',
name='job_tags',
field=models.CharField(blank=True, default='', help_text='Parts of the project update playbook that will be run.', max_length=1024),
),
]

View File

@@ -1,15 +0,0 @@
import logging
from django.db.models import Q
logger = logging.getLogger('awx.main.migrations')
def remove_azure_credentials(apps, schema_editor):
'''Azure is not supported as of 3.2 and greater. Instead, azure_rm is
supported.
'''
Credential = apps.get_model('main', 'Credential')
logger.debug("Removing all Azure Credentials from database.")
Credential.objects.filter(kind='azure').delete()

View File

@@ -1,6 +1,4 @@
from awx.main import utils
from awx.main.models import CredentialType
from awx.main.utils.encryption import encrypt_field, decrypt_field
from django.db.models import Q
@@ -61,16 +59,6 @@ def _disassociate_non_insights_projects(apps, cred):
apps.get_model('main', 'Project').objects.filter(~Q(scm_type='insights') & Q(credential=cred)).update(credential=None)
def migrate_to_v2_credentials(apps, schema_editor):
# TODO: remove once legacy/EOL'd Towers no longer support this upgrade path
pass
def migrate_job_credentials(apps, schema_editor):
# TODO: remove once legacy/EOL'd Towers no longer support this upgrade path
pass
def add_vault_id_field(apps, schema_editor):
# this is no longer necessary; schemas are defined in code
pass
@@ -81,21 +69,11 @@ def remove_vault_id_field(apps, schema_editor):
pass
def create_rhv_tower_credtype(apps, schema_editor):
# this is no longer necessary; schemas are defined in code
pass
def add_tower_verify_field(apps, schema_editor):
# this is no longer necessary; schemas are defined in code
pass
def add_azure_cloud_environment_field(apps, schema_editor):
# this is no longer necessary; schemas are defined in code
pass
def remove_become_methods(apps, schema_editor):
# this is no longer necessary; schemas are defined in code
pass

View File

@@ -1,6 +1,5 @@
import logging
from django.db.models import Q
from django.utils.encoding import smart_text
from awx.main.utils.common import parse_yaml_or_json
@@ -8,64 +7,6 @@ from awx.main.utils.common import parse_yaml_or_json
logger = logging.getLogger('awx.main.migrations')
def remove_manual_inventory_sources(apps, schema_editor):
'''Previously we would automatically create inventory sources after
Group creation and we would use the parent Group as our interface for the user.
During that process we would create InventorySource that had a source of "manual".
'''
# TODO: use this in the 3.3 data migrations
InventorySource = apps.get_model('main', 'InventorySource')
# see models/inventory.py SOURCE_CHOICES - ('', _('Manual'))
logger.debug("Removing all Manual InventorySource from database.")
InventorySource.objects.filter(source='').delete()
def remove_rax_inventory_sources(apps, schema_editor):
'''Rackspace inventory sources are not supported since 3.2, remove them.
'''
InventorySource = apps.get_model('main', 'InventorySource')
logger.debug("Removing all Rackspace InventorySource from database.")
InventorySource.objects.filter(source='rax').delete()
def rename_inventory_sources(apps, schema_editor):
'''Rename existing InventorySource entries using the following format.
{{ inventory_source.name }} - {{ inventory.module }} - {{ number }}
The number will be incremented for each InventorySource for the organization.
'''
Organization = apps.get_model('main', 'Organization')
InventorySource = apps.get_model('main', 'InventorySource')
for org in Organization.objects.iterator():
for i, invsrc in enumerate(InventorySource.objects.filter(Q(inventory__organization=org) |
Q(deprecated_group__inventory__organization=org)).distinct().all()):
inventory = invsrc.deprecated_group.inventory if invsrc.deprecated_group else invsrc.inventory
name = '{0} - {1} - {2}'.format(invsrc.name, inventory.name, i)
logger.debug("Renaming InventorySource({0}) {1} -> {2}".format(
invsrc.pk, invsrc.name, name
))
invsrc.name = name
invsrc.save()
def remove_inventory_source_with_no_inventory_link(apps, schema_editor):
'''If we cannot determine the Inventory for which an InventorySource exists
we can safely remove it.
'''
InventorySource = apps.get_model('main', 'InventorySource')
logger.debug("Removing all InventorySource that have no link to an Inventory from database.")
InventorySource.objects.filter(Q(inventory__organization=None) & Q(deprecated_group__inventory=None)).delete()
def remove_azure_inventory_sources(apps, schema_editor):
'''Azure inventory sources are not supported since 3.2, remove them.
'''
InventorySource = apps.get_model('main', 'InventorySource')
logger.debug("Removing all Azure InventorySource from database.")
InventorySource.objects.filter(source='azure').delete()
def _get_instance_id(from_dict, new_id, default=''):
'''logic mostly duplicated with inventory_import command Command._get_instance_id
frozen in time here, for purposes of migrations

View File

@@ -1,79 +1,12 @@
import logging
import json
from django.utils.translation import ugettext_lazy as _
from awx.conf.migrations._reencrypt import (
decrypt_field,
should_decrypt_field,
)
from awx.main.utils.encryption import encrypt_field
from awx.main.notifications.email_backend import CustomEmailBackend
from awx.main.notifications.slack_backend import SlackBackend
from awx.main.notifications.twilio_backend import TwilioBackend
from awx.main.notifications.pagerduty_backend import PagerDutyBackend
from awx.main.notifications.hipchat_backend import HipChatBackend
from awx.main.notifications.mattermost_backend import MattermostBackend
from awx.main.notifications.webhook_backend import WebhookBackend
from awx.main.notifications.irc_backend import IrcBackend
logger = logging.getLogger('awx.main.migrations')
__all__ = ['replace_aesecb_fernet']
NOTIFICATION_TYPES = [('email', _('Email'), CustomEmailBackend),
('slack', _('Slack'), SlackBackend),
('twilio', _('Twilio'), TwilioBackend),
('pagerduty', _('Pagerduty'), PagerDutyBackend),
('hipchat', _('HipChat'), HipChatBackend),
('mattermost', _('Mattermost'), MattermostBackend),
('webhook', _('Webhook'), WebhookBackend),
('irc', _('IRC'), IrcBackend)]
PASSWORD_FIELDS = ('password', 'security_token', 'ssh_key_data', 'ssh_key_unlock',
'become_password', 'vault_password', 'secret', 'authorize_password')
def replace_aesecb_fernet(apps, schema_editor):
_notification_templates(apps)
_credentials(apps)
_unified_jobs(apps)
def _notification_templates(apps):
NotificationTemplate = apps.get_model('main', 'NotificationTemplate')
for nt in NotificationTemplate.objects.all():
CLASS_FOR_NOTIFICATION_TYPE = dict([(x[0], x[2]) for x in NOTIFICATION_TYPES])
notification_class = CLASS_FOR_NOTIFICATION_TYPE[nt.notification_type]
for field in filter(lambda x: notification_class.init_parameters[x]['type'] == "password",
notification_class.init_parameters):
if should_decrypt_field(nt.notification_configuration[field]):
nt.notification_configuration[field] = decrypt_field(nt, 'notification_configuration', subfield=field)
nt.notification_configuration[field] = encrypt_field(nt, 'notification_configuration', subfield=field)
nt.save()
def _credentials(apps):
for credential in apps.get_model('main', 'Credential').objects.all():
for field_name in PASSWORD_FIELDS:
value = getattr(credential, field_name)
if should_decrypt_field(value):
value = decrypt_field(credential, field_name)
setattr(credential, field_name, value)
setattr(credential, field_name, encrypt_field(credential, field_name))
credential.save()
def _unified_jobs(apps):
UnifiedJob = apps.get_model('main', 'UnifiedJob')
for uj in UnifiedJob.objects.all():
if uj.start_args is not None:
if should_decrypt_field(uj.start_args):
uj.start_args = decrypt_field(uj, 'start_args')
uj.start_args = encrypt_field(uj, 'start_args')
uj.save()
__all__ = []
def blank_old_start_args(apps, schema_editor):
@@ -91,53 +24,3 @@ def blank_old_start_args(apps, schema_editor):
logger.debug('Blanking job args for %s', uj.pk)
uj.start_args = ''
uj.save()
def encrypt_survey_passwords(apps, schema_editor):
_encrypt_survey_passwords(
apps.get_model('main', 'Job'),
apps.get_model('main', 'JobTemplate'),
apps.get_model('main', 'WorkflowJob'),
apps.get_model('main', 'WorkflowJobTemplate'),
)
def _encrypt_survey_passwords(Job, JobTemplate, WorkflowJob, WorkflowJobTemplate):
from awx.main.utils.encryption import encrypt_value
for _type in (JobTemplate, WorkflowJobTemplate):
for jt in _type.objects.exclude(survey_spec={}):
changed = False
if jt.survey_spec.get('spec', []):
for field in jt.survey_spec['spec']:
if field.get('type') == 'password' and field.get('default', ''):
default = field['default']
if default.startswith('$encrypted$'):
if default == '$encrypted$':
# If you have a survey_spec with a literal
# '$encrypted$' as the default, you have
# encountered a known bug in awx/Tower
# https://github.com/ansible/ansible-tower/issues/7800
logger.error(
'{}.pk={} survey_spec has ambiguous $encrypted$ default for {}, needs attention...'.format(jt, jt.pk, field['variable'])
)
field['default'] = ''
changed = True
continue
field['default'] = encrypt_value(field['default'], pk=None)
changed = True
if changed:
jt.save()
for _type in (Job, WorkflowJob):
for job in _type.objects.defer('result_stdout_text').exclude(survey_passwords={}).iterator():
changed = False
for key in job.survey_passwords:
if key in job.extra_vars:
extra_vars = json.loads(job.extra_vars)
if not extra_vars.get(key, '') or extra_vars[key].startswith('$encrypted$'):
continue
extra_vars[key] = encrypt_value(extra_vars[key], pk=None)
job.extra_vars = json.dumps(extra_vars)
changed = True
if changed:
job.save()

View File

@@ -1,89 +1,9 @@
import logging
from django.utils.timezone import now
from django.utils.text import slugify
from awx.main.models.base import PERM_INVENTORY_SCAN, PERM_INVENTORY_DEPLOY
from awx.main import utils
logger = logging.getLogger('awx.main.migrations')
def _create_fact_scan_project(ContentType, Project, org):
ct = ContentType.objects.get_for_model(Project)
name = u"Tower Fact Scan - {}".format(org.name if org else "No Organization")
proj = Project(name=name,
scm_url='https://github.com/ansible/awx-facts-playbooks',
scm_type='git',
scm_update_on_launch=True,
scm_update_cache_timeout=86400,
organization=org,
created=now(),
modified=now(),
polymorphic_ctype=ct)
proj.save()
slug_name = slugify(str(name)).replace(u'-', u'_')
proj.local_path = u'_%d__%s' % (int(proj.pk), slug_name)
proj.save()
return proj
def _create_fact_scan_projects(ContentType, Project, orgs):
return {org.id : _create_fact_scan_project(ContentType, Project, org) for org in orgs}
def _get_tower_scan_job_templates(JobTemplate):
return JobTemplate.objects.filter(job_type=PERM_INVENTORY_SCAN, project__isnull=True) \
.prefetch_related('inventory__organization')
def _get_orgs(Organization, job_template_ids):
return Organization.objects.filter(inventories__jobtemplates__in=job_template_ids).distinct()
def _migrate_scan_job_templates(apps):
JobTemplate = apps.get_model('main', 'JobTemplate')
Organization = apps.get_model('main', 'Organization')
ContentType = apps.get_model('contenttypes', 'ContentType')
Project = apps.get_model('main', 'Project')
project_no_org = None
# A scan job template with a custom project will retain the custom project.
JobTemplate.objects.filter(job_type=PERM_INVENTORY_SCAN, project__isnull=False).update(use_fact_cache=True, job_type=PERM_INVENTORY_DEPLOY)
# Scan jobs templates using Tower's default scan playbook will now point at
# the same playbook but in a github repo.
jts = _get_tower_scan_job_templates(JobTemplate)
if jts.count() == 0:
return
orgs = _get_orgs(Organization, jts.values_list('id'))
if orgs.count() == 0:
return
org_proj_map = _create_fact_scan_projects(ContentType, Project, orgs)
for jt in jts:
if jt.inventory and jt.inventory.organization:
jt.project_id = org_proj_map[jt.inventory.organization.id].id
# Job Templates without an Organization; through related Inventory
else:
if not project_no_org:
project_no_org = _create_fact_scan_project(ContentType, Project, None)
jt.project_id = project_no_org.id
jt.job_type = PERM_INVENTORY_DEPLOY
jt.playbook = "scan_facts.yml"
jt.use_fact_cache = True
jt.save()
def migrate_scan_job_templates(apps, schema_editor):
_migrate_scan_job_templates(apps)
def remove_scan_type_nodes(apps, schema_editor):
WorkflowJobTemplateNode = apps.get_model('main', 'WorkflowJobTemplateNode')
WorkflowJobNode = apps.get_model('main', 'WorkflowJobNode')

View File

@@ -270,6 +270,11 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
.filter(capacity__gt=0, enabled=True)
.values_list('hostname', flat=True)))
def set_default_policy_fields(self):
self.policy_instance_list = []
self.policy_instance_minimum = 0
self.policy_instance_percentage = 0
class TowerScheduleState(SingletonModel):
schedule_last_run = models.DateTimeField(auto_now_add=True)
@@ -289,6 +294,8 @@ def on_instance_group_saved(sender, instance, created=False, raw=False, **kwargs
if created or instance.has_policy_changes():
if not instance.is_containerized:
schedule_policy_task()
elif created or instance.is_containerized:
instance.set_default_policy_fields()
@receiver(post_save, sender=Instance)

View File

@@ -61,6 +61,7 @@ from awx.main.models.notifications import (
)
from awx.main.models.credential.injectors import _openstack_data
from awx.main.utils import _inventory_updates, region_sorting, get_licenser
from awx.main.utils.safe_yaml import sanitize_jinja
__all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate',
@@ -754,6 +755,13 @@ class Host(CommonModelNameNotUnique, RelatedJobsMixin):
update_host_smart_inventory_memberships.delay()
connection.on_commit(on_commit)
def clean_name(self):
try:
sanitize_jinja(self.name)
except ValueError as e:
raise ValidationError(str(e) + ": {}".format(self.name))
return self.name
def save(self, *args, **kwargs):
self._update_host_smart_inventory_memeberships()
super(Host, self).save(*args, **kwargs)
@@ -2036,9 +2044,25 @@ class azure_rm(PluginFileInjector):
for key, loc in old_filterables:
value = source_vars.get(key, None)
if value and isinstance(value, str):
user_filters.append('{} not in {}'.format(
loc, value.split(',')
))
# tags can be list of key:value pairs
# e.g. 'Creator:jmarshall, peanutbutter:jelly'
# or tags can be a list of keys
# e.g. 'Creator, peanutbutter'
if key == "tags":
# grab each key value pair
for kvpair in value.split(','):
# split into key and value
kv = kvpair.split(':')
# filter out any host that does not have key
# in their tags.keys() variable
user_filters.append('"{}" not in tags.keys()'.format(kv[0].strip()))
# if a value is provided, check that the key:value pair matches
if len(kv) > 1:
user_filters.append('tags["{}"] != "{}"'.format(kv[0].strip(), kv[1].strip()))
else:
user_filters.append('{} not in {}'.format(
loc, value.split(',')
))
if user_filters:
ret.setdefault('exclude_host_filters', [])
ret['exclude_host_filters'].extend(user_filters)

View File

@@ -634,7 +634,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
else:
# If for some reason we can't count the hosts then lets assume the impact as forks
if self.inventory is not None:
count_hosts = self.inventory.hosts.count()
count_hosts = self.inventory.total_hosts
if self.job_slice_count > 1:
# Integer division intentional
count_hosts = (count_hosts + self.job_slice_count - self.job_slice_number) // self.job_slice_count
@@ -1106,8 +1106,8 @@ class SystemJobOptions(BaseModel):
SYSTEM_JOB_TYPE = [
('cleanup_jobs', _('Remove jobs older than a certain number of days')),
('cleanup_activitystream', _('Remove activity stream entries older than a certain number of days')),
('clearsessions', _('Removes expired browser sessions from the database')),
('cleartokens', _('Removes expired OAuth 2 access tokens and refresh tokens'))
('cleanup_sessions', _('Removes expired browser sessions from the database')),
('cleanup_tokens', _('Removes expired OAuth 2 access tokens and refresh tokens'))
]
class Meta:
@@ -1182,18 +1182,19 @@ class SystemJobTemplate(UnifiedJobTemplate, SystemJobOptions):
for key in unallowed_vars:
rejected[key] = data.pop(key)
if 'days' in data:
try:
if type(data['days']) is bool:
raise ValueError
if float(data['days']) != int(data['days']):
raise ValueError
days = int(data['days'])
if days < 0:
raise ValueError
except ValueError:
errors_list.append(_("days must be a positive integer."))
rejected['days'] = data.pop('days')
if self.job_type in ('cleanup_jobs', 'cleanup_activitystream'):
if 'days' in data:
try:
if isinstance(data['days'], (bool, type(None))):
raise ValueError
if float(data['days']) != int(data['days']):
raise ValueError
days = int(data['days'])
if days < 0:
raise ValueError
except ValueError:
errors_list.append(_("days must be a positive integer."))
rejected['days'] = data.pop('days')
if errors_list:
errors['extra_vars'] = errors_list

View File

@@ -269,6 +269,7 @@ class JobNotificationMixin(object):
'timeout', 'use_fact_cache', 'launch_type', 'status', 'failed', 'started', 'finished',
'elapsed', 'job_explanation', 'execution_node', 'controller_node', 'allow_simultaneous',
'scm_revision', 'diff_mode', 'job_slice_number', 'job_slice_count', 'custom_virtualenv',
'approval_status', 'approval_node_name', 'workflow_url',
{'host_status_counts': ['skipped', 'ok', 'changed', 'failures', 'dark']},
{'playbook_counts': ['play_count', 'task_count']},
{'summary_fields': [{'inventory': ['id', 'name', 'description', 'has_active_failures',
@@ -366,6 +367,9 @@ class JobNotificationMixin(object):
'verbosity': 0},
'job_friendly_name': 'Job',
'url': 'https://towerhost/#/jobs/playbook/1010',
'approval_status': 'approved',
'approval_node_name': 'Approve Me',
'workflow_url': 'https://towerhost/#/workflows/1010',
'job_metadata': """{'url': 'https://towerhost/$/jobs/playbook/13',
'traceback': '',
'status': 'running',

View File

@@ -1,4 +1,5 @@
# Python
import logging
import re
# Django
@@ -22,6 +23,9 @@ DATA_URI_RE = re.compile(r'.*') # FIXME
__all__ = ['OAuth2AccessToken', 'OAuth2Application']
logger = logging.getLogger('awx.main.models.oauth')
class OAuth2Application(AbstractApplication):
class Meta:
@@ -120,15 +124,27 @@ class OAuth2AccessToken(AbstractAccessToken):
def is_valid(self, scopes=None):
valid = super(OAuth2AccessToken, self).is_valid(scopes)
if valid:
try:
self.validate_external_users()
except oauth2.AccessDeniedError:
logger.exception(f'Failed to authenticate {self.user.username}')
return False
self.last_used = now()
connection.on_commit(lambda: self.save(update_fields=['last_used']))
def _update_last_used():
if OAuth2AccessToken.objects.filter(pk=self.pk).exists():
self.save(update_fields=['last_used'])
connection.on_commit(_update_last_used)
return valid
def save(self, *args, **kwargs):
def validate_external_users(self):
if self.user and settings.ALLOW_OAUTH2_FOR_EXTERNAL_USERS is False:
external_account = get_external_account(self.user)
if external_account is not None:
raise oauth2.AccessDeniedError(_(
'OAuth2 Tokens cannot be created by users associated with an external authentication provider ({})'
).format(external_account))
def save(self, *args, **kwargs):
self.validate_external_users()
super(OAuth2AccessToken, self).save(*args, **kwargs)

View File

@@ -483,6 +483,12 @@ class ProjectUpdate(UnifiedJob, ProjectOptions, JobNotificationMixin, TaskManage
choices=PROJECT_UPDATE_JOB_TYPE_CHOICES,
default='check',
)
job_tags = models.CharField(
max_length=1024,
blank=True,
default='',
help_text=_('Parts of the project update playbook that will be run.'),
)
scm_revision = models.CharField(
max_length=1024,
blank=True,
@@ -587,3 +593,24 @@ class ProjectUpdate(UnifiedJob, ProjectOptions, JobNotificationMixin, TaskManage
if not selected_groups:
return self.global_instance_groups
return selected_groups
def save(self, *args, **kwargs):
added_update_fields = []
if not self.job_tags:
job_tags = ['update_{}'.format(self.scm_type)]
if self.job_type == 'run':
job_tags.append('install_roles')
job_tags.append('install_collections')
self.job_tags = ','.join(job_tags)
added_update_fields.append('job_tags')
if self.scm_delete_on_update and 'delete' not in self.job_tags and self.job_type == 'check':
self.job_tags = ','.join([self.job_tags, 'delete'])
added_update_fields.append('job_tags')
elif (not self.scm_delete_on_update) and 'delete' in self.job_tags:
job_tags = self.job_tags.split(',')
job_tags.remove('delete')
self.job_tags = ','.join(job_tags)
added_update_fields.append('job_tags')
if 'update_fields' in kwargs:
kwargs['update_fields'].extend(added_update_fields)
return super(ProjectUpdate, self).save(*args, **kwargs)

View File

@@ -6,15 +6,24 @@ class CustomNotificationBase(object):
DEFAULT_MSG = "{{ job_friendly_name }} #{{ job.id }} '{{ job.name }}' {{ job.status }}: {{ url }}"
DEFAULT_BODY = "{{ job_friendly_name }} #{{ job.id }} had status {{ job.status }}, view details at {{ url }}\n\n{{ job_metadata }}"
DEFAULT_APPROVAL_RUNNING_MSG = 'The approval node "{{ approval_node_name }}" needs review. This node can be viewed at: {{ workflow_url }}'
DEFAULT_APPROVAL_RUNNING_BODY = ('The approval node "{{ approval_node_name }}" needs review. '
'This approval node can be viewed at: {{ workflow_url }}\n\n{{ job_metadata }}')
DEFAULT_APPROVAL_APPROVED_MSG = 'The approval node "{{ approval_node_name }}" was approved. {{ workflow_url }}'
DEFAULT_APPROVAL_APPROVED_BODY = 'The approval node "{{ approval_node_name }}" was approved. {{ workflow_url }}\n\n{{ job_metadata }}'
DEFAULT_APPROVAL_TIMEOUT_MSG = 'The approval node "{{ approval_node_name }}" has timed out. {{ workflow_url }}'
DEFAULT_APPROVAL_TIMEOUT_BODY = 'The approval node "{{ approval_node_name }}" has timed out. {{ workflow_url }}\n\n{{ job_metadata }}'
DEFAULT_APPROVAL_DENIED_MSG = 'The approval node "{{ approval_node_name }}" was denied. {{ workflow_url }}'
DEFAULT_APPROVAL_DENIED_BODY = 'The approval node "{{ approval_node_name }}" was denied. {{ workflow_url }}\n\n{{ job_metadata }}'
default_messages = {"started": {"message": DEFAULT_MSG, "body": None},
"success": {"message": DEFAULT_MSG, "body": None},
"error": {"message": DEFAULT_MSG, "body": None},
"workflow_approval": {"running": {"message": 'The approval node "{{ approval_node_name }}" needs review. '
'This node can be viewed at: {{ workflow_url }}',
"body": None},
"approved": {"message": 'The approval node "{{ approval_node_name }}" was approved. {{ workflow_url }}',
"body": None},
"timed_out": {"message": 'The approval node "{{ approval_node_name }}" has timed out. {{ workflow_url }}',
"body": None},
"denied": {"message": 'The approval node "{{ approval_node_name }}" was denied. {{ workflow_url }}',
"body": None}}}
"workflow_approval": {"running": {"message": DEFAULT_APPROVAL_RUNNING_MSG, "body": None},
"approved": {"message": DEFAULT_APPROVAL_APPROVED_MSG, "body": None},
"timed_out": {"message": DEFAULT_APPROVAL_TIMEOUT_MSG, "body": None},
"denied": {"message": DEFAULT_APPROVAL_DENIED_MSG, "body": None}}}

View File

@@ -8,6 +8,18 @@ from awx.main.notifications.custom_notification_base import CustomNotificationBa
DEFAULT_MSG = CustomNotificationBase.DEFAULT_MSG
DEFAULT_BODY = CustomNotificationBase.DEFAULT_BODY
DEFAULT_APPROVAL_RUNNING_MSG = CustomNotificationBase.DEFAULT_APPROVAL_RUNNING_MSG
DEFAULT_APPROVAL_RUNNING_BODY = CustomNotificationBase.DEFAULT_APPROVAL_RUNNING_BODY
DEFAULT_APPROVAL_APPROVED_MSG = CustomNotificationBase.DEFAULT_APPROVAL_APPROVED_MSG
DEFAULT_APPROVAL_APPROVED_BODY = CustomNotificationBase.DEFAULT_APPROVAL_APPROVED_BODY
DEFAULT_APPROVAL_TIMEOUT_MSG = CustomNotificationBase.DEFAULT_APPROVAL_TIMEOUT_MSG
DEFAULT_APPROVAL_TIMEOUT_BODY = CustomNotificationBase.DEFAULT_APPROVAL_TIMEOUT_BODY
DEFAULT_APPROVAL_DENIED_MSG = CustomNotificationBase.DEFAULT_APPROVAL_DENIED_MSG
DEFAULT_APPROVAL_DENIED_BODY = CustomNotificationBase.DEFAULT_APPROVAL_DENIED_BODY
class CustomEmailBackend(EmailBackend, CustomNotificationBase):
@@ -26,10 +38,10 @@ class CustomEmailBackend(EmailBackend, CustomNotificationBase):
default_messages = {"started": {"message": DEFAULT_MSG, "body": DEFAULT_BODY},
"success": {"message": DEFAULT_MSG, "body": DEFAULT_BODY},
"error": {"message": DEFAULT_MSG, "body": DEFAULT_BODY},
"workflow_approval": {"running": {"message": DEFAULT_MSG, "body": DEFAULT_BODY},
"approved": {"message": DEFAULT_MSG, "body": DEFAULT_BODY},
"timed_out": {"message": DEFAULT_MSG, "body": DEFAULT_BODY},
"denied": {"message": DEFAULT_MSG, "body": DEFAULT_BODY}}}
"workflow_approval": {"running": {"message": DEFAULT_APPROVAL_RUNNING_MSG, "body": DEFAULT_APPROVAL_RUNNING_BODY},
"approved": {"message": DEFAULT_APPROVAL_APPROVED_MSG, "body": DEFAULT_APPROVAL_APPROVED_BODY},
"timed_out": {"message": DEFAULT_APPROVAL_TIMEOUT_MSG, "body": DEFAULT_APPROVAL_TIMEOUT_BODY},
"denied": {"message": DEFAULT_APPROVAL_DENIED_MSG, "body": DEFAULT_APPROVAL_DENIED_BODY}}}
def format_body(self, body):
# leave body unchanged (expect a string)

View File

@@ -15,7 +15,6 @@ class DependencyGraph(object):
INVENTORY_UPDATES = 'inventory_updates'
JOB_TEMPLATE_JOBS = 'job_template_jobs'
JOB_PROJECT_IDS = 'job_project_ids'
JOB_INVENTORY_IDS = 'job_inventory_ids'
SYSTEM_JOB = 'system_job'
@@ -41,8 +40,6 @@ class DependencyGraph(object):
Track runnable job related project and inventory to ensure updates
don't run while a job needing those resources is running.
'''
# project_id -> True / False
self.data[self.JOB_PROJECT_IDS] = {}
# inventory_id -> True / False
self.data[self.JOB_INVENTORY_IDS] = {}
@@ -66,7 +63,7 @@ class DependencyGraph(object):
def get_now(self):
return tz_now()
def mark_system_job(self):
self.data[self.SYSTEM_JOB] = False
@@ -81,15 +78,13 @@ class DependencyGraph(object):
def mark_job_template_job(self, job):
self.data[self.JOB_INVENTORY_IDS][job.inventory_id] = False
self.data[self.JOB_PROJECT_IDS][job.project_id] = False
self.data[self.JOB_TEMPLATE_JOBS][job.job_template_id] = False
def mark_workflow_job(self, job):
self.data[self.WORKFLOW_JOB_TEMPLATES_JOBS][job.workflow_job_template_id] = False
def can_project_update_run(self, job):
return self.data[self.JOB_PROJECT_IDS].get(job.project_id, True) and \
self.data[self.PROJECT_UPDATES].get(job.project_id, True)
return self.data[self.PROJECT_UPDATES].get(job.project_id, True)
def can_inventory_update_run(self, job):
return self.data[self.JOB_INVENTORY_IDS].get(job.inventory_source.inventory_id, True) and \

View File

@@ -173,7 +173,7 @@ def generate_tmp_kube_config(credential, namespace):
"current-context": host_input
}
if credential.get_input('verify_ssl'):
if credential.get_input('verify_ssl') and 'ssl_ca_cert' in credential.inputs:
config["clusters"][0]["cluster"]["certificate-authority-data"] = b64encode(
credential.get_input('ssl_ca_cert').encode() # encode to bytes
).decode() # decode the base64 data into a str

View File

@@ -258,19 +258,24 @@ class TaskManager():
for group in InstanceGroup.objects.all():
if group.is_containerized or group.controller_id:
continue
match = group.find_largest_idle_instance()
match = group.fit_task_to_most_remaining_capacity_instance(task)
if match:
break
task.instance_group = rampart_group
if task.supports_isolation():
task.controller_node = match.hostname
if match is None:
logger.warn(
'No available capacity to run containerized <{}>.'.format(task.log_format)
)
else:
# project updates and inventory updates don't *actually* run in pods,
# so just pick *any* non-isolated, non-containerized host and use it
# as the execution node
task.execution_node = match.hostname
logger.debug('Submitting containerized {} to queue {}.'.format(
task.log_format, task.execution_node))
if task.supports_isolation():
task.controller_node = match.hostname
else:
# project updates and inventory updates don't *actually* run in pods,
# so just pick *any* non-isolated, non-containerized host and use it
# as the execution node
task.execution_node = match.hostname
logger.debug('Submitting containerized {} to queue {}.'.format(
task.log_format, task.execution_node))
else:
task.instance_group = rampart_group
if instance is not None:

View File

@@ -5,11 +5,15 @@ import logging
# AWX
from awx.main.scheduler import TaskManager
from awx.main.dispatch.publish import task
from awx.main.utils.db import migration_in_progress_check_or_relase
logger = logging.getLogger('awx.main.scheduler')
@task()
def run_task_manager():
if migration_in_progress_check_or_relase():
logger.debug("Not running task manager because migration is in progress.")
return
logger.debug("Running Tower task manager.")
TaskManager().schedule()

View File

@@ -71,7 +71,7 @@ from awx.main.utils import (get_ssh_version, update_scm_url,
ignore_inventory_group_removal, extract_ansible_vars, schedule_task_manager,
get_awx_version)
from awx.main.utils.ansible import read_ansible_config
from awx.main.utils.common import get_ansible_version, _get_ansible_version, get_custom_venv_choices
from awx.main.utils.common import _get_ansible_version, get_custom_venv_choices
from awx.main.utils.safe_yaml import safe_dump, sanitize_jinja
from awx.main.utils.reload import stop_local_services
from awx.main.utils.pglock import advisory_lock
@@ -263,6 +263,12 @@ def apply_cluster_membership_policies():
logger.debug('Cluster policy computation finished in {} seconds'.format(time.time() - started_compute))
@task(queue='tower_broadcast_all', exchange_type='fanout')
def set_migration_flag():
logger.debug('Received migration-in-progress signal, will serve redirect.')
cache.set('migration_in_progress', True)
@task(queue='tower_broadcast_all', exchange_type='fanout')
def handle_setting_changes(setting_keys):
orig_len = len(setting_keys)
@@ -1336,7 +1342,7 @@ class BaseTask(object):
ansible_runner.utils.dump_artifacts(params)
isolated_manager_instance = isolated_manager.IsolatedManager(
cancelled_callback=lambda: self.update_model(self.instance.pk).cancel_flag,
canceled_callback=lambda: self.update_model(self.instance.pk).cancel_flag,
check_callback=self.check_handler,
pod_manager=pod_manager
)
@@ -1734,14 +1740,16 @@ class RunJob(BaseTask):
project_path = job.project.get_project_path(check_if_exists=False)
job_revision = job.project.scm_revision
needs_sync = True
sync_needs = []
all_sync_needs = ['update_{}'.format(job.project.scm_type), 'install_roles', 'install_collections']
if not job.project.scm_type:
# manual projects are not synced, user has responsibility for that
needs_sync = False
pass # manual projects are not synced, user has responsibility for that
elif not os.path.exists(project_path):
logger.debug('Performing fresh clone of {} on this instance.'.format(job.project))
sync_needs = all_sync_needs
elif not job.project.scm_revision:
logger.debug('Revision not known for {}, will sync with remote'.format(job.project))
sync_needs = all_sync_needs
elif job.project.scm_type == 'git':
git_repo = git.Repo(project_path)
try:
@@ -1752,23 +1760,27 @@ class RunJob(BaseTask):
if desired_revision == current_revision:
job_revision = desired_revision
logger.info('Skipping project sync for {} because commit is locally available'.format(job.log_format))
needs_sync = False
else:
sync_needs = all_sync_needs
except (ValueError, BadGitName):
logger.debug('Needed commit for {} not in local source tree, will sync with remote'.format(job.log_format))
sync_needs = all_sync_needs
else:
sync_needs = all_sync_needs
# Galaxy requirements are not supported for manual projects
if not needs_sync and job.project.scm_type:
if not sync_needs and job.project.scm_type:
# see if we need a sync because of presence of roles
galaxy_req_path = os.path.join(project_path, 'roles', 'requirements.yml')
if os.path.exists(galaxy_req_path):
logger.debug('Running project sync for {} because of galaxy role requirements.'.format(job.log_format))
needs_sync = True
sync_needs.append('install_roles')
galaxy_collections_req_path = os.path.join(project_path, 'collections', 'requirements.yml')
if os.path.exists(galaxy_collections_req_path):
logger.debug('Running project sync for {} because of galaxy collections requirements.'.format(job.log_format))
needs_sync = True
sync_needs.append('install_collections')
if needs_sync:
if sync_needs:
pu_ig = job.instance_group
pu_en = job.execution_node
if job.is_isolated() is True:
@@ -1778,6 +1790,7 @@ class RunJob(BaseTask):
sync_metafields = dict(
launch_type="sync",
job_type='run',
job_tags=','.join(sync_needs),
status='running',
instance_group = pu_ig,
execution_node=pu_en,
@@ -1785,6 +1798,8 @@ class RunJob(BaseTask):
)
if job.scm_branch and job.scm_branch != job.project.scm_branch:
sync_metafields['scm_branch'] = job.scm_branch
if 'update_' not in sync_metafields['job_tags']:
sync_metafields['scm_revision'] = job_revision
local_project_sync = job.project.create_project_update(_eager_fields=sync_metafields)
# save the associated job before calling run() so that a
# cancel() call on the job can cancel the project update
@@ -1929,7 +1944,8 @@ class RunProjectUpdate(BaseTask):
env['TMP'] = settings.AWX_PROOT_BASE_PATH
env['PROJECT_UPDATE_ID'] = str(project_update.pk)
env['ANSIBLE_CALLBACK_PLUGINS'] = self.get_path_to('..', 'plugins', 'callback')
env['ANSIBLE_GALAXY_IGNORE'] = True
if settings.GALAXY_IGNORE_CERTS:
env['ANSIBLE_GALAXY_IGNORE'] = True
# Set up the public Galaxy server, if enabled
if settings.PUBLIC_GALAXY_ENABLED:
galaxy_servers = [settings.PUBLIC_GALAXY_SERVER]
@@ -2008,8 +2024,8 @@ class RunProjectUpdate(BaseTask):
args = []
if getattr(settings, 'PROJECT_UPDATE_VVV', False):
args.append('-vvv')
else:
args.append('-v')
if project_update.job_tags:
args.extend(['-t', project_update.job_tags])
return args
def build_extra_vars_file(self, project_update, private_data_dir):
@@ -2023,28 +2039,16 @@ class RunProjectUpdate(BaseTask):
scm_branch = project_update.project.scm_revision
elif not scm_branch:
scm_branch = {'hg': 'tip'}.get(project_update.scm_type, 'HEAD')
if project_update.job_type == 'check':
roles_enabled = False
collections_enabled = False
else:
roles_enabled = getattr(settings, 'AWX_ROLES_ENABLED', True)
collections_enabled = getattr(settings, 'AWX_COLLECTIONS_ENABLED', True)
# collections were introduced in Ansible version 2.8
if Version(get_ansible_version()) <= Version('2.8'):
collections_enabled = False
extra_vars.update({
'project_path': project_update.get_project_path(check_if_exists=False),
'insights_url': settings.INSIGHTS_URL_BASE,
'awx_license_type': get_license(show_key=False).get('license_type', 'UNLICENSED'),
'awx_version': get_awx_version(),
'scm_type': project_update.scm_type,
'scm_url': scm_url,
'scm_branch': scm_branch,
'scm_clean': project_update.scm_clean,
'scm_delete_on_update': project_update.scm_delete_on_update if project_update.job_type == 'check' else False,
'scm_full_checkout': True if project_update.job_type == 'run' else False,
'roles_enabled': roles_enabled,
'collections_enabled': collections_enabled,
'roles_enabled': settings.AWX_ROLES_ENABLED,
'collections_enabled': settings.AWX_COLLECTIONS_ENABLED,
})
if project_update.job_type != 'check' and self.job_private_data_dir:
extra_vars['collections_destination'] = os.path.join(self.job_private_data_dir, 'requirements_collections')
@@ -2156,7 +2160,7 @@ class RunProjectUpdate(BaseTask):
try:
instance.refresh_from_db(fields=['cancel_flag'])
if instance.cancel_flag:
logger.debug("ProjectUpdate({0}) was cancelled".format(instance.pk))
logger.debug("ProjectUpdate({0}) was canceled".format(instance.pk))
return
fcntl.lockf(self.lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
break
@@ -2185,7 +2189,10 @@ class RunProjectUpdate(BaseTask):
project_path = instance.project.get_project_path(check_if_exists=False)
if os.path.exists(project_path):
git_repo = git.Repo(project_path)
self.original_branch = git_repo.active_branch
if git_repo.head.is_detached:
self.original_branch = git_repo.head.commit
else:
self.original_branch = git_repo.active_branch
@staticmethod
def make_local_copy(project_path, destination_folder, scm_type, scm_revision):
@@ -2217,26 +2224,29 @@ class RunProjectUpdate(BaseTask):
copy_tree(project_path, destination_folder)
def post_run_hook(self, instance, status):
if self.job_private_data_dir:
# copy project folder before resetting to default branch
# because some git-tree-specific resources (like submodules) might matter
self.make_local_copy(
instance.get_project_path(check_if_exists=False), os.path.join(self.job_private_data_dir, 'project'),
instance.scm_type, self.playbook_new_revision
)
if self.original_branch:
# for git project syncs, non-default branches can be problems
# restore to branch the repo was on before this run
try:
self.original_branch.checkout()
except Exception:
# this could have failed due to dirty tree, but difficult to predict all cases
logger.exception('Failed to restore project repo to prior state after {}'.format(instance.log_format))
self.release_lock(instance)
# To avoid hangs, very important to release lock even if errors happen here
try:
if self.playbook_new_revision:
instance.scm_revision = self.playbook_new_revision
instance.save(update_fields=['scm_revision'])
if self.job_private_data_dir:
# copy project folder before resetting to default branch
# because some git-tree-specific resources (like submodules) might matter
self.make_local_copy(
instance.get_project_path(check_if_exists=False), os.path.join(self.job_private_data_dir, 'project'),
instance.scm_type, instance.scm_revision
)
if self.original_branch:
# for git project syncs, non-default branches can be problems
# restore to branch the repo was on before this run
try:
self.original_branch.checkout()
except Exception:
# this could have failed due to dirty tree, but difficult to predict all cases
logger.exception('Failed to restore project repo to prior state after {}'.format(instance.log_format))
finally:
self.release_lock(instance)
p = instance.project
if self.playbook_new_revision:
instance.scm_revision = self.playbook_new_revision
instance.save(update_fields=['scm_revision'])
if instance.job_type == 'check' and status not in ('failed', 'canceled',):
if self.playbook_new_revision:
p.scm_revision = self.playbook_new_revision
@@ -2497,6 +2507,7 @@ class RunInventoryUpdate(BaseTask):
_eager_fields=dict(
launch_type="sync",
job_type='run',
job_tags='update_{},install_collections'.format(source_project.scm_type), # roles are never valid for inventory
status='running',
execution_node=inventory_update.execution_node,
instance_group = inventory_update.instance_group,
@@ -2732,10 +2743,11 @@ class RunSystemJob(BaseTask):
json_vars = {}
else:
json_vars = json.loads(system_job.extra_vars)
if 'days' in json_vars:
args.extend(['--days', str(json_vars.get('days', 60))])
if 'dry_run' in json_vars and json_vars['dry_run']:
args.extend(['--dry-run'])
if system_job.job_type in ('cleanup_jobs', 'cleanup_activitystream'):
if 'days' in json_vars:
args.extend(['--days', str(json_vars.get('days', 60))])
if 'dry_run' in json_vars and json_vars['dry_run']:
args.extend(['--dry-run'])
if system_job.job_type == 'cleanup_jobs':
args.extend(['--jobs', '--project-updates', '--inventory-updates',
'--management-jobs', '--ad-hoc-commands', '--workflow-jobs',

View File

@@ -1,7 +1,7 @@
---
- name: Hello World Sample
hosts: all
tasks:
- name: Hello Message
debug:
msg: "Hello World!"

View File

@@ -1,7 +1,7 @@
---
- name: Hello World Sample
hosts: all
tasks:
- name: Hello Message
debug:
msg: "Hello World!"

View File

@@ -1 +1,2 @@
---
- hosts: all

View File

@@ -1 +1,2 @@
---
- import_playbook: foo

View File

@@ -1 +1,2 @@
---
- include: foo

View File

@@ -3,6 +3,10 @@ conditional_groups:
default_host_filters: []
exclude_host_filters:
- resource_group not in ['foo_resources', 'bar_resources']
- '"Creator" not in tags.keys()'
- tags["Creator"] != "jmarshall"
- '"peanutbutter" not in tags.keys()'
- tags["peanutbutter"] != "jelly"
- location not in ['southcentralus', 'westus']
fail_on_template_errors: false
hostvar_expressions:

View File

@@ -7,4 +7,5 @@ locations = southcentralus,westus
base_source_var = value_of_var
use_private_ip = True
resource_groups = foo_resources,bar_resources
tags = Creator:jmarshall, peanutbutter:jelly

View File

@@ -25,6 +25,8 @@ EXPECTED_VALUES = {
'awx_custom_virtualenvs_total':0.0,
'awx_running_jobs_total':0.0,
'awx_instance_capacity':100.0,
'awx_instance_consumed_capacity':0.0,
'awx_instance_remaining_capacity':100.0,
'awx_instance_cpu':0.0,
'awx_instance_memory':0.0,
'awx_instance_info':1.0,

View File

@@ -1439,3 +1439,15 @@ def test_create_credential_with_invalid_url_xfail(post, organization, admin, url
assert response.status_code == status
if status != 201:
assert response.data['inputs']['server_url'] == [msg]
@pytest.mark.django_db
def test_external_credential_rbac_test_endpoint(post, alice, external_credential):
url = reverse('api:credential_external_test', kwargs={'pk': external_credential.pk})
data = {'metadata': {'key': 'some_key'}}
external_credential.read_role.members.add(alice)
assert post(url, data, alice).status_code == 403
external_credential.use_role.members.add(alice)
assert post(url, data, alice).status_code == 202

View File

@@ -85,14 +85,35 @@ def test_update_credential_type_in_use_xfail(patch, delete, admin):
Credential(credential_type=_type, name='My Custom Cred').save()
url = reverse('api:credential_type_detail', kwargs={'pk': _type.pk})
response = patch(url, {'name': 'Some Other Name'}, admin)
assert response.status_code == 200
patch(url, {'name': 'Some Other Name'}, admin, expect=200)
url = reverse('api:credential_type_detail', kwargs={'pk': _type.pk})
response = patch(url, {'inputs': {}}, admin)
assert response.status_code == 403
response = patch(url, {'inputs': {}}, admin, expect=403)
assert response.data['detail'] == 'Modifications to inputs are not allowed for credential types that are in use'
assert delete(url, admin).status_code == 403
response = delete(url, admin, expect=403)
assert response.data['detail'] == 'Credential types that are in use cannot be deleted'
@pytest.mark.django_db
def test_update_credential_type_unvalidated_inputs(post, patch, admin):
simple_inputs = {'fields': [
{'id': 'api_token', 'label': 'fooo'}
]}
response = post(
url=reverse('api:credential_type_list'),
data={'name': 'foo', 'kind': 'cloud', 'inputs': simple_inputs},
user=admin,
expect=201
)
# validation adds the type field to the input
_type = CredentialType.objects.get(pk=response.data['id'])
Credential(credential_type=_type, name='My Custom Cred').save()
# should not raise an error because we should only compare
# post-validation values to other post-validation values
url = reverse('api:credential_type_detail', kwargs={'pk': _type.id})
patch(url, {'inputs': simple_inputs}, admin, expect=200)
@pytest.mark.django_db
@@ -460,3 +481,12 @@ def test_create_with_undefined_template_variable_xfail(post, admin):
}, admin)
assert response.status_code == 400
assert "'api_tolkien' is undefined" in json.dumps(response.data)
@pytest.mark.django_db
def test_credential_type_rbac_external_test(post, alice, admin, credentialtype_external):
# only admins may use the credential type test endpoint
url = reverse('api:credential_type_external_test', kwargs={'pk': credentialtype_external.pk})
data = {'inputs': {}, 'metadata': {}}
assert post(url, data, admin).status_code == 202
assert post(url, data, alice).status_code == 403

View File

@@ -274,3 +274,21 @@ def test_instance_group_update_fields(patch, instance, instance_group, admin, co
assert ["Containerized instances may not be managed via the API"] == resp.data['policy_instance_minimum']
resp = patch(cg_url, {'policy_instance_list':[instance.hostname]}, admin)
assert ["Containerized instances may not be managed via the API"] == resp.data['policy_instance_list']
@pytest.mark.django_db
def test_containerized_group_default_fields(instance_group, kube_credential):
ig = InstanceGroup(name="test_policy_field_defaults")
ig.policy_instance_list = [1]
ig.policy_instance_minimum = 5
ig.policy_instance_percentage = 5
ig.save()
assert ig.policy_instance_list == [1]
assert ig.policy_instance_minimum == 5
assert ig.policy_instance_percentage == 5
ig.credential = kube_credential
ig.save()
assert ig.policy_instance_list == []
assert ig.policy_instance_minimum == 0
assert ig.policy_instance_percentage == 0

View File

@@ -1,6 +1,8 @@
import pytest
import base64
import contextlib
import json
from unittest import mock
from django.db import connection
from django.test.utils import override_settings
@@ -14,6 +16,18 @@ from awx.sso.models import UserEnterpriseAuth
from oauth2_provider.models import RefreshToken
@contextlib.contextmanager
def immediate_on_commit():
"""
Context manager executing transaction.on_commit() hooks immediately as
if the connection was in auto-commit mode.
"""
def on_commit(func):
func()
with mock.patch('django.db.connection.on_commit', side_effect=on_commit) as patch:
yield patch
@pytest.mark.django_db
def test_personal_access_token_creation(oauth_application, post, alice):
url = drf_reverse('api:oauth_authorization_root_view') + 'token/'
@@ -54,6 +68,41 @@ def test_token_creation_disabled_for_external_accounts(oauth_application, post,
assert AccessToken.objects.count() == 0
@pytest.mark.django_db
def test_existing_token_disabled_for_external_accounts(oauth_application, get, post, admin):
UserEnterpriseAuth(user=admin, provider='radius').save()
url = drf_reverse('api:oauth_authorization_root_view') + 'token/'
with override_settings(RADIUS_SERVER='example.org', ALLOW_OAUTH2_FOR_EXTERNAL_USERS=True):
resp = post(
url,
data='grant_type=password&username=admin&password=admin&scope=read',
content_type='application/x-www-form-urlencoded',
HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([
oauth_application.client_id, oauth_application.client_secret
])))),
status=201
)
token = json.loads(resp.content)['access_token']
assert AccessToken.objects.count() == 1
with immediate_on_commit():
resp = get(
drf_reverse('api:user_me_list', kwargs={'version': 'v2'}),
HTTP_AUTHORIZATION='Bearer ' + token,
status=200
)
assert json.loads(resp.content)['results'][0]['username'] == 'admin'
with override_settings(RADIUS_SERVER='example.org', ALLOW_OAUTH2_FOR_EXTERNAL_USER=False):
with immediate_on_commit():
resp = get(
drf_reverse('api:user_me_list', kwargs={'version': 'v2'}),
HTTP_AUTHORIZATION='Bearer ' + token,
status=401
)
assert b'To establish a login session' in resp.content
@pytest.mark.django_db
def test_pat_creation_no_default_scope(oauth_application, post, admin):
# tests that the default scope is overriden

View File

@@ -0,0 +1,173 @@
import json
from cryptography.fernet import InvalidToken
from django.test.utils import override_settings
from django.conf import settings
import pytest
from awx.main import models
from awx.conf.models import Setting
from awx.main.management.commands import regenerate_secret_key
from awx.main.utils.encryption import encrypt_field, decrypt_field, encrypt_value
PREFIX = '$encrypted$UTF8$AESCBC$'
@pytest.mark.django_db
class TestKeyRegeneration:
def test_encrypted_ssh_password(self, credential):
# test basic decryption
assert credential.inputs['password'].startswith(PREFIX)
assert credential.get_input('password') == 'secret'
# re-key the credential
new_key = regenerate_secret_key.Command().handle()
new_cred = models.Credential.objects.get(pk=credential.pk)
assert credential.inputs['password'] != new_cred.inputs['password']
# verify that the old SECRET_KEY doesn't work
with pytest.raises(InvalidToken):
new_cred.get_input('password')
# verify that the new SECRET_KEY *does* work
with override_settings(SECRET_KEY=new_key):
assert new_cred.get_input('password') == 'secret'
def test_encrypted_setting_values(self):
# test basic decryption
settings.LOG_AGGREGATOR_PASSWORD = 'sensitive'
s = Setting.objects.filter(key='LOG_AGGREGATOR_PASSWORD').first()
assert s.value.startswith(PREFIX)
assert settings.LOG_AGGREGATOR_PASSWORD == 'sensitive'
# re-key the setting value
new_key = regenerate_secret_key.Command().handle()
new_setting = Setting.objects.filter(key='LOG_AGGREGATOR_PASSWORD').first()
assert s.value != new_setting.value
# wipe out the local cache so the value is pulled from the DB again
settings.cache.delete('LOG_AGGREGATOR_PASSWORD')
# verify that the old SECRET_KEY doesn't work
with pytest.raises(InvalidToken):
settings.LOG_AGGREGATOR_PASSWORD
# verify that the new SECRET_KEY *does* work
with override_settings(SECRET_KEY=new_key):
assert settings.LOG_AGGREGATOR_PASSWORD == 'sensitive'
def test_encrypted_notification_secrets(self, notification_template_with_encrypt):
# test basic decryption
nt = notification_template_with_encrypt
nc = nt.notification_configuration
assert nc['token'].startswith(PREFIX)
Slack = nt.CLASS_FOR_NOTIFICATION_TYPE[nt.notification_type]
class TestBackend(Slack):
def __init__(self, *args, **kw):
assert kw['token'] == 'token'
def send_messages(self, messages):
pass
nt.CLASS_FOR_NOTIFICATION_TYPE['test'] = TestBackend
nt.notification_type = 'test'
nt.send('Subject', 'Body')
# re-key the notification config
new_key = regenerate_secret_key.Command().handle()
new_nt = models.NotificationTemplate.objects.get(pk=nt.pk)
assert nt.notification_configuration['token'] != new_nt.notification_configuration['token']
# verify that the old SECRET_KEY doesn't work
with pytest.raises(InvalidToken):
new_nt.CLASS_FOR_NOTIFICATION_TYPE['test'] = TestBackend
new_nt.notification_type = 'test'
new_nt.send('Subject', 'Body')
# verify that the new SECRET_KEY *does* work
with override_settings(SECRET_KEY=new_key):
new_nt.send('Subject', 'Body')
def test_job_start_args(self, job_factory):
# test basic decryption
job = job_factory()
job.start_args = json.dumps({'foo': 'bar'})
job.start_args = encrypt_field(job, field_name='start_args')
job.save()
assert job.start_args.startswith(PREFIX)
# re-key the start_args
new_key = regenerate_secret_key.Command().handle()
new_job = models.Job.objects.get(pk=job.pk)
assert new_job.start_args != job.start_args
# verify that the old SECRET_KEY doesn't work
with pytest.raises(InvalidToken):
decrypt_field(new_job, field_name='start_args')
# verify that the new SECRET_KEY *does* work
with override_settings(SECRET_KEY=new_key):
assert json.loads(
decrypt_field(new_job, field_name='start_args')
) == {'foo': 'bar'}
@pytest.mark.parametrize('cls', ('JobTemplate', 'WorkflowJobTemplate'))
def test_survey_spec(self, inventory, project, survey_spec_factory, cls):
params = {}
if cls == 'JobTemplate':
params['inventory'] = inventory
params['project'] = project
# test basic decryption
jt = getattr(models, cls).objects.create(
name='Example Template',
survey_spec=survey_spec_factory([{
'variable': 'secret_key',
'default': encrypt_value('donttell', pk=None),
'type': 'password'
}]),
survey_enabled=True,
**params
)
job = jt.create_unified_job()
assert jt.survey_spec['spec'][0]['default'].startswith(PREFIX)
assert job.survey_passwords == {'secret_key': '$encrypted$'}
assert json.loads(job.decrypted_extra_vars())['secret_key'] == 'donttell'
# re-key the extra_vars
new_key = regenerate_secret_key.Command().handle()
new_job = models.UnifiedJob.objects.get(pk=job.pk)
assert new_job.extra_vars != job.extra_vars
# verify that the old SECRET_KEY doesn't work
with pytest.raises(InvalidToken):
new_job.decrypted_extra_vars()
# verify that the new SECRET_KEY *does* work
with override_settings(SECRET_KEY=new_key):
assert json.loads(
new_job.decrypted_extra_vars()
)['secret_key'] == 'donttell'
def test_oauth2_application_client_secret(self, oauth_application):
# test basic decryption
secret = oauth_application.client_secret
assert len(secret) == 128
# re-key the client_secret
new_key = regenerate_secret_key.Command().handle()
# verify that the old SECRET_KEY doesn't work
with pytest.raises(InvalidToken):
models.OAuth2Application.objects.get(
pk=oauth_application.pk
).client_secret
# verify that the new SECRET_KEY *does* work
with override_settings(SECRET_KEY=new_key):
assert models.OAuth2Application.objects.get(
pk=oauth_application.pk
).client_secret == secret

View File

@@ -122,6 +122,22 @@ def project_playbooks():
mocked.start()
@pytest.fixture
def run_computed_fields_right_away(request):
def run_me(inventory_id, should_update_hosts=True):
i = Inventory.objects.get(id=inventory_id)
i.update_computed_fields(update_hosts=should_update_hosts)
mocked = mock.patch(
'awx.main.signals.update_inventory_computed_fields.delay',
new=run_me
)
mocked.start()
request.addfinalizer(mocked.stop)
@pytest.fixture
@mock.patch.object(Project, "update", lambda self, **kwargs: None)
def project(instance, organization):
@@ -280,14 +296,21 @@ def credentialtype_external():
}],
'required': ['url', 'token', 'key'],
}
external_type = CredentialType(
kind='external',
managed_by_tower=True,
name='External Service',
inputs=external_type_inputs
)
external_type.save()
return external_type
class MockPlugin(object):
def backend(self, **kwargs):
return 'secret'
with mock.patch('awx.main.models.credential.CredentialType.plugin', new_callable=PropertyMock) as mock_plugin:
mock_plugin.return_value = MockPlugin()
external_type = CredentialType(
kind='external',
managed_by_tower=True,
name='External Service',
inputs=external_type_inputs
)
external_type.save()
yield external_type
@pytest.fixture

View File

@@ -88,6 +88,9 @@ class TestJobNotificationMixin(object):
'verbosity': int},
'job_friendly_name': str,
'job_metadata': str,
'approval_status': str,
'approval_node_name': str,
'workflow_url': str,
'url': str}

View File

@@ -281,15 +281,18 @@ class TestTaskImpact:
return job
return r
def test_limit_task_impact(self, job_host_limit):
def test_limit_task_impact(self, job_host_limit, run_computed_fields_right_away):
job = job_host_limit(5, 2)
job.inventory.refresh_from_db() # FIXME: computed fields operates on reloaded inventory
assert job.inventory.total_hosts == 5
assert job.task_impact == 2 + 1 # forks becomes constraint
def test_host_task_impact(self, job_host_limit):
def test_host_task_impact(self, job_host_limit, run_computed_fields_right_away):
job = job_host_limit(3, 5)
job.inventory.refresh_from_db() # FIXME: computed fields operates on reloaded inventory
assert job.task_impact == 3 + 1 # hosts becomes constraint
def test_shard_task_impact(self, slice_job_factory):
def test_shard_task_impact(self, slice_job_factory, run_computed_fields_right_away):
# factory creates on host per slice
workflow_job = slice_job_factory(3, jt_kwargs={'forks': 50}, spawn=True)
# arrange the jobs by their number
@@ -308,4 +311,5 @@ class TestTaskImpact:
len(jobs[0].inventory.get_script_data(slice_number=i + 1, slice_count=3)['all']['hosts'])
for i in range(3)
] == [2, 1, 1]
jobs[0].inventory.refresh_from_db() # FIXME: computed fields operates on reloaded inventory
assert [job.task_impact for job in jobs] == [3, 2, 2]

View File

@@ -4,6 +4,7 @@ import json
from datetime import timedelta
from awx.main.scheduler import TaskManager
from awx.main.scheduler.dependency_graph import DependencyGraph
from awx.main.utils import encrypt_field
from awx.main.models import WorkflowJobTemplate, JobTemplate
@@ -326,3 +327,29 @@ def test_shared_dependencies_launch(default_instance_group, job_template_factory
iu = [x for x in ii.inventory_updates.all()]
assert len(pu) == 1
assert len(iu) == 1
@pytest.mark.django_db
def test_job_not_blocking_project_update(default_instance_group, job_template_factory):
objects = job_template_factory('jt', organization='org1', project='proj',
inventory='inv', credential='cred',
jobs=["job"])
job = objects.jobs["job"]
job.instance_group = default_instance_group
job.status = "running"
job.save()
with mock.patch("awx.main.scheduler.TaskManager.start_task"):
task_manager = TaskManager()
task_manager._schedule()
proj = objects.project
project_update = proj.create_project_update()
project_update.instance_group = default_instance_group
project_update.status = "pending"
project_update.save()
assert not task_manager.is_job_blocked(project_update)
dependency_graph = DependencyGraph(None)
dependency_graph.add_job(job)
assert not dependency_graph.is_job_blocked(project_update)

View File

@@ -54,7 +54,8 @@ INI_TEST_VARS = {
},
'azure_rm': {
'use_private_ip': True,
'resource_groups': 'foo_resources,bar_resources'
'resource_groups': 'foo_resources,bar_resources',
'tags': 'Creator:jmarshall, peanutbutter:jelly'
},
'satellite6': {
'satellite6_group_patterns': 'foo_group_patterns',

View File

@@ -2,52 +2,10 @@ import pytest
from unittest import mock
from awx.main.migrations import _inventory_source as invsrc
from awx.main.models import InventorySource
from django.apps import apps
@pytest.mark.django_db
def test_inv_src_manual_removal(inventory_source):
inventory_source.source = ''
inventory_source.save()
assert InventorySource.objects.filter(pk=inventory_source.pk).exists()
invsrc.remove_manual_inventory_sources(apps, None)
assert not InventorySource.objects.filter(pk=inventory_source.pk).exists()
@pytest.mark.django_db
def test_rax_inv_src_removal(inventory_source):
inventory_source.source = 'rax'
inventory_source.save()
assert InventorySource.objects.filter(pk=inventory_source.pk).exists()
invsrc.remove_rax_inventory_sources(apps, None)
assert not InventorySource.objects.filter(pk=inventory_source.pk).exists()
@pytest.mark.django_db
def test_inv_src_rename(inventory_source_factory):
inv_src01 = inventory_source_factory('t1')
invsrc.rename_inventory_sources(apps, None)
inv_src01.refresh_from_db()
# inv-is-t1 is generated in the inventory_source_factory
assert inv_src01.name == 't1 - inv-is-t1 - 0'
@pytest.mark.django_db
def test_azure_inv_src_removal(inventory_source):
inventory_source.source = 'azure'
inventory_source.save()
assert InventorySource.objects.filter(pk=inventory_source.pk).exists()
invsrc.remove_azure_inventory_sources(apps, None)
assert not InventorySource.objects.filter(pk=inventory_source.pk).exists()
@pytest.mark.parametrize('vars,id_var,result', [
({'foo': {'bar': '1234'}}, 'foo.bar', '1234'),
({'cat': 'meow'}, 'cat', 'meow'),

View File

@@ -1,100 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved.
import pytest
from django.apps import apps
from awx.main.models.base import PERM_INVENTORY_SCAN, PERM_INVENTORY_DEPLOY
from awx.main.models import (
JobTemplate,
Project,
Inventory,
Organization,
)
from awx.main.migrations._scan_jobs import _migrate_scan_job_templates
@pytest.fixture
def organizations():
return [Organization.objects.create(name=u"org-\xe9-{}".format(x)) for x in range(3)]
@pytest.fixture
def inventories(organizations):
return [Inventory.objects.create(name=u"inv-\xe9-{}".format(x),
organization=organizations[x]) for x in range(3)]
@pytest.fixture
def job_templates_scan(inventories):
return [JobTemplate.objects.create(name=u"jt-\xe9-scan-{}".format(x),
job_type=PERM_INVENTORY_SCAN,
inventory=inventories[x]) for x in range(3)]
@pytest.fixture
def job_templates_deploy(inventories):
return [JobTemplate.objects.create(name=u"jt-\xe9-deploy-{}".format(x),
job_type=PERM_INVENTORY_DEPLOY,
inventory=inventories[x]) for x in range(3)]
@pytest.fixture
def project_custom(organizations):
return Project.objects.create(name=u"proj-\xe9-scan_custom",
scm_url='https://giggity.com',
organization=organizations[0])
@pytest.fixture
def job_templates_custom_scan_project(project_custom):
return [JobTemplate.objects.create(name=u"jt-\xe9-scan-custom-{}".format(x),
project=project_custom,
job_type=PERM_INVENTORY_SCAN) for x in range(3)]
@pytest.fixture
def job_template_scan_no_org():
return JobTemplate.objects.create(name=u"jt-\xe9-scan-no-org",
job_type=PERM_INVENTORY_SCAN)
@pytest.mark.django_db
def test_scan_jobs_migration(job_templates_scan, job_templates_deploy, job_templates_custom_scan_project, project_custom, job_template_scan_no_org):
_migrate_scan_job_templates(apps)
# Ensure there are no scan job templates after the migration
assert 0 == JobTemplate.objects.filter(job_type=PERM_INVENTORY_SCAN).count()
# Ensure special No Organization proj created
# And No Organization project is associated with correct jt
proj = Project.objects.get(name="Tower Fact Scan - No Organization")
assert proj.id == JobTemplate.objects.get(id=job_template_scan_no_org.id).project.id
# Ensure per-org projects were created
projs = Project.objects.filter(name__startswith="Tower Fact Scan")
assert projs.count() == 4
# Ensure scan job templates with Tower project are migrated
for i, jt_old in enumerate(job_templates_scan):
jt = JobTemplate.objects.get(id=jt_old.id)
assert PERM_INVENTORY_DEPLOY == jt.job_type
assert jt.use_fact_cache is True
assert projs[i] == jt.project
# Ensure scan job templates with custom projects are migrated
for jt_old in job_templates_custom_scan_project:
jt = JobTemplate.objects.get(id=jt_old.id)
assert PERM_INVENTORY_DEPLOY == jt.job_type
assert jt.use_fact_cache is True
assert project_custom == jt.project
# Ensure other job template aren't touched
for jt_old in job_templates_deploy:
jt = JobTemplate.objects.get(id=jt_old.id)
assert PERM_INVENTORY_DEPLOY == jt.job_type
assert jt.project is None

View File

@@ -12,7 +12,9 @@ from awx.main.models import SystemJobTemplate
{"days": 13435},
])
def test_valid__clean_extra_data_system_jobs(extra_data):
accepted, rejected, errors = SystemJobTemplate().accept_or_ignore_variables(extra_data)
accepted, rejected, errors = SystemJobTemplate(
job_type='cleanup_jobs'
).accept_or_ignore_variables(extra_data)
assert not rejected
assert not errors
@@ -32,12 +34,14 @@ def test_valid__clean_extra_data_system_jobs(extra_data):
{"days": "foobar"},
])
def test_invalid__extra_data_system_jobs(extra_data):
accepted, rejected, errors = SystemJobTemplate().accept_or_ignore_variables(extra_data)
accepted, rejected, errors = SystemJobTemplate(
job_type='cleanup_jobs'
).accept_or_ignore_variables(extra_data)
assert str(errors['extra_vars'][0]) == u'days must be a positive integer.'
def test_unallowed_system_job_data():
sjt = SystemJobTemplate()
sjt = SystemJobTemplate(job_type='cleanup_jobs')
accepted, ignored, errors = sjt.accept_or_ignore_variables({
'days': 34,
'foobar': 'baz'
@@ -54,7 +58,7 @@ def test_reject_other_prommpts():
def test_reject_some_accept_some():
sjt = SystemJobTemplate()
sjt = SystemJobTemplate(job_type='cleanup_jobs')
accepted, ignored, errors = sjt._accept_or_ignore_job_kwargs(limit="", extra_vars={
'days': 34,
'foobar': 'baz'

View File

@@ -36,11 +36,14 @@ from django.utils.encoding import smart_str
from django.utils.text import slugify
from django.apps import apps
# AWX
from awx.conf.license import get_license
logger = logging.getLogger('awx.main.utils')
__all__ = [
'get_object_or_400', 'camelcase_to_underscore', 'underscore_to_camelcase', 'memoize',
'memoize_delete', 'get_ansible_version', 'get_ssh_version', 'get_licenser',
'memoize_delete', 'get_ansible_version', 'get_ssh_version', 'get_licenser', 'get_awx_http_client_headers',
'get_awx_version', 'update_scm_url', 'get_type_for_model', 'get_model_for_type',
'copy_model_by_class', 'region_sorting', 'copy_m2m_relationships',
'prefetch_page_capabilities', 'to_python_boolean', 'ignore_inventory_computed_fields',
@@ -212,6 +215,19 @@ def get_awx_version():
return __version__
def get_awx_http_client_headers():
license = get_license(show_key=False).get('license_type', 'UNLICENSED')
headers = {
'Content-Type': 'application/json',
'User-Agent': '{} {} ({})'.format(
'AWX' if license == 'open' else 'Red Hat Ansible Tower',
get_awx_version(),
license
)
}
return headers
class StubLicense(object):
features = {

View File

@@ -1,8 +1,16 @@
# Copyright (c) 2017 Ansible by Red Hat
# All Rights Reserved.
import logging
from itertools import chain
from django.core.cache import cache
from django.db.migrations.executor import MigrationExecutor
from django.db import connection
logger = logging.getLogger('awx.main.utils.db')
def get_all_field_names(model):
# Implements compatibility with _meta.get_all_field_names
@@ -14,3 +22,21 @@ def get_all_field_names(model):
# GenericForeignKey from the results.
if not (field.many_to_one and field.related_model is None)
)))
def migration_in_progress_check_or_relase():
'''A memcache flag is raised (set to True) to inform cluster
that a migration is ongoing see main.apps.MainConfig.ready
if the flag is True then the flag is removed on this instance if
models-db consistency is observed
effective value of migration flag is returned
'''
migration_in_progress = cache.get('migration_in_progress', False)
if migration_in_progress:
executor = MigrationExecutor(connection)
plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
if not bool(plan):
logger.info('Detected that migration finished, migration flag taken down.')
cache.delete('migration_in_progress')
migration_in_progress = False
return migration_in_progress

View File

@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
import base64
import hashlib
import logging
@@ -35,7 +37,7 @@ class Fernet256(Fernet):
self._backend = backend
def get_encryption_key(field_name, pk=None):
def get_encryption_key(field_name, pk=None, secret_key=None):
'''
Generate key for encrypted password based on field name,
``settings.SECRET_KEY``, and instance pk (if available).
@@ -46,19 +48,58 @@ def get_encryption_key(field_name, pk=None):
'''
from django.conf import settings
h = hashlib.sha512()
h.update(smart_bytes(settings.SECRET_KEY))
h.update(smart_bytes(secret_key or settings.SECRET_KEY))
if pk is not None:
h.update(smart_bytes(str(pk)))
h.update(smart_bytes(field_name))
return base64.urlsafe_b64encode(h.digest())
def encrypt_value(value, pk=None):
def encrypt_value(value, pk=None, secret_key=None):
#
# ⚠️ D-D-D-DANGER ZONE ⚠️
#
# !!! BEFORE USING THIS FUNCTION PLEASE READ encrypt_field !!!
#
TransientField = namedtuple('TransientField', ['pk', 'value'])
return encrypt_field(TransientField(pk=pk, value=value), 'value')
return encrypt_field(TransientField(pk=pk, value=value), 'value', secret_key=secret_key)
def encrypt_field(instance, field_name, ask=False, subfield=None):
def encrypt_field(instance, field_name, ask=False, subfield=None, secret_key=None):
#
# ⚠️ D-D-D-DANGER ZONE ⚠️
#
# !!! PLEASE READ BEFORE USING THIS FUNCTION ANYWHERE !!!
#
# You should know that this function is used in various places throughout
# AWX for symmetric encryption - generally it's used to encrypt sensitive
# values that we store in the AWX database (such as SSH private keys for
# credentials).
#
# If you're reading this function's code because you're thinking about
# using it to encrypt *something new*, please remember that AWX has
# official support for *regenerating* the SECRET_KEY (on which the
# symmetric key is based):
#
# $ awx-manage regenerate_secret_key
# $ setup.sh -k
#
# ...so you'll need to *also* add code to support the
# migration/re-encryption of these values (the code in question lives in
# `awx.main.management.commands.regenerate_secret_key`):
#
# For example, if you find that you're adding a new database column that is
# encrypted, in addition to calling `encrypt_field` in the appropriate
# places, you would also need to update the `awx-manage regenerate_secret_key`
# so that values are properly migrated when the SECRET_KEY changes.
#
# This process *generally* involves adding Python code to the
# `regenerate_secret_key` command, i.e.,
#
# 1. Query the database for existing encrypted values on the appropriate object(s)
# 2. Decrypting them using the *old* SECRET_KEY
# 3. Storing newly encrypted values using the *newly generated* SECRET_KEY
#
'''
Return content of the given instance and field name encrypted.
'''
@@ -76,7 +117,11 @@ def encrypt_field(instance, field_name, ask=False, subfield=None):
value = smart_str(value)
if not value or value.startswith('$encrypted$') or (ask and value == 'ASK'):
return value
key = get_encryption_key(field_name, getattr(instance, 'pk', None))
key = get_encryption_key(
field_name,
getattr(instance, 'pk', None),
secret_key=secret_key
)
f = Fernet256(key)
encrypted = f.encrypt(smart_bytes(value))
b64data = smart_str(base64.b64encode(encrypted))
@@ -99,7 +144,7 @@ def decrypt_value(encryption_key, value):
return smart_str(value)
def decrypt_field(instance, field_name, subfield=None):
def decrypt_field(instance, field_name, subfield=None, secret_key=None):
'''
Return content of the given instance and field name decrypted.
'''
@@ -115,7 +160,11 @@ def decrypt_field(instance, field_name, subfield=None):
value = smart_str(value)
if not value or not value.startswith('$encrypted$'):
return value
key = get_encryption_key(field_name, getattr(instance, 'pk', None))
key = get_encryption_key(
field_name,
getattr(instance, 'pk', None),
secret_key=secret_key
)
try:
return smart_str(decrypt_value(key, value))

View File

@@ -4,7 +4,7 @@
import json
# Django
from django.http import HttpResponse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _
@@ -97,3 +97,6 @@ def handle_csp_violation(request):
logger = logging.getLogger('awx')
logger.error(json.loads(request.body))
return HttpResponse(content=None)
def handle_login_redirect(request):
return HttpResponseRedirect("/#/login")

View File

@@ -18,8 +18,8 @@
src: "{{src}}/artifacts/"
dest: "{{src}}/artifacts/"
mode: pull
delete: yes
recursive: yes
delete: true
recursive: true
when: ansible_kubectl_config is not defined
- name: Copy daemon log from the isolated host
@@ -34,9 +34,9 @@
src: "{{src}}/artifacts/"
dest: "{{src}}/artifacts/"
mode: pull
delete: yes
recursive: yes
set_remote_user: no
delete: true
recursive: true
set_remote_user: false
rsync_opts:
- "--rsh=$RSH"
environment:
@@ -49,7 +49,7 @@
src: "{{src}}/daemon.log"
dest: "{{src}}/daemon.log"
mode: pull
set_remote_user: no
set_remote_user: false
rsync_opts:
- "--rsh=$RSH"
environment:

View File

@@ -12,14 +12,16 @@
- name: cancel the job
command: "ansible-runner stop {{private_data_dir}}"
ignore_errors: yes
ignore_errors: true
- name: remove build artifacts
file: path="{{item}}" state=absent
file:
path: '{{item}}'
state: absent
register: result
with_items: "{{cleanup_dirs}}"
until: result is succeeded
ignore_errors: yes
ignore_errors: true
retries: 3
delay: 5

View File

@@ -1,160 +1,173 @@
---
# The following variables will be set by the runner of this playbook:
# project_path: PROJECTS_DIR/_local_path_
# scm_type: git|hg|svn|insights
# scm_url: https://server/repo
# insights_url: Insights service URL (from configuration)
# scm_branch: branch/tag/revision (HEAD if unset)
# scm_clean: true/false
# scm_delete_on_update: true/false
# scm_full_checkout: true (if for a job template run), false (if retrieving revision)
# scm_username: username (only for svn/insights)
# scm_password: password (only for svn/insights)
# scm_accept_hostkey: true/false (only for git, set automatically)
# scm_refspec: a refspec to fetch in addition to obtaining version
# roles_enabled: Allow us to pull roles from a requirements.yml file
# roles_enabled: Value of the global setting to enable roles downloading
# collections_enabled: Value of the global setting to enable collections downloading
# roles_destination: Path to save roles from galaxy to
# collections_destination: Path to save collections from galaxy to
# awx_version: Current running version of the awx or tower as a string
# awx_license_type: "open" for AWX; else presume Tower
- hosts: all
- hosts: localhost
gather_facts: false
connection: local
name: Update source tree if necessary
tasks:
- name: delete project directory before update
file:
path: "{{project_path|quote}}"
state: absent
when: scm_delete_on_update|default('')
delegate_to: localhost
tags:
- delete
- block:
- name: update project using git
git:
dest: "{{project_path|quote}}"
repo: "{{scm_url}}"
version: "{{scm_branch|quote}}"
refspec: "{{scm_refspec|default(omit)}}"
force: "{{scm_clean}}"
accept_hostkey: "{{scm_accept_hostkey|default(omit)}}"
register: git_result
- name: update project using git
git:
dest: "{{project_path|quote}}"
repo: "{{scm_url}}"
version: "{{scm_branch|quote}}"
refspec: "{{scm_refspec|default(omit)}}"
force: "{{scm_clean}}"
accept_hostkey: "{{scm_accept_hostkey|default(omit)}}"
register: git_result
- name: Set the git repository version
set_fact:
scm_version: "{{ git_result['after'] }}"
when: "'after' in git_result"
when: scm_type == 'git'
delegate_to: localhost
- name: Set the git repository version
set_fact:
scm_version: "{{ git_result['after'] }}"
when: "'after' in git_result"
tags:
- update_git
- block:
- name: update project using hg
hg:
dest: "{{project_path|quote}}"
repo: "{{scm_url|quote}}"
revision: "{{scm_branch|quote}}"
force: "{{scm_clean}}"
register: hg_result
- name: update project using hg
hg:
dest: "{{project_path|quote}}"
repo: "{{scm_url|quote}}"
revision: "{{scm_branch|quote}}"
force: "{{scm_clean}}"
register: hg_result
- name: Set the hg repository version
set_fact:
scm_version: "{{ hg_result['after'] }}"
when: "'after' in hg_result"
- name: Set the hg repository version
set_fact:
scm_version: "{{ hg_result['after'] }}"
when: "'after' in hg_result"
- name: parse hg version string properly
set_fact:
scm_version: "{{scm_version|regex_replace('^([A-Za-z0-9]+).*$', '\\1')}}"
when: scm_type == 'hg'
delegate_to: localhost
- name: parse hg version string properly
set_fact:
scm_version: "{{scm_version|regex_replace('^([A-Za-z0-9]+).*$', '\\1')}}"
tags:
- update_hg
- block:
- name: update project using svn
subversion:
dest: "{{project_path|quote}}"
repo: "{{scm_url|quote}}"
revision: "{{scm_branch|quote}}"
force: "{{scm_clean}}"
username: "{{scm_username|default(omit)}}"
password: "{{scm_password|default(omit)}}"
environment:
LC_ALL: 'en_US.UTF-8'
register: svn_result
- name: update project using svn
subversion:
dest: "{{project_path|quote}}"
repo: "{{scm_url|quote}}"
revision: "{{scm_branch|quote}}"
force: "{{scm_clean}}"
username: "{{scm_username|default(omit)}}"
password: "{{scm_password|default(omit)}}"
environment:
LC_ALL: 'en_US.UTF-8'
register: svn_result
- name: Set the svn repository version
set_fact:
scm_version: "{{ svn_result['after'] }}"
when: "'after' in svn_result"
- name: Set the svn repository version
set_fact:
scm_version: "{{ svn_result['after'] }}"
when: "'after' in svn_result"
- name: parse subversion version string properly
set_fact:
scm_version: "{{scm_version|regex_replace('^.*Revision: ([0-9]+).*$', '\\1')}}"
when: scm_type == 'svn'
delegate_to: localhost
- name: parse subversion version string properly
set_fact:
scm_version: "{{scm_version|regex_replace('^.*Revision: ([0-9]+).*$', '\\1')}}"
tags:
- update_svn
- block:
- name: Ensure the project directory is present
file:
dest: "{{project_path|quote}}"
state: directory
- name: Ensure the project directory is present
file:
dest: "{{project_path|quote}}"
state: directory
- name: Fetch Insights Playbook(s)
insights:
insights_url: "{{insights_url}}"
username: "{{scm_username}}"
password: "{{scm_password}}"
project_path: "{{project_path}}"
awx_license_type: "{{awx_license_type}}"
awx_version: "{{awx_version}}"
register: results
- name: Save Insights Version
set_fact:
scm_version: "{{results.version}}"
when: results is defined
when: scm_type == 'insights'
delegate_to: localhost
- name: Fetch Insights Playbook(s)
insights:
insights_url: "{{insights_url}}"
username: "{{scm_username}}"
password: "{{scm_password}}"
project_path: "{{project_path}}"
awx_license_type: "{{awx_license_type}}"
awx_version: "{{awx_version}}"
register: results
- name: Save Insights Version
set_fact:
scm_version: "{{results.version}}"
when: results is defined
tags:
- update_insights
- name: Repository Version
debug: msg="Repository Version {{ scm_version }}"
when: scm_version is defined
debug:
msg: "Repository Version {{ scm_version }}"
tags:
- update_git
- update_hg
- update_svn
- update_insights
- hosts: all
- hosts: localhost
gather_facts: false
connection: local
name: Install content with ansible-galaxy command if necessary
tasks:
- block:
- name: detect requirements.yml
stat: path={{project_path|quote}}/roles/requirements.yml
register: doesRequirementsExist
- name: detect requirements.yml
stat:
path: '{{project_path|quote}}/roles/requirements.yml'
register: doesRequirementsExist
- name: fetch galaxy roles from requirements.yml
command: ansible-galaxy install -r requirements.yml -p {{roles_destination|quote}}{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }}
args:
chdir: "{{project_path|quote}}/roles"
register: galaxy_result
when: doesRequirementsExist.stat.exists
changed_when: "'was installed successfully' in galaxy_result.stdout"
environment:
ANSIBLE_FORCE_COLOR: False
- name: fetch galaxy roles from requirements.yml
command: ansible-galaxy install -r requirements.yml -p {{roles_destination|quote}}{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }}
args:
chdir: "{{project_path|quote}}/roles"
register: galaxy_result
when: doesRequirementsExist.stat.exists
changed_when: "'was installed successfully' in galaxy_result.stdout"
environment:
ANSIBLE_FORCE_COLOR: false
when: roles_enabled|bool
delegate_to: localhost
tags:
- install_roles
- block:
- name: detect collections/requirements.yml
stat: path={{project_path|quote}}/collections/requirements.yml
register: doesCollectionRequirementsExist
- name: detect collections/requirements.yml
stat:
path: '{{project_path|quote}}/collections/requirements.yml'
register: doesCollectionRequirementsExist
- name: fetch galaxy collections from collections/requirements.yml
command: ansible-galaxy collection install -r requirements.yml -p {{collections_destination|quote}}{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }}
args:
chdir: "{{project_path|quote}}/collections"
register: galaxy_collection_result
when: doesCollectionRequirementsExist.stat.exists
changed_when: "'Installing ' in galaxy_collection_result.stdout"
environment:
ANSIBLE_FORCE_COLOR: False
ANSIBLE_COLLECTIONS_PATHS: "{{ collections_destination }}"
- name: fetch galaxy collections from collections/requirements.yml
command: ansible-galaxy collection install -r requirements.yml -p {{collections_destination|quote}}{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }}
args:
chdir: "{{project_path|quote}}/collections"
register: galaxy_collection_result
when: doesCollectionRequirementsExist.stat.exists
changed_when: "'Installing ' in galaxy_collection_result.stdout"
environment:
ANSIBLE_FORCE_COLOR: false
ANSIBLE_COLLECTIONS_PATHS: "{{ collections_destination }}"
when: collections_enabled|bool
delegate_to: localhost
when:
- "ansible_version.full is version_compare('2.8', '>=')"
- collections_enabled|bool
tags:
- install_collections

View File

@@ -13,17 +13,17 @@
tasks:
- name: synchronize job environment with isolated host
synchronize:
copy_links: yes
copy_links: true
src: "{{ src }}"
dest: "{{ dest }}"
when: ansible_kubectl_config is not defined
- name: synchronize job environment with remote job container
synchronize:
copy_links: yes
copy_links: true
src: "{{ src }}"
dest: "{{ dest }}"
set_remote_user: no
set_remote_user: false
rsync_opts:
- "--rsh=$RSH"
environment:
@@ -51,4 +51,4 @@
content: "{{secret}}"
path: "{{src}}/env/ssh_key"
when: key.stat.exists
no_log: True
no_log: true

View File

@@ -1,3 +1,4 @@
---
- hosts: all
vars:
scan_use_checksum: false
@@ -33,4 +34,3 @@
get_checksum: '{{ scan_use_checksum }}'
recursive: '{{ scan_use_recursive }}'
when: scan_file_paths is defined and ansible_os_family == "Windows"

View File

@@ -123,6 +123,12 @@ user = foreman
password = secret
ssl_verify = True
# Foreman 1.24 introduces a new reports API to improve performance of the inventory script.
# Note: This requires foreman_ansible plugin installed.
# Set to False if you want to use the old API. Defaults to True.
use_reports_api = True
# Retrieve only hosts from the organization "Web Engineering".
# host_filters = organization="Web Engineering"
@@ -130,15 +136,42 @@ ssl_verify = True
# also in the host collection "Apache Servers".
# host_filters = organization="Web Engineering" and host_collection="Apache Servers"
# Foreman Inventory report related configuration options.
# Configs that default to True :
# want_organization , want_location, want_ipv4, want_host_group, want_subnet, want_smart_proxies, want_facts
# Configs that default to False :
# want_ipv6, want_subnet_v6, want_content_facet_attributes, want_host_params
[report]
want_organization = True
want_location = True
want_ipv4 = True
want_ipv6 = False
want_host_group = True
want_subnet = True
want_subnet_v6 = False
want_smart_proxies = True
want_content_facet_attributes = False
want_host_params = False
# use this config to determine if facts are to be fetched in the report and stored on the hosts.
# want_facts = False
# Upon receiving a request to return inventory report, Foreman schedules a report generation job.
# The script then polls the report_data endpoint repeatedly to check if the job is complete and retrieves data
# poll_interval allows to define the polling interval between 2 calls to the report_data endpoint while polling.
# Defaults to 10 seconds
poll_interval = 10
[ansible]
group_patterns = ["{app}-{tier}-{color}",
"{app}-{color}",
"{app}",
"{tier}"]
group_prefix = foreman_
# Whether to fetch facts from Foreman and store them on the host
want_facts = True
group_prefix = foreman_
# Whether to create Ansible groups for host collections. Only tested
# with Katello (Red Hat Satellite). Disabled by default to not break

View File

@@ -34,7 +34,7 @@ import copy
import os
import re
import sys
from time import time
from time import time, sleep
from collections import defaultdict
from distutils.version import LooseVersion, StrictVersion
@@ -92,6 +92,72 @@ class ForemanInventory(object):
print("Error parsing configuration: %s" % e, file=sys.stderr)
return False
# Inventory Report Related
try:
self.foreman_use_reports_api = config.getboolean('foreman', 'use_reports_api')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.foreman_use_reports_api = True
try:
self.want_organization = config.getboolean('report', 'want_organization')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_organization = True
try:
self.want_location = config.getboolean('report', 'want_location')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_location = True
try:
self.want_IPv4 = config.getboolean('report', 'want_ipv4')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_IPv4 = True
try:
self.want_IPv6 = config.getboolean('report', 'want_ipv6')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_IPv6 = False
try:
self.want_host_group = config.getboolean('report', 'want_host_group')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_host_group = True
try:
self.want_host_params = config.getboolean('report', 'want_host_params')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_host_params = False
try:
self.want_subnet = config.getboolean('report', 'want_subnet')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_subnet = True
try:
self.want_subnet_v6 = config.getboolean('report', 'want_subnet_v6')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_subnet_v6 = False
try:
self.want_smart_proxies = config.getboolean('report', 'want_smart_proxies')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_smart_proxies = True
try:
self.want_content_facet_attributes = config.getboolean('report', 'want_content_facet_attributes')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_content_facet_attributes = False
try:
self.report_want_facts = config.getboolean('report', 'want_facts')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.report_want_facts = True
try:
self.poll_interval = config.getint('report', 'poll_interval')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.poll_interval = 10
# Ansible related
try:
group_patterns = config.get('ansible', 'group_patterns')
@@ -110,6 +176,8 @@ class ForemanInventory(object):
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.want_facts = True
self.want_facts = self.want_facts and self.report_want_facts
try:
self.want_hostcollections = config.getboolean('ansible', 'want_hostcollections')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
@@ -203,6 +271,52 @@ class ForemanInventory(object):
break
return results
def _use_inventory_report(self):
if not self.foreman_use_reports_api:
return False
status_url = "%s/api/v2/status" % self.foreman_url
result = self._get_json(status_url)
foreman_version = (LooseVersion(result.get('version')) >= LooseVersion('1.24.0'))
return foreman_version
def _fetch_params(self):
options, params = ("no", "yes"), dict()
params["Organization"] = options[self.want_organization]
params["Location"] = options[self.want_location]
params["IPv4"] = options[self.want_IPv4]
params["IPv6"] = options[self.want_IPv6]
params["Facts"] = options[self.want_facts]
params["Host Group"] = options[self.want_host_group]
params["Host Collections"] = options[self.want_hostcollections]
params["Subnet"] = options[self.want_subnet]
params["Subnet v6"] = options[self.want_subnet_v6]
params["Smart Proxies"] = options[self.want_smart_proxies]
params["Content Attributes"] = options[self.want_content_facet_attributes]
params["Host Parameters"] = options[self.want_host_params]
if self.host_filters:
params["Hosts"] = self.host_filters
return params
def _post_request(self):
url = "%s/ansible/api/v2/ansible_inventories/schedule" % self.foreman_url
session = self._get_session()
params = {'input_values': self._fetch_params()}
ret = session.post(url, json=params)
if not ret:
raise Exception("Error scheduling inventory report on foreman. Please check foreman logs!")
url = "{0}/{1}".format(self.foreman_url, ret.json().get('data_url'))
response = session.get(url)
while response:
if response.status_code != 204:
break
else:
sleep(self.poll_interval)
response = session.get(url)
if not response:
raise Exception("Error receiving inventory report from foreman. Please check foreman logs!")
else:
return response.json()
def _get_hosts(self):
url = "%s/api/v2/hosts" % self.foreman_url
@@ -276,6 +390,97 @@ class ForemanInventory(object):
def update_cache(self, scan_only_new_hosts=False):
"""Make calls to foreman and save the output in a cache"""
use_inventory_report = self._use_inventory_report()
if use_inventory_report:
self._update_cache_inventory(scan_only_new_hosts)
else:
self._update_cache_host_api(scan_only_new_hosts)
def _update_cache_inventory(self, scan_only_new_hosts):
self.groups = dict()
self.hosts = dict()
try:
inventory_report_response = self._post_request()
except Exception:
self._update_cache_host_api(scan_only_new_hosts)
return
host_data = json.loads(inventory_report_response)
for host in host_data:
if not(host) or (host["name"] in self.cache.keys() and scan_only_new_hosts):
continue
dns_name = host['name']
host_params = host.pop('host_parameters', {})
fact_list = host.pop('facts', {})
content_facet_attributes = host.get('content_attributes', {}) or {}
# Create ansible groups for hostgroup
group = 'host_group'
val = host.get(group)
if val:
safe_key = self.to_safe('%s%s_%s' % (
to_text(self.group_prefix),
group,
to_text(val).lower()
))
self.inventory[safe_key].append(dns_name)
# Create ansible groups for environment, location and organization
for group in ['environment', 'location', 'organization']:
val = host.get('%s' % group)
if val:
safe_key = self.to_safe('%s%s_%s' % (
to_text(self.group_prefix),
group,
to_text(val).lower()
))
self.inventory[safe_key].append(dns_name)
for group in ['lifecycle_environment', 'content_view']:
val = content_facet_attributes.get('%s_name' % group)
if val:
safe_key = self.to_safe('%s%s_%s' % (
to_text(self.group_prefix),
group,
to_text(val).lower()
))
self.inventory[safe_key].append(dns_name)
params = host_params
# Ansible groups by parameters in host groups and Foreman host
# attributes.
groupby = dict()
for k, v in params.items():
groupby[k] = self.to_safe(to_text(v))
# The name of the ansible groups is given by group_patterns:
for pattern in self.group_patterns:
try:
key = pattern.format(**groupby)
self.inventory[key].append(dns_name)
except KeyError:
pass # Host not part of this group
if self.want_hostcollections:
hostcollections = host.get('host_collections')
if hostcollections:
# Create Ansible groups for host collections
for hostcollection in hostcollections:
safe_key = self.to_safe('%shostcollection_%s' % (self.group_prefix, hostcollection.lower()))
self.inventory[safe_key].append(dns_name)
self.hostcollections[dns_name] = hostcollections
self.cache[dns_name] = host
self.params[dns_name] = params
self.facts[dns_name] = fact_list
self.inventory['all'].append(dns_name)
self._write_cache()
def _update_cache_host_api(self, scan_only_new_hosts):
"""Make calls to foreman and save the output in a cache"""
self.groups = dict()
self.hosts = dict()

View File

@@ -1,3 +1,4 @@
---
clouds:
vexxhost:
profile: vexxhost
@@ -19,6 +20,6 @@ clouds:
password: stack
project_name: stack
ansible:
use_hostnames: True
expand_hostvars: False
fail_on_errors: True
use_hostnames: true
expand_hostvars: false
fail_on_errors: true

View File

@@ -373,6 +373,10 @@ TACACSPLUS_AUTH_PROTOCOL = 'ascii'
# Note: This setting may be overridden by database settings.
AUTH_BASIC_ENABLED = True
# If set, specifies a URL that unauthenticated users will be redirected to
# when trying to access a UI page that requries authentication.
LOGIN_REDIRECT_OVERRIDE = None
# If set, serve only minified JS for UI.
USE_MINIFIED_JS = False
@@ -605,6 +609,9 @@ PUBLIC_GALAXY_SERVER = {
'url': 'https://galaxy.ansible.com'
}
# Applies to any galaxy server
GALAXY_IGNORE_CERTS = False
# List of dicts of fallback (additional) Galaxy servers. If configured, these
# will be higher precedence than public Galaxy, but lower than primary Galaxy.
# Available options: 'id', 'url', 'username', 'password', 'token', 'auth_url'

View File

@@ -52,6 +52,9 @@ COLOR_LOGS = True
# Pipe management playbook output to console
LOGGING['loggers']['awx.isolated.manager.playbooks']['propagate'] = True # noqa
# celery is annoyingly loud when docker containers start
LOGGING['loggers'].pop('celery', None) # noqa
ALLOWED_HOSTS = ['*']
mimetypes.add_type("image/svg+xml", ".svg", True)

View File

@@ -156,7 +156,13 @@ function AddEditCredentialsController (
if (credential.get('credential_type') === credentialType.get('id')) {
vm.inputSources.items = credential.get('related.input_sources.results');
}
vm.isTestable = (isEditable && credentialType.get('kind') === 'external');
if (mode === 'add') {
vm.isTestable = (models.me.get('is_superuser') && credentialType.get('kind') === 'external');
} else {
vm.isTestable = (isEditable && credentialType.get('kind') === 'external');
}
vm.getSubmitData = getSubmitData;
vm.checkForm = check;

View File

@@ -39,7 +39,7 @@ function getStatusDetails (jobStatus) {
value = choices[unmapped];
}
return { label, icon, value };
return { unmapped, label, icon, value };
}
function getStartDetails (started) {

View File

@@ -12,9 +12,9 @@
class="List-actionButton List-actionButton--delete"
data-placement="top"
ng-click="vm.cancelJob()"
ng-show="vm.status.value === 'Pending' ||
vm.status.value === 'Waiting' ||
vm.status.value === 'Running'"
ng-show="vm.status.unmapped === 'pending' ||
vm.status.unmapped === 'waiting' ||
vm.status.unmapped === 'running'"
aw-tool-tip="{{:: vm.strings.get('tooltips.CANCEL') }}"
data-original-title=""
title="">
@@ -27,11 +27,11 @@
data-placement="top"
ng-click="vm.deleteJob()"
ng-show="vm.canDelete && (
vm.status.value === 'New' ||
vm.status.value === 'Successful' ||
vm.status.value === 'Failed' ||
vm.status.value === 'Error' ||
vm.status.value === 'Canceled')"
vm.status.unmapped === 'new' ||
vm.status.unmapped === 'successful' ||
vm.status.unmapped === 'failed' ||
vm.status.unmapped === 'error' ||
vm.status.unmapped === 'canceled')"
aw-tool-tip="{{:: vm.strings.get('tooltips.DELETE') }}"
data-original-title=""
title="">

View File

@@ -21,12 +21,6 @@
src: url(/static/assets/OpenSans-Bold.ttf);
}
/* Bootstrap fix that's causing a right margin to appear
whenver a modal is opened */
body.modal-open {
margin-right: 0;
}
/* Helper Classes */
.pad-right-sm { padding-right: 10px; }
.pad-left-md { padding-left: 30px; }

View File

@@ -4,6 +4,13 @@ body {
color: @default-data-txt;
background-color: @default-secondary-bg;
font-size: 0.88rem;
/*
* Bootstrap fix that's causing a right margin and padding
* to appear whenever a modal is opened
* https://github.com/twbs/bootstrap/issues/27071
*/
margin-right: 0px !important;
padding-right: 0px !important;
}
.dropdown-toggle::after {

View File

@@ -3,6 +3,8 @@ global.$AnsibleConfig = null;
// Provided via Webpack DefinePlugin in webpack.config.js
global.$ENV = {};
global.$ConfigResponse = {};
var urlPrefix;
if ($basePath) {
@@ -383,7 +385,11 @@ angular
var stime = timestammp[lastUser.id].time,
now = new Date().getTime();
if ((stime - now) <= 0) {
$location.path('/login');
if (global.$AnsibleConfig.login_redirect_override) {
window.location.replace(global.$AnsibleConfig.login_redirect_override);
} else {
$location.path('/login');
}
}
}
// If browser refresh, set the user_is_superuser value

View File

@@ -15,7 +15,9 @@ function bootstrap (callback) {
angular.module('I18N').constant('LOCALE', locale);
}
angular.element(document).ready(() => callback());
fetchConfig(() => {
angular.element(document).ready(() => callback());
});
});
}
@@ -49,6 +51,25 @@ function fetchLocaleStrings (callback) {
request.fail(() => callback({ code: DEFAULT_LOCALE }));
}
function fetchConfig (callback) {
const request = $.ajax('/api/');
request.done(res => {
global.$ConfigResponse = res;
if (res.login_redirect_override) {
if (!document.cookie.split(';').filter((item) => item.includes('userLoggedIn=true')).length && !window.location.href.includes('/#/login')) {
window.location.replace(res.login_redirect_override);
} else {
callback();
}
} else {
callback();
}
});
request.fail(() => callback());
}
/**
* Grabs the language off of navigator for browser compatibility.
* If the language isn't set, then it falls back to the DEFAULT_LOCALE. The

View File

@@ -40,6 +40,10 @@ export default ['i18n', function(i18n) {
ALLOW_OAUTH2_FOR_EXTERNAL_USERS: {
type: 'toggleSwitch',
},
LOGIN_REDIRECT_OVERRIDE: {
type: 'text',
reset: 'LOGIN_REDIRECT_OVERRIDE'
},
ACCESS_TOKEN_EXPIRE_SECONDS: {
type: 'text',
reset: 'ACCESS_TOKEN_EXPIRE_SECONDS'

View File

@@ -7,8 +7,8 @@ function AddController ($state, models, strings) {
vm.panelTitle = strings.get('state.ADD_BREADCRUMB_LABEL');
vm.docs = {
url: 'https://docs.ansible.com/ansible-tower/latest/html/administration/clustering.html',
help_text: vm.strings.get('tooltips.DOCS_HELP_TEXT')
url: 'https://docs.ansible.com/ansible-tower/latest/html/userguide/instance_groups.html',
help_text: vm.strings.get('tooltips.IG_DOCS_HELP_TEXT')
};
vm.tab = {

View File

@@ -17,8 +17,8 @@ function EditController ($rootScope, $state, models, strings) {
vm.panelTitle = instanceGroup.get('name');
vm.docs = {
url: 'https://docs.ansible.com/ansible-tower/latest/html/administration/clustering.html',
help_text: vm.strings.get('tooltips.DOCS_HELP_TEXT')
url: 'https://docs.ansible.com/ansible-tower/latest/html/userguide/instance_groups.html',
help_text: vm.strings.get('tooltips.IG_DOCS_HELP_TEXT')
};
vm.tab = {

View File

@@ -10,6 +10,11 @@ function AddContainerGroupController(ToJSON, $scope, $state, models, strings, i1
vm.panelTitle = strings.get('state.ADD_CONTAINER_GROUP_BREADCRUMB_LABEL');
vm.lookUpTitle = strings.get('container.LOOK_UP_TITLE');
vm.docs = {
url: 'https://docs.ansible.com/ansible-tower/latest/html/administration/external_execution_envs.html#ag-container-groups',
help_text: vm.strings.get('tooltips.CG_DOCS_HELP_TEXT')
};
vm.form = instanceGroup.createFormSchema('post');
vm.form.name.required = true;
delete vm.form.name.help_text;

View File

@@ -6,7 +6,11 @@
</div>
</a>
<at-panel>
<at-panel-heading title="{{:: vm.panelTitle }}"></at-panel-heading>
<at-panel-heading title="{{:: vm.panelTitle }}">
<a class="Panel-docsLink" ng-href="{{ vm.docs.url }}" target="_blank" aw-tool-tip="{{:: vm.docs.help_text }}">
<i class="fa fa-book"></i>
</a>
</at-panel-heading>
<at-tab-group>
<at-tab state="vm.tab.details">{{:: vm.strings.get('tab.DETAILS') }}</at-tab>
<at-tab state="vm.tab.jobs">{{:: vm.strings.get('tab.JOBS') }}</at-tab>

View File

@@ -23,6 +23,11 @@ function EditContainerGroupController($rootScope, $scope, $state, models, string
vm.panelTitle = EditContainerGroupDataset.data.name;
vm.lookUpTitle = strings.get('container.LOOK_UP_TITLE');
vm.docs = {
url: 'https://docs.ansible.com/ansible-tower/latest/html/administration/external_execution_envs.html#ag-container-groups',
help_text: vm.strings.get('tooltips.CG_DOCS_HELP_TEXT')
};
vm.form = instanceGroup.createFormSchema('post');
vm.switchDisabled = false;
vm.form.disabled = !instanceGroup.has('options', 'actions.PUT');

View File

@@ -32,7 +32,8 @@ function InstanceGroupsStrings(BaseString) {
ns.tooltips = {
ADD_INSTANCE_GROUP: t.s('Create a new Instance Group'),
ASSOCIATE_INSTANCES: t.s('Associate an existing Instance'),
DOCS_HELP_TEXT: t.s('Instance Groups Help')
IG_DOCS_HELP_TEXT: t.s('Instance Groups Help'),
CG_DOCS_HELP_TEXT: t.s('Container Groups Help')
};
ns.instance = {

Some files were not shown because too many files have changed in this diff Show More