Skip to content
This repository was archived by the owner on Oct 12, 2023. It is now read-only.

Commit 3f0ef10

Browse files
committed
Merge branch 'release/1.2.0'
2 parents a36d7d9 + 68f2944 commit 3f0ef10

File tree

14 files changed

+499
-67
lines changed

14 files changed

+499
-67
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,4 @@ ENV/
9090

9191
#Vagrant machine stuff
9292
.vagrant
93+
settings.ini

CHANGELOG

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## [1.2.0] - 2018-01-19
2+
## Summary
3+
This update updated some wording under the search in order to make it clear
4+
that the search works best in non-IE browsers. Also added the ability to search for
5+
a project based on the QPI name or Gatorlink. Finally, the facets sorting changed
6+
from most used to alphabetical.
7+
### Added
8+
* Update FAQ on home page to match approver which added a new question (Matthew McConnell)
9+
* Add a line about browser compatibility and make text larger under search (Matthew McConnell)
10+
* Add the ability to search on QPI name and gatorlink (Matthew McConnell)
11+
### Changed
12+
* Change the facet to sort by name instead of project count (Matthew McConnell)
13+
114
## [1.1.0] - 2017-02-06
215
## Summary
316
This release updates some broken links and fixes a bug where big

qipr/fabfile.py

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
"""
2+
This fab file takes the QIPR project and deploys it using a provided settings file.
3+
The settings files contains secrets, so it should be kept locally somewhere outside
4+
the git directory. The secrets file path in this script needs to be changed to match your installation.
5+
6+
7+
The basic deployment command (for staging) is "fab s deploy:0.7.0"
8+
fab - calling the fab command
9+
s - using the alias for stage, but could also be invoked using the whole word "stage"
10+
deploy - function to run called deploy
11+
:0.7.0 - run deploy and pass in the argument 0.7.0
12+
13+
The settings file needs to include the following fields with the correct settings:
14+
[fabric_deploy]
15+
deploy_user = deploy #user account associated with deploying
16+
deploy_user_group = www-data #group that the deploy account is part of
17+
staging_host = my.staging.host.com #staging server
18+
production_host = my.production.host.com #production server
19+
admin_user = superman #account with root privilege on the server
20+
live_pre_path = /var/www #directory path leading up to the actual project directory
21+
backup_pre_path = /var/www.backup #directory path leading up to the backup project directory
22+
project_path = qipr/approver #where the project files are going to sit
23+
ssh_keyfile_path = /my/user/.ssh/id_rsa #path to a local ssh private key for easy login
24+
25+
In order to keep deployments easy, the setups across all environments will remain
26+
constant, with the only difference being the server ip. The directory paths will
27+
remain constant.
28+
29+
There are 3 environments this script will be able to deploy to:
30+
vagrant: includes the default admin user name and ssh key location, ip, and port
31+
staging: this is the test live server.
32+
production: this is the functional live server.
33+
Each environment includes a boolean flag to enable running as root for certain
34+
functions in this script.
35+
36+
Running deploy is the main function to get a version sent off to the server.
37+
38+
If the server is not setup for deployment yet, there are some additional helper
39+
functions to create a deployment user and set up its permissions.
40+
setup_server - creates the user and creates directories
41+
setup_webspace - just creates the directories for the deploy user
42+
43+
There are also functions to add ssh keys to the deploy user for simple
44+
ssh commands using a passed in string or pub file location.
45+
"""
46+
47+
from fabric.api import *
48+
from fabric.contrib.files import exists
49+
from fabric.utils import abort
50+
from datetime import datetime
51+
import configparser, string, random, os
52+
53+
config = configparser.ConfigParser()
54+
settings_file_path = 'qipr/deploy/settings.ini' #path to where app is looking for settings.ini
55+
56+
def get_config(key):
57+
return config.get("fabric_deploy", key)
58+
59+
def define_env():
60+
"""
61+
This function sets up some global variables
62+
"""
63+
64+
#first, copy the secrets file into the deploy directory
65+
if os.path.exists(settings_file_path):
66+
config.read(settings_file_path)
67+
else:
68+
print("The secrets file path cannot be found. It is set to: %s" % settings_file_path)
69+
abort("Secrets File not set")
70+
71+
env.user = get_config('deploy_user') #default ssh deploy user account
72+
env.project_name = get_config('project_name')
73+
env.project_settings_path = get_config('project_settings_path')
74+
env.live_project_full_path = get_config('live_pre_path') + "/" + get_config('project_path') #
75+
env.backup_project_full_path = get_config('backup_pre_path') + "/" + get_config('project_path')
76+
env.key_filename = get_config('ssh_keyfile_path')
77+
78+
@task(alias='v')
79+
def vagrant(admin=False):
80+
"""
81+
Set up deployment for vagrant
82+
83+
admin: True or False depending on if running as admin
84+
"""
85+
#TODO: vagrant ssh-config gives these details, we can read them and strip them out automatically
86+
87+
define_env()
88+
if admin:
89+
env.user = 'vagrant'
90+
env.key_filename = '../vagrant/.vagrant/machines/qipr/virtualbox/private_key' #This is the hardcoded vagrant key based on this projects file structure
91+
92+
env.hosts = ['127.0.0.1']
93+
env.port = '2222'
94+
95+
@task(alias='s')
96+
def stage(admin=False):
97+
"""
98+
Set up deployment for staging server
99+
100+
admin: True or False depending on if running as admin
101+
"""
102+
103+
define_env()
104+
if admin:
105+
env.user = get_config('admin_user')
106+
107+
env.hosts = get_config('staging_host')
108+
#env.port = ### #Uncomment this line if a specific port is required
109+
110+
@task(alias='p')
111+
def prod(admin=False):
112+
"""
113+
Set up deployment for production server
114+
115+
admin: True or False depending on if running as admin
116+
"""
117+
118+
define_env()
119+
if admin:
120+
env.user = get_config('admin_user')
121+
122+
env.hosts = get_config('production_host')
123+
#env.port = ### #Uncomment this line if a specific port is required
124+
125+
@task
126+
def git_version(version):
127+
"""
128+
Pulls the requested version from Master for deployment
129+
130+
version: the version applied to the tag of the release
131+
Assumptions:
132+
git is installed and configured
133+
Master branch contains releases with the version number as a tag
134+
"""
135+
136+
# local("git stash save 'Stashing current changes while releasing version %s'" % version)
137+
local("git fetch --all; git checkout %s" % (version))
138+
139+
def package_files():
140+
"""
141+
This function will go into the project directory and zip all
142+
of the required files
143+
"""
144+
#pull out file name for reuse
145+
env.package_name = '%(project_name)s-%(project_version)s.tar.gzip' % env
146+
147+
#create the package
148+
local("tar -cz --exclude='__pycache__' --exclude='.DS_Store' \
149+
-f %(package_name)s \
150+
manage.py requirements.txt \
151+
qipr/deploy/settings.ini \
152+
qipr/__init__.py \
153+
qipr/settings.py \
154+
qipr/urls.py \
155+
qipr/wsgi.py \
156+
registry/ \
157+
static/" % env)
158+
159+
def create_backup():
160+
"""
161+
This function creates a backup of the current live directory using the project name and current time
162+
"""
163+
with cd("%(backup_project_full_path)s" % env):
164+
run("mkdir -p backup")
165+
if exists('live'):
166+
run("tar -cz -f backup/%s-%s.tar.gzip live" % (env.project_name, datetime.now().strftime("%Y%m%dT%H%M%Z")))
167+
168+
#TODO create restore backup function
169+
170+
def unpackage_files():
171+
"""
172+
Unpackage the file in the remote backup directory
173+
"""
174+
with cd("%(backup_project_full_path)s" % env):
175+
run("mkdir -p archive/%(project_name)s-%(project_version)s" % env)
176+
run("tar -x -C archive/%(project_name)s-%(project_version)s \
177+
-f %(project_name)s-%(project_version)s.tar.gzip" % env)
178+
179+
def create_venv():
180+
"""
181+
Create a new virtual environment and install requirements
182+
"""
183+
184+
with cd("%(backup_project_full_path)s/archive/%(project_name)s-%(project_version)s" % env):
185+
run("virtualenv venv")
186+
run("source venv/bin/activate && pip install -r requirements.txt && deactivate")
187+
188+
def link_to_live():
189+
"""
190+
This function creates a symlink from the backup to the live www directory for the server
191+
to run the app from.
192+
"""
193+
run("ln -sf -T %(backup_project_full_path)s/archive/%(project_name)s-%(project_version)s \
194+
%(live_project_full_path)s" % env)
195+
196+
def refresh_server():
197+
"""
198+
Touchs the wsgi file to have the server reload the necessary files
199+
This may not be needed since we are overwriting the files anyways
200+
"""
201+
run("touch %(live_project_full_path)s/%(project_settings_path)s/wsgi.py" % env)
202+
203+
def ship_to_host():
204+
"""
205+
Move the zip file to the correct remote directory
206+
"""
207+
put('%(project_name)s-%(project_version)s.tar.gzip' % env, '%(backup_project_full_path)s' % env)
208+
209+
def clean_up():
210+
#clean up local files
211+
local('rm %(project_name)s-%(project_version)s.tar.gzip' % env)
212+
213+
#clean up remote files
214+
run('rm %(backup_project_full_path)s/%(project_name)s-%(project_version)s.tar.gzip' % env)
215+
216+
@task
217+
def deploy(version):
218+
"""
219+
This function does all the work required to ship code to the
220+
server being deployed to.
221+
222+
version: the version applied to the tag of the release
223+
"""
224+
225+
env.project_version = version
226+
227+
git_version(version)
228+
package_files()
229+
ship_to_host()
230+
create_backup()
231+
unpackage_files()
232+
create_venv()
233+
link_to_live()
234+
refresh_server()
235+
#TODO: Run tests, run django validation
236+
clean_up()
237+
238+
"""
239+
The following functions are admin level functions which will alter the server.
240+
They will need to be run with the admin=True flag when setting up the env
241+
"""
242+
243+
@task
244+
def setup_webspace():
245+
"""
246+
make www and www.backup directory as admin or root user
247+
248+
Note: Admin must be true in the environment
249+
"""
250+
sudo("mkdir -p %(live_project_full_path)s" % env)
251+
sudo("mkdir -p %(backup_project_full_path)s/archive" % env)
252+
253+
#Change the permissions to match the correct user and group
254+
sudo("chown -R %s.%s /var/www.backup" % (get_config('deploy_user'), get_config('deploy_user_group')))
255+
sudo("chmod -R 755 /var/www.backup")
256+
257+
sudo("chown -R %s.%s /var/www" % (get_config('deploy_user'), get_config('deploy_user_group')))
258+
sudo("chmod -R 755 /var/www/")
259+
260+
@task
261+
def setup_server():
262+
"""
263+
This function creates the deploy user and sets up the directories being used for the project
264+
Note: Admin must be true in the environment
265+
"""
266+
create_deploy_user_with_ssh()
267+
setup_webspace()
268+
269+
@task
270+
def create_deploy_user_with_ssh():
271+
"""
272+
This function will ssh in with the assigned admin user account,
273+
prompt for password, and create the deployment user. It will place
274+
this user in the group assigned.
275+
276+
Note: If ssh is locked to specific users, make sure to add this
277+
new user to that list
278+
Note: Admin must be true in the environment
279+
"""
280+
random_password = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(32))
281+
deploy_user = get_config('deploy_user')
282+
deploy_user_group = get_config('deploy_user_group')
283+
sudo('useradd -m -b /home -u 800 -g %s -s /bin/bash -c "deployment user" %s -p %s' % (deploy_user_group, deploy_user, random_password))
284+
285+
#create SSH directory
286+
sudo('mkdir -p /home/%s/.ssh/keys' % deploy_user)
287+
update_ssh_permissions()
288+
289+
#TODO: automatically add ssh key or prompt
290+
291+
def update_ssh_permissions():
292+
"""
293+
This function makes the deploy user owner of these documents and locks down
294+
other permissions
295+
"""
296+
deploy_user = get_config('deploy_user')
297+
298+
sudo('chmod 700 /home/%s/.ssh' % deploy_user)
299+
sudo('chmod 644 /home/%s/.ssh/authorized_keys' % deploy_user)
300+
sudo('chmod -R 700 /home/%s/.ssh/keys' % deploy_user)
301+
sudo('chown -R %s.%s /home/%s' % (deploy_user, get_config('deploy_user_group'), deploy_user))
302+
303+
@task(alias='ssh_string')
304+
def add_new_ssh_key_as_string(ssh_public_key_string, name):
305+
"""
306+
This function will add the passed ssh key string to the deploy user to enable
307+
passwordless login
308+
Note: Admin must be true in the environment
309+
TODO: validate string is valid ssh key
310+
311+
ssh_public_key_string: the actual public key string
312+
name: the name of the user this key is tied to
313+
"""
314+
315+
ssh_key = ssh_public_key_string
316+
copy_ssh_key_to_host(ssh_key,name)
317+
rebuild_authorized_keys()
318+
update_ssh_permissions()
319+
320+
@task(alias='ssh_file')
321+
def add_new_ssh_key_as_file(ssh_public_key_path, name):
322+
"""
323+
This function will copy the ssh key from a local file to the deploy user to enable
324+
passwordless login
325+
Note: Admin must be true in the environment
326+
327+
ssh_public_key_string: the actual public key full file path
328+
name: the name of the user this key is tied to
329+
"""
330+
ssh_key = open(ssh_public_key_path, 'r').read()
331+
copy_ssh_key_to_host(ssh_key, name)
332+
rebuild_authorized_keys()
333+
update_ssh_permissions()
334+
335+
def copy_ssh_key_to_host(ssh_key, name):
336+
"""
337+
Creates a new pub file with the name provided and
338+
ssh key inside. Ships that pub file to the deploy
339+
users ssh directory
340+
341+
ssh_key: String of ssh_key to create a new pub file from
342+
name: the name of the user this key is tied to
343+
"""
344+
pub_file = open('%s.pub' % name, 'w')
345+
pub_file.write(ssh_key)
346+
pub_file.close()
347+
put('%s.pub' % name, '/home/%s/.ssh/keys/' % get_config('deploy_user'), use_sudo=True)
348+
349+
def rebuild_authorized_keys():
350+
"""
351+
Take all the current pub files and recreate authorized_keys from them.
352+
If any of the pub files are removed, they get removed from the authorized keys
353+
and can no longer ssh in.
354+
"""
355+
sudo('sudo cat `sudo find /home/%s/.ssh/keys/ -type f` > tmpfile' % get_config('deploy_user'))
356+
sudo('cp tmpfile /home/%s/.ssh/authorized_keys' % get_config('deploy_user'))
357+
sudo('rm tmpfile')

0 commit comments

Comments
 (0)