Skip to content

Commit 7cf38df

Browse files
Merge pull request #192 from robled/cinch_installer_fix
Updates for linchpin 1.0.4 compatibility
2 parents c05173a + 9252c96 commit 7cf38df

8 files changed

+144
-341
lines changed

CHANGELOG

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
v 0.9.0 (16 Nov 2017)
2+
- IMPORTANT - The 'cinchpin' command has been REMOVED for linchpin 1.0.4
3+
compatibility. The new 'teardown' command replaces Jenkins slave
4+
disconnection functionality previously handled by the 'cinchpin' command.
5+
- The RHEL7 installer now creates two virtualenvs, one for linchpin and one for
6+
cinch
7+
- Removed 'latest tip' and Beaker python package installation options from
8+
RHEL7 installer as they are no longer necessary
9+
- Fixed a bug where Jenkins slaves would not be removed from the master during
10+
a provisioning failure in our JJB example workflow
11+
(ci-jslave-project-sample.yaml)
12+
113
v 0.8.5 (10 Oct 2017)
214
- Remove management of the executor setting on masters (GH #182)
315
- Remove stale, unused repo key that began failing (GH #184)

cinch/bin/entry_point.py

+23-37
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,12 @@
22
from __future__ import print_function
33
from argparse import ArgumentParser, REMAINDER
44
from os import getcwd, path
5-
from wrappers import call_ansible, call_linchpin
5+
from wrappers import call_ansible
66

77
import sys
88

99

10-
def cinch():
11-
"""
12-
Entry point for the "cinch" CLI that merely wraps the ansible-playbook
13-
command and pre-fills its path to the site.yml file for Cinch. The cinch
14-
tool requires a single argument - the Ansible inventory file - and accepts
15-
an arbitrary number of extra arguments that are passed through to the
16-
ansible-playbook executable.
17-
18-
:return: Exit code 0 if the execution is completed successfully, or 255
19-
if an unknown error occurs. If ansible-playbook exits with an error code,
20-
this executable will exit with the same code.
21-
"""
10+
def cinch_generic(playbook):
2211
# Parse the command line arguments
2312
parser = ArgumentParser(description='A wrapper around Cinch for the most '
2413
'common use case')
@@ -35,38 +24,35 @@ def cinch():
3524
inventory = path.join(getcwd(), args.inventory)
3625
else:
3726
raise Exception('Inventory path needs to be non-empty')
38-
exit_code = call_ansible(inventory, 'site.yml', args.args)
27+
exit_code = call_ansible(inventory, playbook, args.args)
3928
sys.exit(exit_code)
4029

4130

42-
def cinchpin():
31+
def cinch():
4332
"""
44-
Entry point for the "cinchpin" CLI that wraps the linchpin command and
45-
loads the linch-pin PinFile to provision resources and then uses the
46-
generated inventory file to pass to cinch. The cinchpin tool requires a
47-
single argument - a valid linchpin subcommand - and accepts an arbitrary
48-
number of extra arguments that are passed through to the linchpin
49-
executable. If a linch-pin PinFile is not found in the current working
50-
directory, a path to a linch-pin working directory may be optionally
51-
provided.
33+
Entry point for the "cinch" CLI that merely wraps the ansible-playbook
34+
command and pre-fills its path to the site.yml file for Cinch. The cinch
35+
tool requires a single argument - the Ansible inventory file - and accepts
36+
an arbitrary number of extra arguments that are passed through to the
37+
ansible-playbook executable.
5238
5339
:return: Exit code 0 if the execution is completed successfully, or 255
54-
if an unknown error occurs. If linchpin exits with an error code,
40+
if an unknown error occurs. If ansible-playbook exits with an error code,
5541
this executable will exit with the same code.
5642
"""
57-
# Parse the command line arguments
58-
parser = ArgumentParser(description='A wrapper around linchpin for the '
59-
'most common use case')
60-
# The linch-pin working directory containing a PinFile that the user
61-
# provides which will get passed along to linchpin for its consumption
62-
parser.add_argument('-w', '--workdir', default=getcwd(),
63-
help='''path to linch-pin working directory containing a
64-
PinFile''')
65-
# All remaining arguments are passed through, untouched, to linchpin
66-
parser.add_argument('arg', help='argument to pass to the linchpin command')
67-
args = parser.parse_args()
68-
exit_code = call_linchpin(args.workdir, args.arg)
69-
sys.exit(exit_code)
43+
cinch_generic('site.yml')
44+
45+
46+
def teardown():
47+
"""
48+
Entry point for the "teardown" CLI that wraps ansible-playbook commands and
49+
pre-fills its path to the teardown.yml file.
50+
51+
:return: Exit code 0 if the execution is completed successfully, or 255 if
52+
an unknown error occurs. If ansible-playbook exits with an error code, this
53+
executable will exit with the same code.
54+
"""
55+
cinch_generic('teardown.yml')
7056

7157

7258
if __name__ == '__main__':

cinch/bin/wrappers.py

+2-137
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import os
88
import sys
9-
import yaml
109

1110

1211
BASE = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
@@ -36,149 +35,15 @@ def call_ansible(inventory, playbook, *args):
3635
os.path.join(BASE, playbook),
3736
'-i', inventory,
3837
'-v',
39-
'--ssh-common-args=-o StrictHostKeyChecking=no'
38+
'--ssh-common-args=-o StrictHostKeyChecking=no ' +
39+
'-o UserKnownHostsFile=/dev/null'
4040
]
4141
ansible_args.extend(args)
4242
ansible = local['ansible-playbook']
4343
exit_code = command_handler(ansible, ansible_args)
4444
return exit_code
4545

4646

47-
def call_linchpin(work_dir, arg):
48-
"""
49-
Wraps a call out to the linchpin executable, and then kicks off a cinch
50-
Ansible playbook if necessary.
51-
52-
:param work_dir: The linch-pin working directory that contains a PinFile
53-
and associated configuration files
54-
:param arg: A single argument to pass to the linchpin command
55-
:return: The exit code returned from linchpin, or 255 if errors come from
56-
elsewhere
57-
"""
58-
# cinch will only support a subset of linchpin subcommands
59-
supported_cmds = ['up', 'destroy', 'init']
60-
if arg not in supported_cmds:
61-
sys.exit('linchpin command "{0}" not '
62-
'supported by cinch'.format(arg))
63-
64-
# If we are to ask linch-pin to interact with infrastructure we will check
65-
# for some required configuration items and set up them for later use
66-
if arg != 'init':
67-
inventory_file = get_inventory(work_dir)
68-
inventory_path = os.path.join(work_dir, 'inventories', inventory_file)
69-
70-
# For destroy/teardown, we must run our teardown playbook(s) *before*
71-
# linchpin terminates the instance(s)
72-
if arg == 'destroy':
73-
exit_code = call_ansible(inventory_path, 'teardown.yml')
74-
75-
# Construct the arguments to pass to linch-pin by munging the arguments
76-
# provided to this method
77-
linchpin_args = [
78-
'-v',
79-
'-w', work_dir,
80-
'--creds-path', os.path.join(work_dir, 'credentials')
81-
]
82-
linchpin_args.append(arg)
83-
# Execute the 'linchpin' command
84-
linchpin = local['linchpin']
85-
exit_code = command_handler(linchpin, linchpin_args)
86-
87-
# Set up a linch-pin+cinch configuration skeleton for later use if the
88-
# 'init' subcommand was executed previously
89-
if arg == 'init':
90-
cinchpin_init(work_dir)
91-
92-
# If linchpin is asked to provision resources, we will then run our
93-
# cinch provisioning playbook
94-
if arg == 'up' and exit_code == 0:
95-
exit_code = call_ansible(inventory_path, 'site.yml')
96-
return exit_code
97-
98-
99-
def cinchpin_init(work_dir):
100-
"""
101-
Set up a linch-pin+cinch configuration skeleton
102-
103-
:param work_dir: The linch-pin working directory that contains a PinFile
104-
and associated configuration files
105-
"""
106-
# Consistent filename to use for various linch-pin YAML configurations
107-
# for 'cinchpin'
108-
config_file = 'cinch.yml'
109-
# Cinch layout and topology paths to be added to linch-pin PinFile
110-
config_setup = {
111-
'cinch': {
112-
'topology': config_file,
113-
'layout': config_file
114-
}
115-
}
116-
117-
# Ansible group_vars directory that will be created for later use
118-
group_vars = os.path.join('inventories', 'group_vars')
119-
120-
# Dictionary of workspace directories and target filenames where we will
121-
# put our skeletons
122-
local_paths = {
123-
'layouts': config_file,
124-
'topologies': config_file,
125-
'credentials': config_file,
126-
group_vars: 'all'
127-
}
128-
129-
# Overwrite the PinFile that linch-pin created with our configuration
130-
pin_file = os.path.join(work_dir, 'PinFile')
131-
with open(pin_file, 'w') as f:
132-
yaml.dump(config_setup, f, default_flow_style=False)
133-
134-
# Create Ansible group_vars directory since linch-pin doesn't provide this
135-
os.mkdir(os.path.join(work_dir, group_vars))
136-
137-
# Write out the skeletons and inform the user that they exist
138-
for directory, filename in local_paths.items():
139-
path = os.path.join(work_dir, directory, filename)
140-
with open(path, 'w') as f:
141-
f.write(SKEL_TEXT.format(directory, DOCS))
142-
print('Please configure this file to use cinch: ' + path)
143-
print('Example configurations: ' + DOCS)
144-
145-
146-
def get_inventory(work_dir):
147-
"""
148-
Basic checks for cinch compatibility in the linch-pin working directory,
149-
and if successful, we produce a topology file for cinch to use.
150-
151-
:param work_dir: The linch-pin working directory as created by 'linchpin
152-
init' or 'cinchpin init'
153-
:return: The topology file to pass to the 'cinch' command
154-
"""
155-
# Attempt to open the linch-pin PinFile
156-
try:
157-
with open(os.path.join(work_dir, 'PinFile'), 'r') as f:
158-
pin_file_yaml = yaml.safe_load(f)
159-
except IOError:
160-
sys.exit('linch-pin PinFile not found in ' + work_dir)
161-
# We must find a topology section named 'cinch' to determine where our
162-
# inventory file will live
163-
try:
164-
cinch_topology = 'cinch'
165-
topology = pin_file_yaml[cinch_topology]['topology']
166-
except KeyError:
167-
sys.exit('linch-pin PinFile must contain a topology '
168-
'section named "{0}"'.format(cinch_topology))
169-
# The inventory file generated by linchpin that will be used by cinch for
170-
# configuration
171-
try:
172-
topology_path = os.path.join(work_dir, 'topologies', topology)
173-
with open(topology_path) as topology_file:
174-
topology_yaml = yaml.safe_load(topology_file)
175-
inventory_file = topology_yaml['topology_name'] + '.inventory'
176-
except (IOError, TypeError):
177-
sys.exit('linch-pin topology file not found or malformed: ' +
178-
topology_path)
179-
return inventory_file
180-
181-
18247
def command_handler(command, args):
18348
"""
18449
Generic function to run external programs.

cinch/playbooks/install-rhel7.yml

+30-61
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@
44
# on RHEL7 to work with newer Python libraries such as those required by
55
# 'cinch'.
66
#
7-
# This playbook was tested with Ansible 1.8.4.
7+
# This playbook was tested with Ansible 2.4.1.0
88

99
- name: install cinch into a virtualenv on RHEL7
1010
hosts: localhost
1111
vars:
1212
jenkins_home: /var/lib/jenkins
13-
venv_dir: "{{ jenkins_home }}/opt/cinch"
14-
temp_dir: "{{ venv_dir }}/tmp"
15-
python: "{{ venv_dir }}/bin/python"
13+
venvs:
14+
cinch: "{{ jenkins_home }}/opt/cinch"
15+
linchpin: "{{ jenkins_home }}/opt/linchpin"
1616
delete_venv: false
17-
latest_tip: false
18-
beaker_kerberos: true
1917

2018
tasks:
2119
- name: fail if we are not running this playbook on RHEL7
@@ -38,77 +36,48 @@
3836
msg: "directory {{ jenkins_home }} must exist for this playbook to run"
3937
when: not jenkins_home_stat_result.stat.exists
4038

41-
- name: check for /var/lib/jenkins/opt/cinch directory
39+
- name: check for existing virtualenvs
4240
stat:
43-
path: "{{ venv_dir }}"
41+
path: "{{ item.value }}"
42+
with_dict: "{{ venvs }}"
4443
register: venv_stat_result
4544

4645
- name: >-
47-
fail if pre-existing cinch installation at
48-
/var/lib/jenkins/opt/cinch is found and it cannot be deleted
46+
fail if pre-existing virtualenvs are found and cannot be deleted because
47+
delete_venv is set to 'false'
4948
fail:
50-
msg: "directory {{ venv_dir }} exists, but 'delete_venv' setting is False"
51-
when: venv_stat_result.stat.exists and not (delete_venv|bool)
49+
msg: "directory {{ item.item.value }} exists, but 'delete_venv' setting is False"
50+
with_items: "{{ venv_stat_result.results }}"
51+
when: item.stat.exists == true and not (delete_venv|bool)
5252
5353
- name: >-
5454
delete existing virtualenv directory (disabled by default, override
5555
with DELETE_VENV Jenkins job parameter or delete_venv playbook variable)
5656
file:
57-
path: "{{ venv_dir }}"
57+
path: "{{ item.value }}"
5858
state: absent
59+
with_dict: "{{ venvs }}"
5960
when: (delete_venv|bool)
6061
61-
- name: create virtualenv
62-
command: virtualenv --no-setuptools "{{ venv_dir }}"
63-
args:
64-
creates: "{{ venv_dir }}"
65-
66-
- name: create temp dir in root of virtualenv
67-
file:
68-
path: "{{ temp_dir }}"
69-
state: directory
70-
71-
- name: download latest version of pip (version included with RHEL7 is too old)
72-
get_url:
73-
url: https://bootstrap.pypa.io/get-pip.py
74-
dest: "{{ temp_dir }}"
75-
76-
- name: install pip manually by running get-pip.py script
77-
command: "{{ python }} {{ temp_dir }}/get-pip.py"
78-
args:
79-
creates: "{{ venv_dir }}/lib/python2.7/site-packages/setuptools"
80-
81-
- name: install released versions of cinch+linch-pin using pip
62+
- name: >-
63+
create virtualenvs with --system-site-packages to allow for selinux
64+
module compatibility, then upgrade setuptools and pip
8265
pip:
83-
name: cinch
84-
virtualenv: "{{ venv_dir }}"
66+
name: setuptools pip
67+
virtualenv: "{{ item.value }}"
8568
extra_args: -U
86-
when: not (latest_tip|bool)
69+
virtualenv_site_packages: true
70+
with_dict: "{{ venvs }}"
8771
88-
# This pip install should be non-editable, but the pip module in Ansible
89-
# 1.8.4. does not support that flag
90-
- name: install latest tip of cinch+linch-pin instead of latest release from pypi
91-
command: >-
92-
"{{ venv_dir }}/bin/pip" install -U
93-
https://github.com/CentOS-PaaS-SIG/linchpin/archive/develop.tar.gz
94-
https://github.com/RedHatQE/cinch/archive/master.tar.gz
95-
when: (latest_tip|bool)
96-
97-
- name: install beaker-client and python-krbV with pip to use kerberos with Beaker
72+
- name: install version 1.0.4 of linchpin using pip
9873
pip:
99-
name: "{{ item }}"
100-
virtualenv: "{{ venv_dir }}"
74+
name: linchpin
75+
virtualenv: "{{ venvs.linchpin }}"
10176
extra_args: -U
102-
with_items:
103-
- beaker-client
104-
- python-krbV
105-
when: (beaker_kerberos|bool)
77+
version: 1.0.4
10678

107-
## https://dmsimard.com/2016/01/08/selinux-python-virtualenv-chroot-and-ansible-dont-play-nice/
108-
- name: >-
109-
set up symlink in virtualenv for selinux module in system site-packages
110-
since it's not pip installable
111-
file:
112-
src: /usr/lib64/python2.7/site-packages/selinux
113-
dest: "{{ venv_dir }}/lib/python2.7/site-packages/selinux"
114-
state: link
79+
- name: install released version of cinch using pip
80+
pip:
81+
name: cinch
82+
virtualenv: "{{ venvs.cinch }}"
83+
extra_args: -U

0 commit comments

Comments
 (0)