Skip to content

Commit 9252c96

Browse files
author
David Roble
committed
Updates for linchpin 1.0.4 compatibility
* cinch/bin - The 'cinchpin' command has been fully removed and deprecated. cinch and linchpin are tracking different versions of Ansible and cannot live in the same Python virtualenv, hence the need to remove this convenience wrapper. The new 'teardown' command is now available to assist with removing Jenkins slaves from Jenkins masters. This functionality was previously provided by 'cinchpin'. Thanks to greg-hellings for providing the code for this. * cinch/playbooks/install-rhel7.yml - we now create two separate virtualenvs for linchpin and cinch * cinch/playbooks/install-rhel7.yml - the Python virtualenv creation process has been stramlined by using native Ansible to work around issues with the selinux Python module and outdated versions of pip and setuptools * cinch/playbooks/install-rhel7.yml - the Beaker Python package is no longer installed in the virtualenv since with system-site-packages we can use the Beaker version that's installed via RPM * cinch/playbooks/install-rhel7.yml - the 'latest tip' install option has been removed. This is a developer tool that's no longer necessary at this time * Sphinx docs have been updated to reflect the changes in this commit * All references to 'linch-pin' have been changed to 'linchpin' as the project has been renamed * jjb/ci-jslave-project-sample.yaml - This example template now uses the two separate Python virtualenvs for linchpin and cinch * jjb/ci-jslave-project-sample.yaml - The Jenkins jobs have been renamed for less visual clutter * jjb/ci-jslave-project-sample.yaml - A bug with teardown has been fixed, where teardown would fail if the Jenkins slave never connected to the Jenkins master * jjb/install-rhel7.yml - Removed options for 'latest tip' and 'beaker' to corresponded with the related Ansible playbook changes described previously * setup.py - Bumped version to 0.9.0 * setup.py - Replaced 'cinchpin' command entry point with new 'teardown' command * setup.py - Removed linchpin as a dependency
1 parent c05173a commit 9252c96

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)