Implement acceptance test job
Implement acceptance tests. Those jobs will run in the post-review pipeline requiring access to secrets containing credentials of friendly public clouds to test sdk with them. Base job is generating a token from the given credentials and writes clouds.yaml file with the token inside instead of password. As a post step the token is physically revoked. This is done to prevent potential leakage of real credentials from the test jobs/logs. Since devstack is not a real cloud we do not use zuul secrets. Change-Id: I95af9b81e6abd51af2a7dd91cae14b56926a869c
This commit is contained in:
parent
74f8869fd9
commit
43ab59d8b3
38
.zuul.yaml
38
.zuul.yaml
@ -436,6 +436,41 @@
|
|||||||
required-projects:
|
required-projects:
|
||||||
- openstack/openstacksdk
|
- openstack/openstacksdk
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: openstacksdk-acceptance-base
|
||||||
|
parent: openstack-tox
|
||||||
|
description: Acceptance test of the OpenStackSDK on real clouds
|
||||||
|
pre-run:
|
||||||
|
- playbooks/acceptance/pre.yaml
|
||||||
|
post-run:
|
||||||
|
- playbooks/acceptance/post.yaml
|
||||||
|
|
||||||
|
- job:
|
||||||
|
name: openstacksdk-acceptance-devstack
|
||||||
|
parent: openstacksdk-functional-devstack
|
||||||
|
description: Acceptance test of the OpenStackSDK on real clouds
|
||||||
|
run:
|
||||||
|
- playbooks/acceptance/run-with-devstack.yaml
|
||||||
|
post-run:
|
||||||
|
- playbooks/acceptance/post.yaml
|
||||||
|
vars:
|
||||||
|
tox_envlist: acceptance-regular-user
|
||||||
|
tox_environment:
|
||||||
|
OPENSTACKSDK_DEMO_CLOUD: acceptance
|
||||||
|
OS_CLOUD: acceptance
|
||||||
|
OS_TEST_CLOUD: acceptance
|
||||||
|
openstack_credentials:
|
||||||
|
auth:
|
||||||
|
auth_url: "https://{{ hostvars['controller']['nodepool']['private_ipv4'] }}/identity"
|
||||||
|
username: demo
|
||||||
|
password: secretadmin
|
||||||
|
project_domain_id: default
|
||||||
|
project_name: demo
|
||||||
|
user_domain_id: default
|
||||||
|
identity_api_version: '3'
|
||||||
|
region_name: RegionOne
|
||||||
|
volume_api_version: '3'
|
||||||
|
|
||||||
- project-template:
|
- project-template:
|
||||||
name: openstacksdk-functional-tips
|
name: openstacksdk-functional-tips
|
||||||
check:
|
check:
|
||||||
@ -486,6 +521,9 @@
|
|||||||
voting: false
|
voting: false
|
||||||
- ansible-collections-openstack-functional-devstack:
|
- ansible-collections-openstack-functional-devstack:
|
||||||
voting: false
|
voting: false
|
||||||
|
post-review:
|
||||||
|
jobs:
|
||||||
|
- openstacksdk-acceptance-devstack
|
||||||
gate:
|
gate:
|
||||||
jobs:
|
jobs:
|
||||||
- opendev-buildset-registry
|
- opendev-buildset-registry
|
||||||
|
1
playbooks/acceptance/library
Symbolic link
1
playbooks/acceptance/library
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../library
|
18
playbooks/acceptance/post.yaml
Normal file
18
playbooks/acceptance/post.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
- hosts: localhost
|
||||||
|
tasks:
|
||||||
|
# TODO:
|
||||||
|
# - clean the resources, which might have been created
|
||||||
|
# - revoke the temp token explicitly
|
||||||
|
- name: read token
|
||||||
|
command: "cat {{ zuul.executor.work_root }}/.{{ zuul.build }}"
|
||||||
|
register: token_data
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: delete data file
|
||||||
|
command: "shred {{ zuul.executor.work_root }}/.{{ zuul.build }}"
|
||||||
|
|
||||||
|
- include_role:
|
||||||
|
name: revoke_token
|
||||||
|
vars:
|
||||||
|
cloud: "{{ openstack_credentials }}"
|
||||||
|
token: "{{ token_data.stdout }}"
|
60
playbooks/acceptance/pre.yaml
Normal file
60
playbooks/acceptance/pre.yaml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
- hosts: all
|
||||||
|
tasks:
|
||||||
|
- name: Get temporary token for the cloud
|
||||||
|
# nolog is important to keep job-output.json clean
|
||||||
|
no_log: true
|
||||||
|
os_auth:
|
||||||
|
cloud:
|
||||||
|
profile: "{{ openstack_credentials.profile | default(omit) }}"
|
||||||
|
auth:
|
||||||
|
auth_url: "{{ openstack_credentials.auth.auth_url }}"
|
||||||
|
username: "{{ openstack_credentials.auth.username }}"
|
||||||
|
password: "{{ openstack_credentials.auth.password }}"
|
||||||
|
user_domain_name: "{{ openstack_credentials.auth.user_domain_name | default(omit) }}"
|
||||||
|
user_domain_id: "{{ openstack_credentials.auth.user_domain_id | default(omit) }}"
|
||||||
|
domain_name: "{{ openstack_credentials.auth.domain_name | default(omit) }}"
|
||||||
|
domain_id: "{{ openstack_credentials.auth.domain_id | default(omit) }}"
|
||||||
|
project_name: "{{ openstack_credentials.auth.project_name | default(omit) }}"
|
||||||
|
project_id: "{{ openstack_credentials.auth.project_id | default(omit) }}"
|
||||||
|
project_domain_name: "{{ openstack_credentials.auth.project_domain_name | default(omit) }}"
|
||||||
|
project_domain_id: "{{ openstack_credentials.auth.project_domain_id | default(omit) }}"
|
||||||
|
register: os_auth
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Verify token
|
||||||
|
no_log: true
|
||||||
|
os_auth:
|
||||||
|
cloud:
|
||||||
|
profile: "{{ openstack_credentials.profile | default(omit) }}"
|
||||||
|
auth_type: token
|
||||||
|
auth:
|
||||||
|
auth_url: "{{ openstack_credentials.auth.auth_url }}"
|
||||||
|
token: "{{ os_auth.auth_token }}"
|
||||||
|
project_name: "{{ openstack_credentials.auth.project_name | default(omit) }}"
|
||||||
|
project_id: "{{ openstack_credentials.auth.project_id | default(omit) }}"
|
||||||
|
project_domain_id: "{{ openstack_credentials.auth.project_domain_id | default(omit) }}"
|
||||||
|
project_domain_name: "{{ openstack_credentials.auth.project_domain_name | default(omit) }}"
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Include deploy-clouds-config role
|
||||||
|
include_role:
|
||||||
|
name: deploy-clouds-config
|
||||||
|
vars:
|
||||||
|
cloud_config:
|
||||||
|
clouds:
|
||||||
|
acceptance:
|
||||||
|
profile: "{{ openstack_credentials.profile | default(omit) }}"
|
||||||
|
auth_type: "token"
|
||||||
|
auth:
|
||||||
|
auth_url: "{{ openstack_credentials.auth.auth_url | default(omit) }}"
|
||||||
|
project_name: "{{ openstack_credentials.auth.project_name | default(omit) }}"
|
||||||
|
token: "{{ os_auth.auth_token }}"
|
||||||
|
|
||||||
|
# Intruders might want to corrupt clouds.yaml to avoid revoking token in the post phase
|
||||||
|
# To prevent this we save token on the executor for later use.
|
||||||
|
- name: Save token
|
||||||
|
delegate_to: localhost
|
||||||
|
copy:
|
||||||
|
dest: "{{ zuul.executor.work_root }}/.{{ zuul.build }}"
|
||||||
|
content: "{{ os_auth.auth_token }}"
|
||||||
|
mode: "0440"
|
83
playbooks/acceptance/run-with-devstack.yaml
Normal file
83
playbooks/acceptance/run-with-devstack.yaml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Need to actually start devstack first
|
||||||
|
- hosts: all
|
||||||
|
roles:
|
||||||
|
- run-devstack
|
||||||
|
|
||||||
|
# Prepare local clouds.yaml
|
||||||
|
# We can't rely on pre.yaml, since it is specifically delegates to
|
||||||
|
# localhost, while on devstack it will not work unless APIs are available
|
||||||
|
# over the net.
|
||||||
|
- hosts: all
|
||||||
|
tasks:
|
||||||
|
- name: Get temporary token for the cloud
|
||||||
|
# nolog is important to keep job-output.json clean
|
||||||
|
no_log: true
|
||||||
|
os_auth:
|
||||||
|
cloud:
|
||||||
|
profile: "{{ openstack_credentials.profile | default(omit) }}"
|
||||||
|
auth:
|
||||||
|
auth_url: "{{ openstack_credentials.auth.auth_url }}"
|
||||||
|
username: "{{ openstack_credentials.auth.username }}"
|
||||||
|
password: "{{ openstack_credentials.auth.password }}"
|
||||||
|
user_domain_name: "{{ openstack_credentials.auth.user_domain_name | default(omit) }}"
|
||||||
|
user_domain_id: "{{ openstack_credentials.auth.user_domain_id | default(omit) }}"
|
||||||
|
domain_name: "{{ openstack_credentials.auth.domain_name | default(omit) }}"
|
||||||
|
domain_id: "{{ openstack_credentials.auth.domain_id | default(omit) }}"
|
||||||
|
project_name: "{{ openstack_credentials.auth.project_name | default(omit) }}"
|
||||||
|
project_id: "{{ openstack_credentials.auth.project_id | default(omit) }}"
|
||||||
|
project_domain_name: "{{ openstack_credentials.auth.project_domain_name | default(omit) }}"
|
||||||
|
project_domain_id: "{{ openstack_credentials.auth.project_domain_id | default(omit) }}"
|
||||||
|
register: os_auth
|
||||||
|
|
||||||
|
- name: Verify token
|
||||||
|
# nolog is important to keep job-output.json clean
|
||||||
|
no_log: true
|
||||||
|
os_auth:
|
||||||
|
cloud:
|
||||||
|
profile: "{{ openstack_credentials.profile | default(omit) }}"
|
||||||
|
auth_type: token
|
||||||
|
auth:
|
||||||
|
auth_url: "{{ openstack_credentials.auth.auth_url }}"
|
||||||
|
token: "{{ os_auth.auth_token }}"
|
||||||
|
project_name: "{{ openstack_credentials.auth.project_name | default(omit) }}"
|
||||||
|
project_id: "{{ openstack_credentials.auth.project_id | default(omit) }}"
|
||||||
|
project_domain_name: "{{ openstack_credentials.auth.project_domain_name | default(omit) }}"
|
||||||
|
project_domain_id: "{{ openstack_credentials.auth.project_domain_id | default(omit) }}"
|
||||||
|
|
||||||
|
- name: Include deploy-clouds-config role
|
||||||
|
include_role:
|
||||||
|
name: deploy-clouds-config
|
||||||
|
vars:
|
||||||
|
cloud_config:
|
||||||
|
clouds:
|
||||||
|
acceptance:
|
||||||
|
profile: "{{ openstack_credentials.profile | default(omit) }}"
|
||||||
|
auth_type: "token"
|
||||||
|
auth:
|
||||||
|
|
||||||
|
auth_url: "{{ openstack_credentials.auth.auth_url }}"
|
||||||
|
project_name: "{{ openstack_credentials.auth.project_name | default(omit) }}"
|
||||||
|
project_domain_id: "{{ openstack_credentials.auth.project_domain_id | default(omit) }}"
|
||||||
|
token: "{{ os_auth.auth_token }}"
|
||||||
|
verify: false
|
||||||
|
|
||||||
|
# Intruders might want to corrupt clouds.yaml to avoid revoking token in
|
||||||
|
# the post phase. To prevent this we save token on the executor for later
|
||||||
|
# use.
|
||||||
|
- name: Save token
|
||||||
|
delegate_to: localhost
|
||||||
|
copy:
|
||||||
|
dest: "{{ zuul.executor.work_root }}/.{{ zuul.build }}"
|
||||||
|
content: "{{ os_auth.auth_token }}"
|
||||||
|
mode: "0640"
|
||||||
|
|
||||||
|
# Run the rest
|
||||||
|
- hosts: all
|
||||||
|
roles:
|
||||||
|
- role: bindep
|
||||||
|
bindep_profile: test
|
||||||
|
bindep_dir: "{{ zuul_work_dir }}"
|
||||||
|
- test-setup
|
||||||
|
- ensure-tox
|
||||||
|
- get-devstack-os-environment
|
||||||
|
- tox
|
45
playbooks/library/os_auth.py
Normal file
45
playbooks/library/os_auth.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Utility to get Keystone token
|
||||||
|
"""
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
|
import openstack
|
||||||
|
|
||||||
|
|
||||||
|
def get_cloud(cloud):
|
||||||
|
if isinstance(cloud, dict):
|
||||||
|
config = openstack.config.loader.OpenStackConfig().get_one(**cloud)
|
||||||
|
return openstack.connection.Connection(config=config)
|
||||||
|
else:
|
||||||
|
return openstack.connect(cloud=cloud)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = AnsibleModule(
|
||||||
|
argument_spec=dict(
|
||||||
|
cloud=dict(required=True, type='raw', no_log=True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cloud = get_cloud(module.params.get('cloud'))
|
||||||
|
module.exit_json(
|
||||||
|
changed=True,
|
||||||
|
auth_token=cloud.auth_token
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
0
roles/deploy-clouds-config/README.rst
Normal file
0
roles/deploy-clouds-config/README.rst
Normal file
1
roles/deploy-clouds-config/defaults/main.yaml
Normal file
1
roles/deploy-clouds-config/defaults/main.yaml
Normal file
@ -0,0 +1 @@
|
|||||||
|
zuul_work_dir: "{{ zuul.project.src_dir }}"
|
11
roles/deploy-clouds-config/tasks/main.yaml
Normal file
11
roles/deploy-clouds-config/tasks/main.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
- name: Create OpenStack config dir
|
||||||
|
ansible.builtin.file:
|
||||||
|
dest: ~/.config/openstack
|
||||||
|
state: directory
|
||||||
|
recurse: true
|
||||||
|
|
||||||
|
- name: Deploy clouds.yaml
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: clouds.yaml.j2
|
||||||
|
dest: ~/.config/openstack/clouds.yaml
|
||||||
|
mode: 0440
|
2
roles/deploy-clouds-config/templates/clouds.yaml.j2
Normal file
2
roles/deploy-clouds-config/templates/clouds.yaml.j2
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
{{ cloud_config | to_nice_yaml }}
|
0
roles/revoke_token/README.rst
Normal file
0
roles/revoke_token/README.rst
Normal file
72
roles/revoke_token/library/os_auth_revoke.py
Normal file
72
roles/revoke_token/library/os_auth_revoke.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright 2014 Rackspace Australia
|
||||||
|
# Copyright 2018 Red Hat, Inc
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Utility to revoke Keystone token
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
import keystoneauth1.exceptions
|
||||||
|
import requests
|
||||||
|
import requests.exceptions
|
||||||
|
|
||||||
|
import openstack
|
||||||
|
|
||||||
|
|
||||||
|
def get_cloud(cloud):
|
||||||
|
if isinstance(cloud, dict):
|
||||||
|
config = openstack.config.loader.OpenStackConfig().get_one(**cloud)
|
||||||
|
return openstack.connection.Connection(config=config)
|
||||||
|
else:
|
||||||
|
return openstack.connect(cloud=cloud)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
module = AnsibleModule(
|
||||||
|
argument_spec=dict(
|
||||||
|
cloud=dict(required=True, type='raw', no_log=True),
|
||||||
|
revoke_token=dict(required=True, type='str', no_log=True)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
p = module.params
|
||||||
|
cloud = get_cloud(p.get('cloud'))
|
||||||
|
try:
|
||||||
|
cloud.identity.delete(
|
||||||
|
'/auth/tokens',
|
||||||
|
headers={
|
||||||
|
'X-Subject-Token': p.get('revoke_token')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except (keystoneauth1.exceptions.http.HttpError,
|
||||||
|
requests.exceptions.RequestException):
|
||||||
|
s = "Error performing token revoke"
|
||||||
|
logging.exception(s)
|
||||||
|
s += "\n" + traceback.format_exc()
|
||||||
|
module.fail_json(
|
||||||
|
changed=False,
|
||||||
|
msg=s,
|
||||||
|
cloud=cloud.name,
|
||||||
|
region_name=cloud.config.region_name)
|
||||||
|
module.exit_json(changed=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
7
roles/revoke_token/tasks/main.yaml
Normal file
7
roles/revoke_token/tasks/main.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
- name: Revoke token
|
||||||
|
delegate_to: localhost
|
||||||
|
no_log: true
|
||||||
|
os_auth_revoke:
|
||||||
|
cloud: "{{ cloud }}"
|
||||||
|
revoke_token: "{{ token }}"
|
||||||
|
failed_when: false
|
Loading…
Reference in New Issue
Block a user