diff --git a/.wiki-images/download-certificate.png b/.wiki-images/download-certificate.png deleted file mode 100644 index e80daba3ffd..00000000000 Binary files a/.wiki-images/download-certificate.png and /dev/null differ diff --git a/.wiki-images/set-end-date-studio.png b/.wiki-images/set-end-date-studio.png deleted file mode 100644 index 87ed4c7ac8d..00000000000 Binary files a/.wiki-images/set-end-date-studio.png and /dev/null differ diff --git a/.wiki-images/wrapping-up-dashboard.png b/.wiki-images/wrapping-up-dashboard.png deleted file mode 100644 index 01772e8c160..00000000000 Binary files a/.wiki-images/wrapping-up-dashboard.png and /dev/null differ diff --git a/CHANGELOG.md b/CHANGELOG.md index e3ff6a43fa0..fbe87057127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +- Role: edxapp + - A new var was added to make it easy ot invalidate the default + memcache store to make it easier to invalidate sessions. Updating + the edxapp env.json files will result in all users getting logged + out. This is a one time penalty as long as the value of `EDXAPP_DEFAULT_CACHE_VERSION` + is not explicitly changed. + +- Role: nginx + - New html templates for server errors added. + Defaults for a ratelimiting static page and server error static page. + CMS/LMS are set to use them by default, wording can be changed in the + Nginx default vars. + +- Role: edxapp + - We now have an all caps variable override for celery workers - Role: common - We now remove the default syslog.d conf file (50-default.conf) this will break people who have hand edited that file. @@ -5,6 +20,10 @@ - Role: edxapp - Updated the module store settings to match the new settings format. +- Update, possible breaking change: the edxapp role vars edxapp_lms_env and edxapp_cms_env have + been changed to EDXAPP_LMS_ENV and EDXAPP_CMS_ENV to indicate, via our convention, + that overridding them is expected. The default values remain the same. + - Role: analytics-api - Added a new role for the analytics-api Django app. Currently a private repo @@ -29,3 +48,7 @@ - Role: Mongo - Fixed case of variable used in if block that breaks cluster configuration by changing mongo_clustered to MONGO_CLUSTERED. + +- Role: Edxapp + - Added EDXAPP_LMS_AUTH_EXTRA and EDXAPP_CMS_AUTH_EXTRA for passing unique AUTH_EXTRA configurations to the LMS and CMS. + Both variables default to EDXAPP_AUTH_EXTRA for backward compatibility diff --git a/playbooks/callback_plugins/hipchat_plugin.py b/playbooks/callback_plugins/hipchat_plugin.py index 16a2f94a207..63c4716c75b 100644 --- a/playbooks/callback_plugins/hipchat_plugin.py +++ b/playbooks/callback_plugins/hipchat_plugin.py @@ -1,9 +1,14 @@ import os -import prettytable -import hipchat import time -import random from ansible import utils +try: + import prettytable +except ImportError: + prettytable = None +try: + import hipchat +except ImportError: + hipchat = None class CallbackModule(object): @@ -24,30 +29,40 @@ class CallbackModule(object): """ def __init__(self): - - if 'HIPCHAT_TOKEN' in os.environ: - self.start_time = time.time() - self.task_report = [] - self.last_task = None - self.last_task_changed = False - self.last_task_count = 0 - self.last_task_delta = 0 - self.last_task_start = time.time() - self.condensed_task_report = (os.getenv('HIPCHAT_CONDENSED', True) == True) - self.room = os.getenv('HIPCHAT_ROOM', 'ansible') - self.from_name = os.getenv('HIPCHAT_FROM', 'ansible') - self.allow_notify = (os.getenv('HIPCHAT_NOTIFY') != 'false') - try: - self.hipchat_conn = hipchat.HipChat(token=os.getenv('HIPCHAT_TOKEN')) - except Exception as e: - utils.warning("Unable to connect to hipchat: {}".format(e)) - self.hipchat_msg_prefix = os.getenv('HIPCHAT_MSG_PREFIX', '') - self.hipchat_msg_color = os.getenv('HIPCHAT_MSG_COLOR', '') - self.printed_playbook = False - self.playbook_name = None - self.enabled = True - else: - self.enabled = False + self.enabled = "HIPCHAT_TOKEN" in os.environ + if not self.enabled: + return + + # make sure we got our imports + if not hipchat: + raise ImportError( + "The hipchat plugin requires the hipchat Python module, " + "which is not installed or was not found." + ) + if not prettytable: + raise ImportError( + "The hipchat plugin requires the prettytable Python module, " + "which is not installed or was not found." + ) + self.start_time = time.time() + self.task_report = [] + self.last_task = None + self.last_task_changed = False + self.last_task_count = 0 + self.last_task_delta = 0 + self.last_task_start = time.time() + self.condensed_task_report = (os.getenv('HIPCHAT_CONDENSED', True) == True) + self.room = os.getenv('HIPCHAT_ROOM', 'ansible') + self.from_name = os.getenv('HIPCHAT_FROM', 'ansible') + self.allow_notify = (os.getenv('HIPCHAT_NOTIFY') != 'false') + try: + self.hipchat_conn = hipchat.HipChat(token=os.getenv('HIPCHAT_TOKEN')) + except Exception as e: + utils.warning("Unable to connect to hipchat: {}".format(e)) + self.hipchat_msg_prefix = os.getenv('HIPCHAT_MSG_PREFIX', '') + self.hipchat_msg_color = os.getenv('HIPCHAT_MSG_COLOR', '') + self.printed_playbook = False + self.playbook_name = None def _send_hipchat(self, message, room=None, from_name=None, color=None, message_format='text'): @@ -221,7 +236,7 @@ def playbook_on_stats(self, stats): summary_output = "{}: {} - ".format(self.hipchat_msg_prefix, host) for summary_item in ['ok', 'changed', 'unreachable', 'failures']: if stats[summary_item] != 0: - summary_output += "{} - {} ".format(summary_item, stats[summary_item]) + summary_output += "{} - {} ".format(summary_item, stats[summary_item]) summary_all_host_output.append(summary_output) self._send_hipchat("
".join(summary_all_host_output), message_format='html') msg = "{description}: Finished Ansible run for {play} in {min:02} minutes, {sec:02} seconds

".format( diff --git a/playbooks/callback_plugins/sqs.py b/playbooks/callback_plugins/sqs.py index 70124eb7140..39ad206b7d4 100644 --- a/playbooks/callback_plugins/sqs.py +++ b/playbooks/callback_plugins/sqs.py @@ -22,11 +22,12 @@ import json import socket try: + import boto +except ImportError: + boto = None +else: import boto.sqs from boto.exception import NoAuthHandlerFound -except ImportError: - print "Boto is required for the sqs_notify callback plugin" - raise class CallbackModule(object): @@ -47,36 +48,42 @@ class CallbackModule(object): - START events """ def __init__(self): + self.enable_sqs = 'ANSIBLE_ENABLE_SQS' in os.environ + if not self.enable_sqs: + return + + # make sure we got our imports + if not boto: + raise ImportError( + "The sqs callback module requires the boto Python module, " + "which is not installed or was not found." + ) self.start_time = time.time() - if 'ANSIBLE_ENABLE_SQS' in os.environ: - self.enable_sqs = True - if not 'SQS_REGION' in os.environ: - print 'ANSIBLE_ENABLE_SQS enabled but SQS_REGION ' \ - 'not defined in environment' - sys.exit(1) - self.region = os.environ['SQS_REGION'] - try: - self.sqs = boto.sqs.connect_to_region(self.region) - except NoAuthHandlerFound: - print 'ANSIBLE_ENABLE_SQS enabled but cannot connect ' \ - 'to AWS due invalid credentials' - sys.exit(1) - if not 'SQS_NAME' in os.environ: - print 'ANSIBLE_ENABLE_SQS enabled but SQS_NAME not ' \ - 'defined in environment' - sys.exit(1) - self.name = os.environ['SQS_NAME'] - self.queue = self.sqs.create_queue(self.name) - if 'SQS_MSG_PREFIX' in os.environ: - self.prefix = os.environ['SQS_MSG_PREFIX'] - else: - self.prefix = '' - - self.last_seen_ts = {} + if not 'SQS_REGION' in os.environ: + print 'ANSIBLE_ENABLE_SQS enabled but SQS_REGION ' \ + 'not defined in environment' + sys.exit(1) + self.region = os.environ['SQS_REGION'] + try: + self.sqs = boto.sqs.connect_to_region(self.region) + except NoAuthHandlerFound: + print 'ANSIBLE_ENABLE_SQS enabled but cannot connect ' \ + 'to AWS due invalid credentials' + sys.exit(1) + if not 'SQS_NAME' in os.environ: + print 'ANSIBLE_ENABLE_SQS enabled but SQS_NAME not ' \ + 'defined in environment' + sys.exit(1) + self.name = os.environ['SQS_NAME'] + self.queue = self.sqs.create_queue(self.name) + if 'SQS_MSG_PREFIX' in os.environ: + self.prefix = os.environ['SQS_MSG_PREFIX'] else: - self.enable_sqs = False + self.prefix = '' + + self.last_seen_ts = {} def runner_on_failed(self, host, res, ignore_errors=False): if self.enable_sqs: diff --git a/playbooks/edx-east/aide.yml b/playbooks/edx-east/aide.yml index 96dfb35cc08..f3886ed2659 100644 --- a/playbooks/edx-east/aide.yml +++ b/playbooks/edx-east/aide.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - aide - role: datadog diff --git a/playbooks/edx-east/alton.yml b/playbooks/edx-east/alton.yml index 4bc81a3744e..2236c6e1987 100644 --- a/playbooks/edx-east/alton.yml +++ b/playbooks/edx-east/alton.yml @@ -3,5 +3,8 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - alton diff --git a/playbooks/edx-east/antivirus.yml b/playbooks/edx-east/antivirus.yml new file mode 100644 index 00000000000..2e8206eaac2 --- /dev/null +++ b/playbooks/edx-east/antivirus.yml @@ -0,0 +1,12 @@ +- name: Deploy Antivirus Scanner + hosts: all + sudo: True + gather_facts: True + roles: + - antivirus + - role: datadog + when: COMMON_ENABLE_DATADOG + - role: splunkforwarder + when: COMMON_ENABLE_SPLUNKFORWARDER + - role: newrelic + when: COMMON_ENABLE_NEWRELIC diff --git a/playbooks/edx-east/aws.yml b/playbooks/edx-east/aws.yml index 48bd8f833b1..ea77ca006e8 100644 --- a/playbooks/edx-east/aws.yml +++ b/playbooks/edx-east/aws.yml @@ -2,5 +2,8 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - aws diff --git a/playbooks/edx-east/bastion.yml b/playbooks/edx-east/bastion.yml index 9933dce7da2..9392434a74e 100644 --- a/playbooks/edx-east/bastion.yml +++ b/playbooks/edx-east/bastion.yml @@ -2,5 +2,8 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - bastion diff --git a/playbooks/edx-east/certs.yml b/playbooks/edx-east/certs.yml index 8f9d9b69a52..3c6d9553fec 100644 --- a/playbooks/edx-east/certs.yml +++ b/playbooks/edx-east/certs.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - aws - certs diff --git a/playbooks/edx-east/cluster_rabbitmq.yml b/playbooks/edx-east/cluster_rabbitmq.yml index adaeff78108..e95beb94ad1 100644 --- a/playbooks/edx-east/cluster_rabbitmq.yml +++ b/playbooks/edx-east/cluster_rabbitmq.yml @@ -1,5 +1,5 @@ -# ansible-playbook -i ec2.py commoncluster.yml --limit tag_Name_stage-edx-commoncluster -e@/path/to/vars/env-deployment.yml -T 30 --list-hosts +# ansible-playbook -i ec2.py cluster_rabbitmq.yml --limit tag_Name_stage-edx-commoncluster -e@/path/to/vars/env-deployment.yml -T 30 --list-hosts - hosts: all sudo: True @@ -28,14 +28,9 @@ tasks: - debug: msg="{{ ansible_ec2_local_ipv4 }}" with_items: list.results - - shell: echo "rabbit@ip-{{ item|replace('.', '-') }}" - when: item != ansible_ec2_local_ipv4 - with_items: hostvars.keys() - register: list - command: rabbitmqctl stop_app - - command: rabbitmqctl join_cluster {{ item.stdout }} - when: item.stdout is defined - with_items: list.results + - command: rabbitmqctl join_cluster rabbit@ip-{{ hostvars.keys()[0]|replace('.', '-') }} + when: hostvars.keys()[0] != ansible_ec2_local_ipv4 - command: rabbitmqctl start_app post_tasks: - debug: var="{{ ansible_ec2_instance_id }}" diff --git a/playbooks/edx-east/common.yml b/playbooks/edx-east/common.yml index 35051f49927..549ecfeb325 100644 --- a/playbooks/edx-east/common.yml +++ b/playbooks/edx-east/common.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - common - role: datadog diff --git a/playbooks/edx-east/connect_sandbox.yml b/playbooks/edx-east/connect_sandbox.yml index 0a6c33d180b..1547ffacc8b 100644 --- a/playbooks/edx-east/connect_sandbox.yml +++ b/playbooks/edx-east/connect_sandbox.yml @@ -31,7 +31,7 @@ - "EDXAPP_MONGO_HOSTS: {{ EDXAPP_MONGO_HOSTS }}" - "EDXAPP_MONGO_DB_NAME: {{ EDXAPP_MONGO_DB_NAME }}" - "EDXAPP_MONGO_USER: {{ EDXAPP_MONGO_USER }}" - - "EDXAPP_MONGO_PASS: {{ EDXAPP_MONGO_PASS }}" + - "EDXAPP_MONGO_PASSWORD: {{ EDXAPP_MONGO_PASSWORD }}" tags: update_edxapp_mysql_host - name: call update on edx-platform diff --git a/playbooks/edx-east/create_dbs.yml b/playbooks/edx-east/create_dbs.yml index fcb4dda4b61..e82760374ce 100644 --- a/playbooks/edx-east/create_dbs.yml +++ b/playbooks/edx-east/create_dbs.yml @@ -40,6 +40,10 @@ sudo: yes with_items: - python-mysqldb + # When this is run on jenkins the package will already + # exist and can't run as the jenkins user because it + # does not have sudo privs. + when: ansible_ssh_user != 'jenkins' - name: create mysql databases for the edX stack mysql_db: > db={{ item[0] }}{{ item[1].db_name }} diff --git a/playbooks/edx-east/demo.yml b/playbooks/edx-east/demo.yml index 76e77aa17f1..fff266f0d91 100644 --- a/playbooks/edx-east/demo.yml +++ b/playbooks/edx-east/demo.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - demo - role: datadog diff --git a/playbooks/edx-east/devpi.yml b/playbooks/edx-east/devpi.yml index 205c65d404c..a57a741ffd6 100644 --- a/playbooks/edx-east/devpi.yml +++ b/playbooks/edx-east/devpi.yml @@ -2,5 +2,8 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - devpi diff --git a/playbooks/edx-east/discern.yml b/playbooks/edx-east/discern.yml index 78e7e4dbf15..710717323e5 100644 --- a/playbooks/edx-east/discern.yml +++ b/playbooks/edx-east/discern.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - aws - role: nginx diff --git a/playbooks/edx-east/edx_ansible.yml b/playbooks/edx-east/edx_ansible.yml index 7ae30643abc..ca890dbd7fe 100644 --- a/playbooks/edx-east/edx_ansible.yml +++ b/playbooks/edx-east/edx_ansible.yml @@ -1,6 +1,9 @@ - name: Deploy the edx_ansible role hosts: all sudo: True - gather_facts: False + gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - edx_ansible diff --git a/playbooks/edx-east/edx_continuous_integration.yml b/playbooks/edx-east/edx_continuous_integration.yml index 5b237bfc29b..8752023d526 100644 --- a/playbooks/edx-east/edx_continuous_integration.yml +++ b/playbooks/edx-east/edx_continuous_integration.yml @@ -12,7 +12,6 @@ nginx_sites: - cms - lms - - ora - xqueue - xserver - certs @@ -31,7 +30,6 @@ - forum - { role: "xqueue", update_users: True } - xserver - - ora - certs - edx_ansible - analytics-api diff --git a/playbooks/edx-east/edx_provision.yml b/playbooks/edx-east/edx_provision.yml index a240de0352d..7c1e2bd98b9 100644 --- a/playbooks/edx-east/edx_provision.yml +++ b/playbooks/edx-east/edx_provision.yml @@ -1,7 +1,7 @@ - name: Create ec2 instance hosts: localhost connection: local - gather_facts: False + gather_facts: True vars: keypair: continuous-integration instance_type: t2.medium diff --git a/playbooks/edx-east/edxapp.yml b/playbooks/edx-east/edxapp.yml index f97486f57f7..64df0a84415 100644 --- a/playbooks/edx-east/edxapp.yml +++ b/playbooks/edx-east/edxapp.yml @@ -3,6 +3,8 @@ sudo: True gather_facts: True vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - aws - role: nginx @@ -20,6 +22,10 @@ - role: splunkforwarder when: COMMON_ENABLE_SPLUNKFORWARDER - role: newrelic + NEWRELIC_LOGWATCH: + - logwatch-503.j2 + - logwatch-cms-errors.j2 + - logwatch-lms-errors.j2 when: COMMON_ENABLE_NEWRELIC - role: minos when: COMMON_ENABLE_MINOS diff --git a/playbooks/edx-east/edxapp_migrate.yml b/playbooks/edx-east/edxapp_migrate.yml index 1a09770b08a..a5bb63819d8 100644 --- a/playbooks/edx-east/edxapp_migrate.yml +++ b/playbooks/edx-east/edxapp_migrate.yml @@ -4,15 +4,27 @@ gather_facts: False vars: db_dry_run: "--db-dry-run" + syncdb: false tasks: # Syncdb with migrate when the migrate user is overridden in extra vars - - name: syncdb and migrate + - name: migrate shell: > chdir={{ edxapp_code_dir }} - python manage.py {{ item }} syncdb --migrate --noinput --settings=aws_migrate {{ db_dry_run }} + python manage.py {{ item }} migrate --noinput {{ db_dry_run }} --settings=aws_migrate environment: DB_MIGRATION_USER: "{{ COMMON_MYSQL_MIGRATE_USER }}" DB_MIGRATION_PASS: "{{ COMMON_MYSQL_MIGRATE_PASS }}" with_items: - lms - cms + - name: syncdb + shell: > + chdir={{ edxapp_code_dir }} + python manage.py {{ item }} syncdb --noinput --settings=aws_migrate + environment: + DB_MIGRATION_USER: "{{ COMMON_MYSQL_MIGRATE_USER }}" + DB_MIGRATION_PASS: "{{ COMMON_MYSQL_MIGRATE_PASS }}" + when: syncdb + with_items: + - lms + - cms diff --git a/playbooks/edx-east/flower.yml b/playbooks/edx-east/flower.yml index 5da7b27a4f8..c78b69fcbdc 100644 --- a/playbooks/edx-east/flower.yml +++ b/playbooks/edx-east/flower.yml @@ -2,5 +2,8 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - flower diff --git a/playbooks/edx-east/forum.yml b/playbooks/edx-east/forum.yml index 5b7d4dd2693..6eb534cc0e3 100644 --- a/playbooks/edx-east/forum.yml +++ b/playbooks/edx-east/forum.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - aws - role: nginx diff --git a/playbooks/edx-east/insights.yml b/playbooks/edx-east/insights.yml new file mode 100644 index 00000000000..8824a8c5c25 --- /dev/null +++ b/playbooks/edx-east/insights.yml @@ -0,0 +1,20 @@ +- name: Deploy Insights + hosts: all + sudo: True + gather_facts: True + vars: + ENABLE_DATADOG: False + ENABLE_SPLUNKFORWARDER: False + ENABLE_NEWRELIC: True + roles: + - role: nginx + nginx_sites: + - insights + - aws + - insights + - role: datadog + when: COMMON_ENABLE_DATADOG + - role: splunkforwarder + when: COMMON_ENABLE_SPLUNKFORWARDER + - role: newrelic + when: COMMON_ENABLE_NEWRELIC diff --git a/playbooks/edx-east/jenkins_admin.yml b/playbooks/edx-east/jenkins_admin.yml index 763f842b85c..ccd177b03ff 100644 --- a/playbooks/edx-east/jenkins_admin.yml +++ b/playbooks/edx-east/jenkins_admin.yml @@ -3,5 +3,8 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - jenkins_admin diff --git a/playbooks/edx-east/jenkins_worker.yml b/playbooks/edx-east/jenkins_worker.yml index f814baf13ed..2d1a4da23ca 100644 --- a/playbooks/edx-east/jenkins_worker.yml +++ b/playbooks/edx-east/jenkins_worker.yml @@ -8,6 +8,8 @@ gather_facts: True vars: mongo_enable_journal: False + serial_count: 1 + serial: "{{ serial_count }}" vars_files: - roles/edxapp/defaults/main.yml - roles/ora/defaults/main.yml @@ -18,4 +20,5 @@ - edxlocal - mongo - browsers + - browsermob-proxy - jenkins_worker diff --git a/playbooks/edx-east/legacy_ora.yml b/playbooks/edx-east/legacy_ora.yml index 295ebf32974..5a45ff3b363 100644 --- a/playbooks/edx-east/legacy_ora.yml +++ b/playbooks/edx-east/legacy_ora.yml @@ -3,7 +3,9 @@ hosts: all sudo: True gather_facts: True - serial: 1 + vars: + serial_count: 1 + serial: "{{ serial_count }}" vars_files: - "{{secure_dir}}/vars/{{COMMON_ENVIRONMENT}}/legacy-ora.yml" roles: diff --git a/playbooks/edx-east/lifecycle_inventory.py b/playbooks/edx-east/lifecycle_inventory.py index fa4a39dcf5e..b3d4c9f8694 100755 --- a/playbooks/edx-east/lifecycle_inventory.py +++ b/playbooks/edx-east/lifecycle_inventory.py @@ -41,6 +41,18 @@ def __init__(self, profile): parser = argparse.ArgumentParser() self.profile = profile + def get_e_d_from_tags(self, group): + + environment = "default_environment" + deployment = "default_deployment" + + for r in group.tags: + if r.key == "environment": + environment = r.value + elif r.key == "deployment": + deployment = r.value + return environment,deployment + def get_instance_dict(self): ec2 = boto.connect_ec2(profile_name=self.profile) reservations = ec2.get_all_instances() @@ -64,10 +76,12 @@ def run(self): for instance in group.instances: private_ip_address = instances[instance.instance_id].private_ip_address - - inventory[group.name].append(private_ip_address) - inventory[group.name + "_" + instance.lifecycle_state].append(private_ip_address) - inventory[instance.lifecycle_state.replace(":","_")].append(private_ip_address) + if private_ip_address: + environment,deployment = self.get_e_d_from_tags(group) + inventory[environment + "_" + deployment + "_" + instance.lifecycle_state.replace(":","_")].append(private_ip_address) + inventory[group.name].append(private_ip_address) + inventory[group.name + "_" + instance.lifecycle_state.replace(":","_")].append(private_ip_address) + inventory[instance.lifecycle_state.replace(":","_")].append(private_ip_address) print json.dumps(inventory, sort_keys=True, indent=2) @@ -77,8 +91,8 @@ def run(self): parser.add_argument('-p', '--profile', help='The aws profile to use when connecting.') parser.add_argument('-l', '--list', help='Ansible passes this, we ignore it.', action='store_true', default=True) args = parser.parse_args() - + LifecycleInventory(args.profile).run() - + diff --git a/playbooks/edx-east/minos.yml b/playbooks/edx-east/minos.yml index 8339970da61..e5865d9686b 100644 --- a/playbooks/edx-east/minos.yml +++ b/playbooks/edx-east/minos.yml @@ -3,6 +3,9 @@ sudo: True gather_facts: True vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - common - - minos + - aws + - minos diff --git a/playbooks/edx-east/mongo.yml b/playbooks/edx-east/mongo.yml index f6afd481759..fe71cedc2e9 100644 --- a/playbooks/edx-east/mongo.yml +++ b/playbooks/edx-east/mongo.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - mongo - mongo_mms diff --git a/playbooks/edx-east/notifier.yml b/playbooks/edx-east/notifier.yml new file mode 100644 index 00000000000..be18bbf11a5 --- /dev/null +++ b/playbooks/edx-east/notifier.yml @@ -0,0 +1,11 @@ +- name: Configure notifier instance + hosts: all + sudo: True + gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" + roles: + - aws + - notifier + diff --git a/playbooks/edx-east/ora.yml b/playbooks/edx-east/ora.yml index 3e74011fde7..3e3b392afc6 100644 --- a/playbooks/edx-east/ora.yml +++ b/playbooks/edx-east/ora.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - role: nginx nginx_sites: diff --git a/playbooks/edx-east/retire_host.yml b/playbooks/edx-east/retire_host.yml index 99c2abca60f..55b4c779d8f 100644 --- a/playbooks/edx-east/retire_host.yml +++ b/playbooks/edx-east/retire_host.yml @@ -1,12 +1,15 @@ -# ansible-playbook -i ./lifecycle_inventory.py ./retire_host.yml -# -e@/vars/env.yml --limit Terminating_Wait +# ansible-playbook -i ./lifecycle_inventory.py ./retire_host.yml +# -e@/vars/env.yml --limit Terminating_Wait -e TARGET="Terminating_Wait" +# +# Note that the target now must be specified as an argument +# # # This is separate because it's use of handlers # leads to various race conditions. # - name: Stop all services - hosts: Terminating_Wait + hosts: "{{TARGET}}" sudo: True gather_facts: False vars: @@ -15,41 +18,30 @@ - stop_all_edx_services - name: Server retirement workflow - hosts: Terminating_Wait + hosts: "{{TARGET}}" sudo: True gather_facts: False tasks: - - name: Force a log rotation - command: /usr/sbin/logrotate -f /etc/logrotate.d/{{ item }} - with_items: - - "apport" - - "apt" - - "aptitude" - - "dpkg" - - "hourly" - - "landscape-client" - - "newrelic-sysmond" - - "nginx" - - "nginx-access" - - "nginx-error" - - "ppp" - - "rsyslog" - - "ufw" - - "unattended-upgrades" - - "upstart" - - name: Force a log rotation + - name: Terminate existing s3 log sync + command: /usr/bin/pkill send-logs-to-s3 || true + - name: "Ensure send-logs-to-s3 script is in the logrotate file" + shell: grep send-logs-to-s3 /etc/logrotate.d/hourly/tracking.log + # We only force a rotation of edx logs. + # Forced rotation of system logfiles will only + # work if there hasn't already been a previous rotation + # The logrotate will also call send-logs-to-s3 but hasn't + # been updated for all servers yet. + - name: Force a log rotation which will call the log sync command: /usr/sbin/logrotate -f /etc/logrotate.d/hourly/{{ item }} with_items: - "tracking.log" - "edx-services" - - name: Terminate existing s3 log sync - command: /usr/bin/pkill send-logs-to-s3 || true - - name: Send logs to s3 - command: /edx/bin/send-logs-to-s3 - - + # This catches the case where tracking.log is 0b + - name: Sync again + command: /edx/bin/send-logs-to-s3 -d "{{ COMMON_LOG_DIR }}/tracking/*" -b "{{ COMMON_AWS_SYNC_BUCKET }}/logs/tracking" + - name: Run minos verification - hosts: Terminating_Wait + hosts: "{{TARGET}}" sudo: True gather_facts: False tasks: diff --git a/playbooks/edx-east/snort.yml b/playbooks/edx-east/snort.yml index 70096bd6dad..b962878b0a2 100644 --- a/playbooks/edx-east/snort.yml +++ b/playbooks/edx-east/snort.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - snort - role: datadog diff --git a/playbooks/edx-east/stop_all_edx_services.yml b/playbooks/edx-east/stop_all_edx_services.yml index b7228d7693c..9e570e009e0 100644 --- a/playbooks/edx-east/stop_all_edx_services.yml +++ b/playbooks/edx-east/stop_all_edx_services.yml @@ -2,5 +2,8 @@ hosts: all sudo: True gather_facts: False + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - stop_all_edx_services diff --git a/playbooks/edx-east/testcourses.yml b/playbooks/edx-east/testcourses.yml index 6c918926523..a1353dea773 100644 --- a/playbooks/edx-east/testcourses.yml +++ b/playbooks/edx-east/testcourses.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - testcourses - role: datadog diff --git a/playbooks/edx-east/xqueue.yml b/playbooks/edx-east/xqueue.yml index cb63e8653fc..d48915b2a79 100644 --- a/playbooks/edx-east/xqueue.yml +++ b/playbooks/edx-east/xqueue.yml @@ -35,6 +35,8 @@ - role: splunkforwarder when: COMMON_ENABLE_SPLUNKFORWARDER - role: newrelic + NEWRELIC_LOGWATCH: + - logwatch-xqueue-errors.j2 when: COMMON_ENABLE_NEWRELIC post_tasks: - debug: var="{{ ansible_ec2_instance_id }}" diff --git a/playbooks/edx-east/xqwatcher.yml b/playbooks/edx-east/xqwatcher.yml index 82735f3a0da..6d1e380181f 100644 --- a/playbooks/edx-east/xqwatcher.yml +++ b/playbooks/edx-east/xqwatcher.yml @@ -8,6 +8,8 @@ ENABLE_DATADOG: False ENABLE_SPLUNKFORWARDER: False ENABLE_NEWRELIC: False + serial_count: 1 + serial: "{{ serial_count }}" roles: - aws - xqwatcher @@ -16,4 +18,4 @@ - role: splunkforwarder when: COMMON_ENABLE_SPLUNKFORWARDER - role: newrelic - when: COMMON_ENABLE_NEWRELIC \ No newline at end of file + when: COMMON_ENABLE_NEWRELIC diff --git a/playbooks/edx-east/xserver.yml b/playbooks/edx-east/xserver.yml index 362c1c9f0ee..8f579fdb574 100644 --- a/playbooks/edx-east/xserver.yml +++ b/playbooks/edx-east/xserver.yml @@ -2,6 +2,9 @@ hosts: all sudo: True gather_facts: True + vars: + serial_count: 1 + serial: "{{ serial_count }}" roles: - aws - role: nginx diff --git a/playbooks/edx-west/cloudformation.yml b/playbooks/edx-west/cloudformation.yml index 0fe9f50e482..fcee0c7caef 100644 --- a/playbooks/edx-west/cloudformation.yml +++ b/playbooks/edx-west/cloudformation.yml @@ -31,14 +31,14 @@ tasks: - name: edX configuration cloudformation: > - stack_name="$name" state=present - region=$region disable_rollback=false + stack_name="{{ name }}" state=present + region="{{ region }}" disable_rollback=false template=../cloudformation_templates/edx-server-multi-instance.json args: template_parameters: - KeyName: $key + KeyName: "{{key}}" InstanceType: m1.small - GroupTag: $group + GroupTag: "{{group}}" register: stack - name: show stack outputs - debug: msg="My stack outputs are ${stack.stack_outputs}" + debug: msg="My stack outputs are {{stack.stack_outputs}}" diff --git a/playbooks/edx_sandbox.yml b/playbooks/edx_sandbox.yml index bb496a9432c..036c9a8f367 100644 --- a/playbooks/edx_sandbox.yml +++ b/playbooks/edx_sandbox.yml @@ -18,6 +18,7 @@ # These should stay false for the public AMI COMMON_ENABLE_DATADOG: False COMMON_ENABLE_SPLUNKFORWARDER: False + ENABLE_LEGACY_ORA: !!null roles: - role: nginx nginx_sites: @@ -38,7 +39,8 @@ - elasticsearch - forum - { role: "xqueue", update_users: True } - - ora + - role: ora + when: ENABLE_LEGACY_ORA - certs - edx_ansible - role: datadog diff --git a/playbooks/roles/alton/tasks/deploy.yml b/playbooks/roles/alton/tasks/deploy.yml index cbe06199f22..1714b040449 100644 --- a/playbooks/roles/alton/tasks/deploy.yml +++ b/playbooks/roles/alton/tasks/deploy.yml @@ -1,3 +1,10 @@ +- name: setup the alton env + template: > + src="alton_env.j2" dest="{{ alton_app_dir }}/alton_env" + owner="{{ alton_user }}" group="{{ common_web_user }}" + mode=0644 + notify: restart alton + - name: configure the boto profiles for alton template: > src="boto.j2" diff --git a/playbooks/roles/alton/tasks/main.yml b/playbooks/roles/alton/tasks/main.yml index 9715860e7f8..9b71a12b47b 100644 --- a/playbooks/roles/alton/tasks/main.yml +++ b/playbooks/roles/alton/tasks/main.yml @@ -34,11 +34,4 @@ - "{{ alton_app_dir }}" - "{{ alton_venvs_dir }}" -- name: setup the alton env - template: > - src="alton_env.j2" dest="{{ alton_app_dir }}/alton_env" - owner="{{ alton_user }}" group="{{ common_web_user }}" - mode=0644 - notify: restart alton - - include: deploy.yml tags=deploy diff --git a/playbooks/roles/alton/templates/alton_env.j2 b/playbooks/roles/alton/templates/alton_env.j2 index 40feb800236..ba2a561f12d 100644 --- a/playbooks/roles/alton/templates/alton_env.j2 +++ b/playbooks/roles/alton/templates/alton_env.j2 @@ -6,3 +6,5 @@ export {{ name }}="{{ value }}" {% endif %} {%- endfor %} +export WILL_BOTO_PROFILES="{{ ALTON_AWS_CREDENTIALS|join(';') }}" + diff --git a/playbooks/roles/alton/templates/boto.j2 b/playbooks/roles/alton/templates/boto.j2 index 8a6ccc181b4..28f21ce1b07 100644 --- a/playbooks/roles/alton/templates/boto.j2 +++ b/playbooks/roles/alton/templates/boto.j2 @@ -1,5 +1,5 @@ {% for deployment, creds in ALTON_AWS_CREDENTIALS.iteritems() %} -[profile {{deployment}}] +[profile {{ deployment }}] aws_access_key_id = {{ creds.access_id }} aws_secret_access_key = {{ creds.secret_key }} diff --git a/playbooks/roles/analytics-api/defaults/main.yml b/playbooks/roles/analytics-api/defaults/main.yml index f31157c54f6..b2cfff1fa6b 100644 --- a/playbooks/roles/analytics-api/defaults/main.yml +++ b/playbooks/roles/analytics-api/defaults/main.yml @@ -19,46 +19,58 @@ ANALYTICS_API_NEWRELIC_APPNAME: "{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT } ANALYTICS_API_PIP_EXTRA_ARGS: "-i {{ COMMON_PYPI_MIRROR_URL }}" ANALYTICS_API_NGINX_PORT: "18100" +ANALYTICS_API_DATABASES: + # rw user + default: + ENGINE: 'django.db.backends.mysql' + NAME: 'analytics-api' + USER: 'api001' + PASSWORD: 'password' + HOST: 'localhost' + PORT: '3306' + # read-only user + reports: + ENGINE: 'django.db.backends.mysql' + NAME: 'reports' + USER: 'reports001' + PASSWORD: 'password' + HOST: 'localhost' + PORT: '3306' + ANALYTICS_API_VERSION: "master" # Default dummy user, override this!! ANALYTICS_API_USERS: "dummy-api-user": "changeme" +ANALYTICS_API_SECRET_KEY: 'Your secret key here' +ANALYTICS_API_TIME_ZONE: 'UTC' +ANALYTICS_API_LANGUAGE_CODE: 'en-us' +ANALYTICS_API_EMAIL_HOST: 'localhost' +ANALYTICS_API_EMAIL_HOST_USER: 'mail_user' +ANALYTICS_API_EMAIL_HOST_PASSWORD: 'mail_password' +ANALYTICS_API_EMAIL_PORT: 587 +ANALYTICS_API_AUTH_TOKEN: 'put-your-api-token-here' + ANALYTICS_API_CONFIG: ANALYTICS_DATABASE: 'reports' - SECRET_KEY: 'Your secret key here' - TIME_ZONE: 'America/New_York' - LANGUAGE_CODE: 'en-us' + SECRET_KEY: '{{ ANALYTICS_API_SECRET_KEY }}' + TIME_ZONE: '{{ ANALYTICS_API_TIME_ZONE }}' + LANGUAGE_CODE: '{{ANALYTICS_API_LANGUAGE_CODE }}' # email config - EMAIL_HOST: 'smtp.example.com' - EMAIL_HOST_PASSWORD: "" - EMAIL_HOST_USER: "" - EMAIL_PORT: 587 - API_AUTH_TOKEN: 'put-your-api-token-here' - STATICFILES_DIRS: [] + EMAIL_HOST: '{{ ANALYTICS_API_EMAIL_HOST }}' + EMAIL_HOST_PASSWORD: '{{ ANALYTICS_API_EMAIL_HOST_PASSWORD }}' + EMAIL_HOST_USER: '{{ ANALYTICS_API_EMAIL_HOST_USER }}' + EMAIL_PORT: $ANALYTICS_API_EMAIL_PORT + API_AUTH_TOKEN: '{{ ANALYTICS_API_AUTH_TOKEN }}' + STATICFILES_DIRS: ['static'] STATIC_ROOT: "{{ COMMON_DATA_DIR }}/{{ analytics_api_service_name }}/staticfiles" # db config DATABASE_OPTIONS: connect_timeout: 10 - DATABASES: - # rw user - default: - ENGINE: 'django.db.backends.mysql' - NAME: 'analytics-api' - USER: 'api001' - PASSWORD: 'password' - HOST: 'localhost' - PORT: '3306' - # read-only user - reports: - ENGINE: 'django.db.backends.mysql' - NAME: 'reports' - USER: 'reports001' - PASSWORD: 'password' - HOST: 'localhost' - PORT: '3306' + DATABASES: '{{ ANALYTICS_API_DATABASES }}' ANALYTICS_API_GUNICORN_WORKERS: "2" +ANALYTICS_API_GUNICORN_EXTRA: "" # # vars are namespace with the module name. # diff --git a/playbooks/roles/analytics-api/tasks/main.yml b/playbooks/roles/analytics-api/tasks/main.yml index 28bc6b47ffd..bb0ce8ab167 100644 --- a/playbooks/roles/analytics-api/tasks/main.yml +++ b/playbooks/roles/analytics-api/tasks/main.yml @@ -32,7 +32,7 @@ # ansible-playbook -i 'api.example.com,' ./analyticsapi.yml -e@/ansible/vars/deployment.yml -e@/ansible/vars/env-deployment.yml # -- fail: msg="You must provide an private key for the analytics repo" +- fail: msg="You must provide a private key for the analytics repo" when: not ANALYTICS_API_GIT_IDENTITY - include: deploy.yml tags=deploy diff --git a/playbooks/roles/analytics-api/templates/edx/app/analytics-api/analytics-api.sh.j2 b/playbooks/roles/analytics-api/templates/edx/app/analytics-api/analytics-api.sh.j2 index 190cd13e613..e188734102e 100644 --- a/playbooks/roles/analytics-api/templates/edx/app/analytics-api/analytics-api.sh.j2 +++ b/playbooks/roles/analytics-api/templates/edx/app/analytics-api/analytics-api.sh.j2 @@ -15,4 +15,4 @@ export NEW_RELIC_LICENSE_KEY="{{ NEWRELIC_LICENSE_KEY }}" source {{ analytics_api_app_dir }}/analytics_api_env -{{ executable }} --pythonpath={{ analytics_api_code_dir }} -b {{ analytics_api_gunicorn_host }}:{{ analytics_api_gunicorn_port }} -w {{ ANALYTICS_API_GUNICORN_WORKERS }} --timeout={{ analytics_api_gunicorn_timeout }} analyticsdataserver.wsgi:application +{{ executable }} --pythonpath={{ analytics_api_code_dir }} -b {{ analytics_api_gunicorn_host }}:{{ analytics_api_gunicorn_port }} -w {{ ANALYTICS_API_GUNICORN_WORKERS }} --timeout={{ analytics_api_gunicorn_timeout }} {{ ANALYTICS_API_GUNICORN_EXTRA }} analyticsdataserver.wsgi:application diff --git a/playbooks/roles/analytics-server/defaults/main.yml b/playbooks/roles/analytics-server/defaults/main.yml index 55765c042a7..3f70a808ef7 100644 --- a/playbooks/roles/analytics-server/defaults/main.yml +++ b/playbooks/roles/analytics-server/defaults/main.yml @@ -19,6 +19,7 @@ AS_SERVER_PORT: '9000' AS_ENV_LANG: 'en_US.UTF-8' AS_LOG_LEVEL: 'INFO' AS_WORKERS: '2' +AS_GUNICORN_EXTRA: "" # add public keys to enable the automator user # for running manage.py commands @@ -40,14 +41,14 @@ analytics_auth_config: DATABASES: analytics: <<: *databases_default - USER: $AS_DB_ANALYTICS_USER - PASSWORD: $AS_DB_ANALYTICS_PASSWORD - HOST: $AS_DB_ANALYTICS_HOST - ANALYTICS_API_KEY: $AS_API_KEY + USER: "{{ AS_DB_ANALYTICS_USER }}" + PASSWORD: "{{ AS_DB_ANALYTICS_PASSWORD }}" + HOST: "{{ AS_DB_ANALYTICS_HOST }}" + ANALYTICS_API_KEY: "{{ AS_API_KEY }}" ANALYTICS_RESULTS_DB: - MONGO_URI: $AS_DB_RESULTS_URL - MONGO_DB: $AS_DB_RESULTS_DB - MONGO_STORED_QUERIES_COLLECTION: $AS_DB_RESULTS_COLLECTION + MONGO_URI: "{{ AS_DB_RESULTS_URL }}" + MONGO_DB: "{{ AS_DB_RESULTS_DB }}" + MONGO_STORED_QUERIES_COLLECTION: "{{ AS_DB_RESULTS_COLLECTION }}" as_role_name: "analytics-server" as_user: "analytics-server" diff --git a/playbooks/roles/analytics-server/tasks/deploy.yml b/playbooks/roles/analytics-server/tasks/deploy.yml index 7ebc382df97..f4153426f68 100644 --- a/playbooks/roles/analytics-server/tasks/deploy.yml +++ b/playbooks/roles/analytics-server/tasks/deploy.yml @@ -28,7 +28,7 @@ accept_hostkey=yes version={{ as_version }} force=true environment: - GIT_SSH: $as_git_ssh + GIT_SSH: "{{ as_git_ssh }}" notify: restart the analytics service notify: start the analytics service tags: diff --git a/playbooks/roles/analytics-server/templates/etc/init/analytics.conf.j2 b/playbooks/roles/analytics-server/templates/etc/init/analytics.conf.j2 index c060f623580..185d74cb625 100644 --- a/playbooks/roles/analytics-server/templates/etc/init/analytics.conf.j2 +++ b/playbooks/roles/analytics-server/templates/etc/init/analytics.conf.j2 @@ -18,4 +18,4 @@ env DJANGO_SETTINGS_MODULE={{ as_django_settings }} chdir {{ as_code_dir }} setuid {{ as_web_user }} -exec {{ as_venv_dir }}/bin/gunicorn -b 0.0.0.0:$PORT -w $WORKERS --pythonpath={{ as_code_dir }}/anserv anserv.wsgi +exec {{ as_venv_dir }}/bin/gunicorn -b 0.0.0.0:$PORT -w $WORKERS --pythonpath={{ as_code_dir }}/anserv {{ AS_GUNICORN_EXTRA }} anserv.wsgi diff --git a/playbooks/roles/analytics/defaults/main.yml b/playbooks/roles/analytics/defaults/main.yml index e17174c1de5..36e0dd3dc62 100644 --- a/playbooks/roles/analytics/defaults/main.yml +++ b/playbooks/roles/analytics/defaults/main.yml @@ -19,6 +19,7 @@ ANALYTICS_SERVER_PORT: '9000' ANALYTICS_ENV_LANG: 'en_US.UTF-8' ANALYTICS_LOG_LEVEL: 'INFO' ANALYTICS_WORKERS: '2' +ANALYTICS_GUNICORN_EXTRA: "" DATABASES: default: &databases_default @@ -33,14 +34,14 @@ analytics_auth_config: DATABASES: analytics: <<: *databases_default - USER: $ANALYTICS_DB_ANALYTICS_USER - PASSWORD: $ANALYTICS_DB_ANALYTICS_PASSWORD - HOST: $ANALYTICS_DB_ANALYTICS_HOST - ANALYTICS_API_KEY: $ANALYTICS_API_KEY + USER: "{{ ANALYTICS_DB_ANALYTICS_USER }}" + PASSWORD: "{{ ANALYTICS_DB_ANALYTICS_PASSWORD }}" + HOST: "{{ ANALYTICS_DB_ANALYTICS_HOST }}" + ANALYTICS_API_KEY: "{{ ANALYTICS_API_KEY }}" ANALYTICS_RESULTS_DB: - MONGO_URI: $ANALYTICS_DB_RESULTS_URL - MONGO_DB: $ANALYTICS_DB_RESULTS_DB - MONGO_STORED_QUERIES_COLLECTION: $ANALYTICS_DB_RESULTS_COLLECTION + MONGO_URI: "{{ ANALYTICS_DB_RESULTS_URL }}" + MONGO_DB: "{{ ANALYTICS_DB_RESULTS_DB }}" + MONGO_STORED_QUERIES_COLLECTION: "{{ ANALYTICS_DB_RESULTS_COLLECTION }}" analytics_role_name: "analytics" analytics_user: "analytics" diff --git a/playbooks/roles/analytics/tasks/deploy.yml b/playbooks/roles/analytics/tasks/deploy.yml index c84667d54e5..b6c5945b9db 100644 --- a/playbooks/roles/analytics/tasks/deploy.yml +++ b/playbooks/roles/analytics/tasks/deploy.yml @@ -28,7 +28,7 @@ accept_hostkey=yes version={{ analytics_version }} force=true environment: - GIT_SSH: $analytics_git_ssh + GIT_SSH: "{{ analytics_git_ssh }}" notify: restart the analytics service notify: start the analytics service tags: diff --git a/playbooks/roles/analytics/templates/etc/init/analytics.conf.j2 b/playbooks/roles/analytics/templates/etc/init/analytics.conf.j2 index f1e797a240f..8e83f5407bd 100644 --- a/playbooks/roles/analytics/templates/etc/init/analytics.conf.j2 +++ b/playbooks/roles/analytics/templates/etc/init/analytics.conf.j2 @@ -18,4 +18,4 @@ env DJANGO_SETTINGS_MODULE={{ analytics_django_settings }} chdir {{ analytics_code_dir }} setuid {{ analytics_web_user }} -exec {{ analytics_venv_dir }}/bin/gunicorn -b 0.0.0.0:$PORT -w $WORKERS --pythonpath={{ analytics_code_dir }}/anserv anserv.wsgi +exec {{ analytics_venv_dir }}/bin/gunicorn -b 0.0.0.0:$PORT -w $WORKERS --pythonpath={{ analytics_code_dir }}/anserv {{ ANALYTICS_GUNICORN_EXTRA }} anserv.wsgi diff --git a/playbooks/roles/ansible-role/tasks/main.yml b/playbooks/roles/ansible-role/tasks/main.yml index ba2a7f5e37f..61ca73a1e10 100644 --- a/playbooks/roles/ansible-role/tasks/main.yml +++ b/playbooks/roles/ansible-role/tasks/main.yml @@ -10,7 +10,7 @@ when: role_exists | success - name: create role directories - file: path=roles/{{role_name}}/{{ item }} state=directory + file: path=roles/{{ role_name }}/{{ item }} state=directory with_items: - tasks - meta diff --git a/playbooks/roles/ansible_debug/tasks/main.yml b/playbooks/roles/ansible_debug/tasks/main.yml index adb4728cc74..a651dfab90f 100644 --- a/playbooks/roles/ansible_debug/tasks/main.yml +++ b/playbooks/roles/ansible_debug/tasks/main.yml @@ -6,7 +6,7 @@ - debug - name: Dump lms auth|env file - template: src=../../edxapp/templates/lms.{{item}}.json.j2 dest=/tmp/lms.{{item}}.json mode=0600 + template: src=../../edxapp/templates/lms.{{ item }}.json.j2 dest=/tmp/lms.{{ item }}.json mode=0600 with_items: - env - auth @@ -16,7 +16,7 @@ - debug - name: Dump lms-preview auth|env file - template: src=../../edxapp/templates/lms-preview.{{item}}.json.j2 dest=/tmp/lms-preview.{{item}}.json mode=0600 + template: src=../../edxapp/templates/lms-preview.{{ item }}.json.j2 dest=/tmp/lms-preview.{{ item }}.json mode=0600 with_items: - env - auth @@ -26,7 +26,7 @@ - debug - name: Dump cms auth|env file - template: src=../../edxapp/templates/cms.{{item}}.json.j2 dest=/tmp/cms.{{item}}.json mode=0600 + template: src=../../edxapp/templates/cms.{{ item }}.json.j2 dest=/tmp/cms.{{ item }}.json mode=0600 with_items: - env - auth @@ -44,7 +44,7 @@ - name: fetch remote files # fetch is fail-safe for remote files that don't exist # setting mode is not an option - fetch: src=/tmp/{{item}} dest=/tmp/{{ansible_hostname}}-{{item}} flat=True + fetch: src=/tmp/{{ item }} dest=/tmp/{{ ansible_hostname }}-{{item}} flat=True with_items: - ansible.all.json - ansible.all.yml diff --git a/playbooks/roles/antivirus/defaults/main.yml b/playbooks/roles/antivirus/defaults/main.yml new file mode 100644 index 00000000000..89a5e5b072f --- /dev/null +++ b/playbooks/roles/antivirus/defaults/main.yml @@ -0,0 +1,38 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +## +# Defaults for role antivirus +# + +# +# vars are namespace with the module name. +# +antivirus_role_name: antivirus + +# +# OS packages +# + +antivirus_debian_pkgs: [clamav] +antivirus_redhat_pkgs: [] +antivirus_pip_pkgs: [] + +antivirus_app_dir: /edx/app/antivirus +antivirus_user: "antivirus" + +ANTIVIRUS_BUCKETS: !!null +ANTIVIRUS_MAILTO: "{{ EDXAPP_TECH_SUPPORT_EMAIL }}" +ANTIVIRUS_MAILFROM: "{{ EDXAPP_DEFAULT_FROM_EMAIL }}" +ANTIVIRUS_AWS_KEY: "" +ANTIVIRUS_AWS_SECRET: "" +ANTIVIRUS_S3_AWS_KEY: "{{ ANTIVIRUS_AWS_KEY }}" +ANTIVIRUS_SES_AWS_KEY: "{{ ANTIVIRUS_AWS_KEY }}" +ANTIVIRUS_S3_AWS_SECRET: "{{ ANTIVIRUS_AWS_SECRET}}" +ANTIVIRUS_SES_AWS_SECRET: "{{ ANTIVIRUS_AWS_SECRET}}" diff --git a/playbooks/roles/antivirus/handlers/main.yml b/playbooks/roles/antivirus/handlers/main.yml new file mode 100644 index 00000000000..faa9e377fd9 --- /dev/null +++ b/playbooks/roles/antivirus/handlers/main.yml @@ -0,0 +1,16 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +# +# +# Handlers for role antivirus +# +# Overview: +# +# diff --git a/playbooks/roles/antivirus/meta/main.yml b/playbooks/roles/antivirus/meta/main.yml new file mode 100644 index 00000000000..32949d70a9d --- /dev/null +++ b/playbooks/roles/antivirus/meta/main.yml @@ -0,0 +1,16 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +## +# Role includes for role antivirus +# +dependencies: + - role: user + user_info: "{{ BASTION_USER_INFO }}" + - aws diff --git a/playbooks/roles/antivirus/tasks/main.yml b/playbooks/roles/antivirus/tasks/main.yml new file mode 100644 index 00000000000..f20d3d2f966 --- /dev/null +++ b/playbooks/roles/antivirus/tasks/main.yml @@ -0,0 +1,63 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +# +# +# Tasks for role antivirus +# +# Overview: +# +# +# Dependencies: +# +# +# Example play: +# +# + +- name: install antivirus system packages + apt: pkg={{ item }} install_recommends=yes state=present + with_items: antivirus_debian_pkgs + +- name: create antivirus scanner user + user: > + name="{{ antivirus_user }}" + home="{{ antivirus_app_dir }}" + createhome=no + shell=/bin/false + +- name: create antivirus app and data dirs + file: > + path="{{ item }}" + state=directory + owner="{{ antivirus_user }}" + group="{{ antivirus_user }}" + with_items: + - "{{ antivirus_app_dir }}" + - "{{ antivirus_app_dir }}/data" + +- name: install antivirus s3 scanner script + template: > + src=s3_bucket_virus_scan.sh.j2 + dest={{ antivirus_app_dir }}/s3_bucket_virus_scan.sh + mode=0555 + owner={{ antivirus_user }} + group={{ antivirus_user }} + +- name: install antivirus s3 scanner cronjob + cron: > + name="antivirus-{{ item }}" + job="{{ antivirus_app_dir }}/s3_bucket_virus_scan.sh -b '{{ item }}' -m '{{ ANTIVIRUS_MAILTO }}' -f '{{ ANTIVIRUS_MAILFROM }}'" + backup=yes + cron_file=antivirus-{{ item }} + user={{ antivirus_user }} + hour="*" + minute="0" + day="*" + with_items: ANTIVIRUS_BUCKETS diff --git a/playbooks/roles/antivirus/templates/s3_bucket_virus_scan.sh.j2 b/playbooks/roles/antivirus/templates/s3_bucket_virus_scan.sh.j2 new file mode 100644 index 00000000000..72b568ce45e --- /dev/null +++ b/playbooks/roles/antivirus/templates/s3_bucket_virus_scan.sh.j2 @@ -0,0 +1,77 @@ +#! /bin/bash + +DEBUG="false" +BUCKETNAME="none" +MAILTO="" +MAILFROM="" +ANTIVIRUS_S3_AWS_KEY="{{ ANTIVIRUS_S3_AWS_KEY }}" +ANTIVIRUS_SES_AWS_KEY="{{ ANTIVIRUS_SES_AWS_KEY }}" +ANTIVIRUS_S3_AWS_SECRET="{{ ANTIVIRUS_S3_AWS_SECRET}}" +ANTIVIRUS_SES_AWS_SECRET="{{ ANTIVIRUS_SES_AWS_SECRET}}" +AWS_DEFAULT_REGION="{{ aws_region }}" + + +function usage { + echo "$0 - $VERSION"; + echo "Run ClamAV against the contents of an S3 Bucket."; + echo "Usage: $0 [options]"; + echo "options:"; + echo " -d Debug mode"; + echo " -h Usage (this screen)"; + echo " -b "; + echo " -m "; + echo " -f "; + echo " -k "; + echo " -s " + +} + +while getopts "dhb:m:f:k:s:" optionName; do + case "$optionName" in + d) + DEBUG="true" + ;; + h) + usage; + exit; + ;; + [?]) + usage; + exit; + ;; + b) + BUCKETNAME=$OPTARG; + ;; + m) + MAILTO=$OPTARG; + ;; + f) + MAILFROM=$OPTARG; + ;; + k) + AWS_ACCESS_KEY_ID=$OPTARG; + ANTIVIRUS_S3_AWS_KEY=$OPTARG; + ANTIVIRUS_SES_AWS_KEY=$OPTARG; + ;; + s) + AWS_SECRET_ACCESS_KEY=$OPTARG; + ANTIVIRUS_S3_AWS_SECRET=$OPTARG; + ANTIVIRUS_SES_AWS_SECRET=$OPTARG; + ;; + esac +done + +cd {{ antivirus_app_dir }} + +export AWS_ACCESS_KEY_ID=$ANTIVIRUS_S3_AWS_KEY +export AWS_SECRET_ACCESS_KEY=$ANTIVIRUS_S3_AWS_SECRET +export AWS_DEFAULT_REGION + +mkdir -p data/$BUCKETNAME +aws s3 sync s3://$BUCKETNAME/ data/$BUCKETNAME +CLAMOUT=$(clamscan -ri data/$BUCKETNAME); +if [[ $? -ne 0 ]]; then + export AWS_ACCESS_KEY_ID=$ANTIVIRUS_SES_AWS_KEY + export AWS_SECRET_ACCESS_KEY=$ANTIVIRUS_SES_AWS_SECRET + aws ses send-email --to $MAILTO --from $MAILFROM --subject "Virus Scanner malicious file on $BUCKETNAME" --text "$CLAMOUT" +fi diff --git a/playbooks/roles/apache/templates/lms.j2 b/playbooks/roles/apache/templates/lms.j2 index e1ccf96f194..2f5c068a5b9 100644 --- a/playbooks/roles/apache/templates/lms.j2 +++ b/playbooks/roles/apache/templates/lms.j2 @@ -1,7 +1,7 @@ WSGIPythonHome {{ edxapp_venv_dir }} WSGIRestrictEmbedded On - + ServerName https://{{ lms_env_config.SITE_NAME }} ServerAlias *.{{ lms_env_config.SITE_NAME }} UseCanonicalName On diff --git a/playbooks/roles/aws/defaults/main.yml b/playbooks/roles/aws/defaults/main.yml index c60912f2220..209b4295675 100644 --- a/playbooks/roles/aws/defaults/main.yml +++ b/playbooks/roles/aws/defaults/main.yml @@ -23,29 +23,34 @@ AWS_S3_LOGS: false # This relies on your server being able to send mail AWS_S3_LOGS_NOTIFY_EMAIL: dummy@example.com AWS_S3_LOGS_FROM_EMAIL: dummy@example.com -# Separate buckets for tracking logs and everything else -# You should be overriding the environment and deployment vars -# Order of precedence is left to right for exclude and include options -AWS_S3_LOG_PATHS: - - bucket: "edx-{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}/logs/tracking" - path: "{{ COMMON_LOG_DIR }}/tracking/*" - - bucket: "edx-{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}/logs/application" - path: "{{ COMMON_LOG_DIR }}/!(*tracking*)" - - bucket: "edx-{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}/logs/system" - path: "/var/log/*" # # vars are namespace with the module name. # aws_role_name: aws -aws_data_dir: "{{ COMMON_DATA_DIR }}/aws" -aws_app_dir: "{{ COMMON_APP_DIR }}/aws" -aws_s3_sync_script: "{{ aws_app_dir }}/send-logs-to-s3" -aws_s3_logfile: "{{ aws_log_dir }}/s3-log-sync.log" -aws_log_dir: "{{ COMMON_LOG_DIR }}/aws" + +aws_dirs: + home: + path: "{{ COMMON_APP_DIR }}/{{ aws_role_name }}" + owner: "root" + group: "root" + mode: "0755" + logs: + path: "{{ COMMON_LOG_DIR }}/{{ aws_role_name }}" + owner: "syslog" + group: "syslog" + mode: "0700" + data: + path: "{{ COMMON_DATA_DIR }}/{{ aws_role_name }}" + owner: "root" + group: "root" + mode: "0700" + +aws_s3_sync_script: "{{ aws_dirs.home.path }}/send-logs-to-s3" +aws_s3_logfile: "{{ aws_dirs.logs.path }}/s3-log-sync.log" aws_region: "us-east-1" # default path to the aws binary -s3cmd_cmd: "{{ COMMON_BIN_DIR }}/s3cmd" +aws_s3cmd: "{{ COMMON_BIN_DIR }}/s3cmd" aws_cmd: "/usr/local/bin/aws" # # OS packages diff --git a/playbooks/roles/aws/tasks/main.yml b/playbooks/roles/aws/tasks/main.yml index 1fba494e85f..ca694d76e15 100644 --- a/playbooks/roles/aws/tasks/main.yml +++ b/playbooks/roles/aws/tasks/main.yml @@ -21,26 +21,14 @@ # # -- name: create data directories +- name: create all service directories file: > - path={{ item }} - state=directory - owner=root - group=root - mode=0700 - with_items: - - "{{ aws_data_dir }}" - - "{{ aws_log_dir }}" - -- name: create app directory - file: > - path={{ item }} - state=directory - owner=root - group=root - mode=0755 - with_items: - - "{{ aws_app_dir }}" + path="{{ item.value.path }}" + state="directory" + owner="{{ item.value.owner }}" + group="{{ item.value.group }}" + mode="{{ item.value.mode }}" + with_dict: aws_dirs - name: install system packages apt: > @@ -57,18 +45,18 @@ - name: get s3cmd get_url: > url={{ aws_s3cmd_url }} - dest={{ aws_data_dir }}/ + dest={{ aws_dirs.data.path }}/ - name: untar s3cmd shell: > - tar xf {{ aws_data_dir }}/{{ aws_s3cmd_version }}.tar.gz - creates={{ aws_app_dir }}/{{ aws_s3cmd_version }}/s3cmd - chdir={{ aws_app_dir }} + tar xf {{ aws_dirs.data.path }}/{{ aws_s3cmd_version }}.tar.gz + creates={{ aws_dirs.data.path }}/{{ aws_s3cmd_version }}/s3cmd + chdir={{ aws_dirs.home.path }} - name: create symlink for s3cmd file: > - src={{ aws_app_dir }}/{{ aws_s3cmd_version }}/s3cmd - dest={{ COMMON_BIN_DIR }}/s3cmd + src={{ aws_dirs.home.path }}/{{ aws_s3cmd_version }}/s3cmd + dest={{ aws_s3cmd }} state=link - name: create s3 log sync script @@ -84,7 +72,7 @@ dest={{ COMMON_BIN_DIR }}/{{ aws_s3_sync_script|basename }} when: AWS_S3_LOGS -- name: run s3 log sync script on supervisor shutdown +- name: force logrotate on supervisor stop template: > src=etc/init/sync-on-stop.conf.j2 dest=/etc/init/sync-on-stop.conf @@ -99,4 +87,5 @@ user: root minute: 0 job: "{{ aws_s3_sync_script }} > /dev/null 2>&1" + state: absent when: AWS_S3_LOGS diff --git a/playbooks/roles/aws/templates/etc/init/sync-on-stop.conf.j2 b/playbooks/roles/aws/templates/etc/init/sync-on-stop.conf.j2 index db9b50d9bec..783417c846f 100644 --- a/playbooks/roles/aws/templates/etc/init/sync-on-stop.conf.j2 +++ b/playbooks/roles/aws/templates/etc/init/sync-on-stop.conf.j2 @@ -1,5 +1,6 @@ start on stopped supervisor -description "sync s3 logs on supervisor shutdown" +description "sync tracking logs on supervisor shutdown" script - /bin/bash {{ aws_s3_sync_script }} + /usr/sbin/logrotate -f /etc/logrotate.d/hourly/tracking.log + /usr/sbin/logrotate -f /etc/logrotate.d/hourly/edx-services end script diff --git a/playbooks/roles/aws/templates/send-logs-to-s3.j2 b/playbooks/roles/aws/templates/send-logs-to-s3.j2 index c3018b5466e..6df79f237f6 100644 --- a/playbooks/roles/aws/templates/send-logs-to-s3.j2 +++ b/playbooks/roles/aws/templates/send-logs-to-s3.j2 @@ -4,13 +4,23 @@ # # This script can be called from logrotate # to sync logs to s3 +# if (( $EUID != 0 )); then echo "Please run as the root user" exit 1 fi -exec > >(tee "{{ aws_s3_logfile }}") +# +# Ensure the log processors can read without +# running as root +if [ ! -f "{{ aws_s3_logfile }}" ]; then + sudo -u syslog touch "{{ aws_s3_logfile }}" +else + chown syslog.syslog "{{ aws_s3_logfile }}" +fi + +exec > >(tee -a "{{ aws_s3_logfile }}") exec 2>&1 # s3cmd sync requires a valid home @@ -31,10 +41,12 @@ usage() { -v add verbosity (set -x) -n echo what will be done -h this + -d directory to sync + -b bucket path to sync to EO } -while getopts "vhn" opt; do +while getopts "vhnb:d:" opt; do case $opt in v) set -x @@ -48,9 +60,21 @@ while getopts "vhn" opt; do noop="echo Would have run: " shift ;; + d) + directory=$OPTARG + ;; + b) + bucket_path=$OPTARG + ;; esac done +if [[ -z $bucket_path || -z $directory ]]; then + echo "ERROR: You must provide a directory and a bucket to sync!" + usage + exit 1 +fi + # grab the first security group for the instance # which will be used as a directory name in the s3 # bucket @@ -90,9 +114,7 @@ instance_id=$(ec2metadata --instance-id) ip=$(ec2metadata --local-ipv4) availability_zone=$(ec2metadata --availability-zone) # region isn't available via the metadata service -region=${availability_zone:0:${{lb}}#availability_zone{{rb}} - 1} +region=${availability_zone:0:${{ lb }}#availability_zone{{ rb }} - 1} s3_path="${2}/$sec_grp/" -{% for item in AWS_S3_LOG_PATHS -%} -$noop {{ s3cmd_cmd }} sync {{ item['path'] }} "s3://{{ item['bucket'] }}/$sec_grp/${instance_id}-${ip}/" -{% endfor %} +$noop {{ aws_s3cmd }} --multipart-chunk-size-mb 5120 --disable-multipart sync $directory "s3://${bucket_path}/${sec_grp}/${instance_id}-${ip}/" diff --git a/playbooks/roles/browsermob-proxy/files/browsermob-proxy b/playbooks/roles/browsermob-proxy/files/browsermob-proxy index 9e9fdef16ff..e2344acadcb 100644 --- a/playbooks/roles/browsermob-proxy/files/browsermob-proxy +++ b/playbooks/roles/browsermob-proxy/files/browsermob-proxy @@ -1,2 +1,2 @@ #!/bin/sh -/etc/browsermob-proxy/bin/browsermob-proxy +/etc/browsermob-proxy/bin/browsermob-proxy $* diff --git a/playbooks/roles/browsers/defaults/main.yml b/playbooks/roles/browsers/defaults/main.yml index 8cc4d8100e3..1d05333a569 100644 --- a/playbooks/roles/browsers/defaults/main.yml +++ b/playbooks/roles/browsers/defaults/main.yml @@ -13,7 +13,7 @@ browser_deb_pkgs: # which often causes spurious acceptance test failures. browser_s3_deb_pkgs: - { name: "google-chrome-stable_30.0.1599.114-1_amd64.deb", url: "https://s3.amazonaws.com/vagrant.testeng.edx.org/google-chrome-stable_30.0.1599.114-1_amd64.deb" } - - { name: "firefox_25.0+build3-0ubuntu0.12.04.1_amd64.deb", url: "https://s3.amazonaws.com/vagrant.testeng.edx.org/firefox_25.0%2Bbuild3-0ubuntu0.12.04.1_amd64.deb" } + - { name: "firefox_28.0+build2-0ubuntu0.12.04.1_amd64.deb", url: "https://s3.amazonaws.com/vagrant.testeng.edx.org/firefox_28.0%2Bbuild2-0ubuntu0.12.04.1_amd64.deb" } # Chrome and ChromeDriver chromedriver_version: 2.6 diff --git a/playbooks/roles/certs/defaults/main.yml b/playbooks/roles/certs/defaults/main.yml index 8ae5b28e42d..a8c317b51a3 100644 --- a/playbooks/roles/certs/defaults/main.yml +++ b/playbooks/roles/certs/defaults/main.yml @@ -71,25 +71,25 @@ certs_env_config: # CERTS_DATA is legacy, not used CERT_DATA: {} QUEUE_NAME: "certificates" - QUEUE_URL: $CERTS_QUEUE_URL - CERT_BUCKET: $CERTS_BUCKET + QUEUE_URL: "{{ CERTS_QUEUE_URL }}" + CERT_BUCKET: "{{ CERTS_BUCKET }}" # gnupg signing key - CERT_KEY_ID: $CERTS_KEY_ID + CERT_KEY_ID: "{{ CERTS_KEY_ID }}" LOGGING_ENV: "" - CERT_GPG_DIR: $certs_gpg_dir - CERT_URL: $CERTS_URL - CERT_DOWNLOAD_URL: $CERTS_DOWNLOAD_URL - CERT_WEB_ROOT: $CERTS_WEB_ROOT - COPY_TO_WEB_ROOT: $CERTS_COPY_TO_WEB_ROOT - S3_UPLOAD: $CERTS_S3_UPLOAD - CERT_VERIFY_URL: $CERTS_VERIFY_URL - TEMPLATE_DATA_DIR: $CERTS_TEMPLATE_DATA_DIR + CERT_GPG_DIR: "{{ certs_gpg_dir }}" + CERT_URL: "{{ CERTS_URL }}" + CERT_DOWNLOAD_URL: "{{ CERTS_DOWNLOAD_URL }}" + CERT_WEB_ROOT: "{{ CERTS_WEB_ROOT }}" + COPY_TO_WEB_ROOT: "{{ CERTS_COPY_TO_WEB_ROOT }}" + S3_UPLOAD: "{{ CERTS_S3_UPLOAD }}" + CERT_VERIFY_URL: "{{ CERTS_VERIFY_URL }}" + TEMPLATE_DATA_DIR: "{{ CERTS_TEMPLATE_DATA_DIR }}" certs_auth_config: - QUEUE_USER: $CERTS_QUEUE_USER - QUEUE_PASS: $CERTS_QUEUE_PASS - QUEUE_AUTH_USER: $CERTS_XQUEUE_AUTH_USER - QUEUE_AUTH_PASS: $CERTS_XQUEUE_AUTH_PASS - CERT_KEY_ID: $CERTS_KEY_ID - CERT_AWS_ID: $CERTS_AWS_ID - CERT_AWS_KEY: $CERTS_AWS_KEY + QUEUE_USER: "{{ CERTS_QUEUE_USER }}" + QUEUE_PASS: "{{ CERTS_QUEUE_PASS }}" + QUEUE_AUTH_USER: "{{ CERTS_XQUEUE_AUTH_USER }}" + QUEUE_AUTH_PASS: "{{ CERTS_XQUEUE_AUTH_PASS }}" + CERT_KEY_ID: "{{ CERTS_KEY_ID }}" + CERT_AWS_ID: "{{ CERTS_AWS_ID }}" + CERT_AWS_KEY: "{{ CERTS_AWS_KEY }}" diff --git a/playbooks/roles/common/defaults/main.yml b/playbooks/roles/common/defaults/main.yml index 72980edf6f6..8c8cbaed74b 100644 --- a/playbooks/roles/common/defaults/main.yml +++ b/playbooks/roles/common/defaults/main.yml @@ -7,7 +7,12 @@ COMMON_ENABLE_BASIC_AUTH: False COMMON_HTPASSWD_USER: edx COMMON_HTPASSWD_PASS: edx - +# Turn on syncing logs on rotation for edx +# application and tracking logs, must also +# have the AWS role installed +COMMON_AWS_SYNC: False +COMMON_AWS_SYNC_BUCKET: "edx-{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}" +COMMON_AWS_S3_SYNC_SCRIPT: "{{ COMMON_BIN_DIR }}/send-logs-to-s3" COMMON_BASE_DIR: /edx COMMON_DATA_DIR: "{{ COMMON_BASE_DIR}}/var" COMMON_APP_DIR: "{{ COMMON_BASE_DIR}}/app" @@ -24,6 +29,7 @@ COMMON_ENVIRONMENT: 'default_env' COMMON_DEPLOYMENT: 'default_deployment' COMMON_PYPI_MIRROR_URL: 'https://pypi.python.org/simple' COMMON_NPM_MIRROR_URL: 'http://registry.npmjs.org' +COMMON_UBUNTU_APT_KEYSERVER: "http://keyserver.ubuntu.com/pks/lookup?op=get&fingerprint=on&search=" # do not include http/https COMMON_GIT_MIRROR: 'github.com' # override this var to set a different hostname @@ -38,6 +44,7 @@ COMMON_CUSTOM_DHCLIENT_CONFIG: false COMMON_MOTD_TEMPLATE: "motd.tail.j2" COMMON_SSH_PASSWORD_AUTH: "no" +COMMON_SECURITY_UPDATES: no # These are three maintenance accounts across all databases # the read only user is is granted select privs on all dbs # the admin user is granted create user privs on all dbs @@ -69,6 +76,7 @@ common_debian_pkgs: - mosh - rsyslog - screen + - tmux - tree - git - unzip @@ -102,6 +110,10 @@ disable_edx_services: False # so different start scripts are generated in dev mode. devstack: False +# Some cluster apps need special settings when in vagrant +# due to eth0 always being the same IP address +vagrant_cluster: False + common_debian_variants: - Ubuntu - Debian diff --git a/playbooks/roles/common/meta/main.yml b/playbooks/roles/common/meta/main.yml index 6a517a3c36f..e43fb06d68a 100644 --- a/playbooks/roles/common/meta/main.yml +++ b/playbooks/roles/common/meta/main.yml @@ -2,5 +2,5 @@ dependencies: - role: user user_info: "{{ COMMON_USER_INFO }}" - - + - role: security + when: COMMON_SECURITY_UPDATES diff --git a/playbooks/roles/common/tasks/main.yml b/playbooks/roles/common/tasks/main.yml index 4483ba1f1b4..0f94382ec6d 100644 --- a/playbooks/roles/common/tasks/main.yml +++ b/playbooks/roles/common/tasks/main.yml @@ -1,4 +1,8 @@ --- +- name: Update CA Certificates + shell: > + /usr/sbin/update-ca-certificates + - name: Add user www-data # This is the default user for nginx user: > diff --git a/playbooks/roles/common/templates/etc/dhcp/dhclient.conf.j2 b/playbooks/roles/common/templates/etc/dhcp/dhclient.conf.j2 index 953c2c78076..2fd4f15d062 100644 --- a/playbooks/roles/common/templates/etc/dhcp/dhclient.conf.j2 +++ b/playbooks/roles/common/templates/etc/dhcp/dhclient.conf.j2 @@ -57,6 +57,6 @@ request subnet-mask, broadcast-address, time-offset, routers, #} interface "eth0" { - prepend domain-search {% for search in COMMON_DHCLIENT_DNS_SEARCH -%}"{{search}}"{%- if not loop.last -%},{%- endif -%} + prepend domain-search {% for search in COMMON_DHCLIENT_DNS_SEARCH -%}"{{ search }}"{%- if not loop.last -%},{%- endif -%} {%- endfor -%}; } diff --git a/playbooks/roles/common/templates/etc/logrotate.d/hourly/edx_logrotate_tracking_log.j2 b/playbooks/roles/common/templates/etc/logrotate.d/hourly/edx_logrotate_tracking_log.j2 index 165b4c14bc7..0623eed858d 100644 --- a/playbooks/roles/common/templates/etc/logrotate.d/hourly/edx_logrotate_tracking_log.j2 +++ b/playbooks/roles/common/templates/etc/logrotate.d/hourly/edx_logrotate_tracking_log.j2 @@ -11,4 +11,9 @@ postrotate /usr/bin/killall -HUP rsyslogd endscript + lastaction + {% if COMMON_AWS_SYNC -%} + {{ COMMON_AWS_S3_SYNC_SCRIPT }} -d "{{ COMMON_LOG_DIR }}/tracking/*" -b "{{ COMMON_AWS_SYNC_BUCKET }}/logs/tracking" + {% endif -%} + endscript } diff --git a/playbooks/roles/datadog/defaults/main.yml b/playbooks/roles/datadog/defaults/main.yml index 8fcc334a267..39f1a3220b4 100644 --- a/playbooks/roles/datadog/defaults/main.yml +++ b/playbooks/roles/datadog/defaults/main.yml @@ -1,7 +1,9 @@ --- DATADOG_API_KEY: "SPECIFY_KEY_HERE" -datadog_apt_key: "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x226AE980C7A7DA52" +datadog_agent_version: '1:5.0.4-516' + +datadog_apt_key: "0x226AE980C7A7DA52" datadog_debian_pkgs: - apparmor-utils - build-essential diff --git a/playbooks/roles/datadog/tasks/main.yml b/playbooks/roles/datadog/tasks/main.yml index 85d2540f04d..d52a2031b10 100644 --- a/playbooks/roles/datadog/tasks/main.yml +++ b/playbooks/roles/datadog/tasks/main.yml @@ -22,17 +22,22 @@ - datadog - name: add apt key - apt_key: id=C7A7DA52 url={{datadog_apt_key}} state=present + apt_key: id=C7A7DA52 url={{ COMMON_UBUNTU_APT_KEYSERVER }}{{ datadog_apt_key }} state=present + tags: + - datadog + +- name: remove unstable apt repository + apt_repository_1.8: repo='deb http://apt.datadoghq.com/ unstable main' validate_certs=no state=absent tags: - datadog - name: install apt repository - apt_repository_1.8: repo='deb http://apt.datadoghq.com/ unstable main' update_cache=yes validate_certs=no + apt_repository_1.8: repo='deb http://apt.datadoghq.com/ stable main' update_cache=yes validate_certs=no tags: - datadog - name: install datadog agent - apt: pkg="datadog-agent" + apt: pkg="datadog-agent={{ datadog_agent_version }}" tags: - datadog diff --git a/playbooks/roles/demo/defaults/main.yml b/playbooks/roles/demo/defaults/main.yml index 65314bda297..8910f34b697 100644 --- a/playbooks/roles/demo/defaults/main.yml +++ b/playbooks/roles/demo/defaults/main.yml @@ -30,6 +30,6 @@ demo_test_users: password: edx demo_edxapp_user: 'edxapp' -demo_edxapp_venv_bin: '{{COMMON_APP_DIR}}/{{demo_edxapp_user}}/venvs/{{demo_edxapp_user}}/bin' -demo_edxapp_course_data_dir: '{{COMMON_DATA_DIR}}/{{demo_edxapp_user}}/data' -demo_edxapp_code_dir: '{{COMMON_APP_DIR}}/{{demo_edxapp_user}}/edx-platform' +demo_edxapp_venv_bin: '{{ COMMON_APP_DIR }}/{{ demo_edxapp_user }}/venvs/{{demo_edxapp_user}}/bin' +demo_edxapp_course_data_dir: '{{ COMMON_DATA_DIR }}/{{ demo_edxapp_user }}/data' +demo_edxapp_code_dir: '{{ COMMON_APP_DIR }}/{{ demo_edxapp_user }}/edx-platform' diff --git a/playbooks/roles/discern/defaults/main.yml b/playbooks/roles/discern/defaults/main.yml index cf423c6d93d..6032cb5054e 100644 --- a/playbooks/roles/discern/defaults/main.yml +++ b/playbooks/roles/discern/defaults/main.yml @@ -11,7 +11,7 @@ DISCERN_MYSQL_PASSWORD: 'password' DISCERN_MYSQL_HOST: 'localhost' DISCERN_MYSQL_PORT: '3306' DISCERN_LANG: "en_US.UTF-8" - +DISCERN_GUNICORN_EXTRA: "" discern_app_dir: "{{ COMMON_APP_DIR }}/discern" discern_code_dir: "{{ discern_app_dir }}/discern" @@ -53,23 +53,23 @@ discern_env_config: discern_auth_config: - AWS_ACCESS_KEY_ID: $DISCERN_AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY: $DISCERN_SECRET_ACCESS_KEY - BROKER_URL: $DISCERN_BROKER_URL + AWS_ACCESS_KEY_ID: "{{ DISCERN_AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "{{ DISCERN_SECRET_ACCESS_KEY }}" + BROKER_URL: "{{ DISCERN_BROKER_URL }}" CACHES: default: BACKEND: 'django.core.cache.backends.memcached.MemcachedCache' - LOCATION: $DISCERN_MEMCACHE - CELERY_RESULT_BACKEND: $DISCERN_RESULT_BACKEND + LOCATION: "{{ DISCERN_MEMCACHE }}" + CELERY_RESULT_BACKEND: "{{ DISCERN_RESULT_BACKEND }}" DATABASES: default: ENGINE: django.db.backends.mysql - HOST: $DISCERN_MYSQL_HOST - NAME: $DISCERN_MYSQL_DB_NAME - PASSWORD: $DISCERN_MYSQL_PASSWORD - PORT: $DISCERN_MYSQL_PORT - USER: $DISCERN_MYSQL_USER - GOOGLE_ANALYTICS_PROPERTY_ID: $DISCERN_GOOGLE_ANALYTICS_PROPERTY_ID + HOST: "{{ DISCERN_MYSQL_HOST }}" + NAME: "{{ DISCERN_MYSQL_DB_NAME }}" + PASSWORD: "{{ DISCERN_MYSQL_PASSWORD }}" + PORT: "{{ DISCERN_MYSQL_PORT }}" + USER: "{{ DISCERN_MYSQL_USER }}" + GOOGLE_ANALYTICS_PROPERTY_ID: "{{ DISCERN_GOOGLE_ANALYTICS_PROPERTY_ID }}" discern_debian_pkgs: diff --git a/playbooks/roles/discern/tasks/deploy.yml b/playbooks/roles/discern/tasks/deploy.yml index 230e5e1397d..a8f1476e89b 100644 --- a/playbooks/roles/discern/tasks/deploy.yml +++ b/playbooks/roles/discern/tasks/deploy.yml @@ -51,7 +51,7 @@ #Numpy has to be a pre-requirement in order for scipy to build - name : install python pre-requirements for discern and ease pip: > - requirements={{item}} virtualenv={{ discern_venv_dir }} state=present + requirements={{ item }} virtualenv={{ discern_venv_dir }} state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ discern_user }}" notify: @@ -62,7 +62,7 @@ - name : install python requirements for discern and ease pip: > - requirements={{item}} virtualenv={{ discern_venv_dir }} state=present + requirements={{ item }} virtualenv={{ discern_venv_dir }} state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ discern_user }}" notify: @@ -84,8 +84,8 @@ tar zxf {{ discern_nltk_tmp_file }} rm -f {{ discern_nltk_tmp_file }} touch {{ discern_nltk_download_url|basename }}-installed - creates={{ discern_data_dir }}/{{ discern_nltk_download_url|basename }}-installed - chdir={{ discern_data_dir }} + creates={{ discern_data_dir }}/{{ discern_nltk_download_url|basename }}-installed + chdir={{ discern_data_dir }} sudo_user: "{{ discern_user }}" notify: - restart discern @@ -95,8 +95,8 @@ #support virtualenvs as of this comment - name: django syncdb migrate and collectstatic for discern shell: > - {{ discern_venv_dir }}/bin/python {{discern_code_dir}}/manage.py {{item}} --noinput --settings={{discern_settings}} --pythonpath={{discern_code_dir}} - chdir={{ discern_code_dir }} + {{ discern_venv_dir }}/bin/python {{ discern_code_dir }}/manage.py {{ item }} --noinput --settings={{discern_settings}} --pythonpath={{discern_code_dir}} + chdir={{ discern_code_dir }} sudo_user: "{{ discern_user }}" notify: - restart discern @@ -107,8 +107,8 @@ #Have this separate from the other three because it doesn't take the noinput flag - name: django update_index for discern shell: > - {{ discern_venv_dir}}/bin/python {{discern_code_dir}}/manage.py update_index --settings={{discern_settings}} --pythonpath={{discern_code_dir}} - chdir={{ discern_code_dir }} + {{ discern_venv_dir}}/bin/python {{ discern_code_dir }}/manage.py update_index --settings={{ discern_settings }} --pythonpath={{discern_code_dir}} + chdir={{ discern_code_dir }} sudo_user: "{{ discern_user }}" notify: - restart discern diff --git a/playbooks/roles/discern/templates/celery.conf.j2 b/playbooks/roles/discern/templates/celery.conf.j2 index fb9d6312a74..db247a39412 100644 --- a/playbooks/roles/discern/templates/celery.conf.j2 +++ b/playbooks/roles/discern/templates/celery.conf.j2 @@ -9,9 +9,9 @@ stop on runlevel [!2345] respawn respawn limit 3 30 -env DJANGO_SETTINGS_MODULE={{discern_settings}} +env DJANGO_SETTINGS_MODULE={{ discern_settings }} chdir {{ discern_code_dir }} -setuid {{discern_user}} +setuid {{ discern_user }} exec {{ discern_venv_dir }}/bin/python {{ discern_code_dir }}/manage.py celeryd --loglevel=info --settings={{ discern_settings }} --pythonpath={{ discern_code_dir }} -B --autoscale={{ ansible_processor_cores * 2 }},1 diff --git a/playbooks/roles/discern/templates/discern.conf.j2 b/playbooks/roles/discern/templates/discern.conf.j2 index 8b64ce24d57..555f8712ab4 100644 --- a/playbooks/roles/discern/templates/discern.conf.j2 +++ b/playbooks/roles/discern/templates/discern.conf.j2 @@ -1,9 +1,9 @@ [program:discern] {% if ansible_processor|length > 0 %} -command={{ discern_venv_bin }}/gunicorn --preload -b {{ discern_gunicorn_host }}:{{ discern_gunicorn_port }} -w {{ ansible_processor|length * discern_worker_mult }} --timeout=30 --pythonpath={{ discern_code_dir }} discern.wsgi +command={{ discern_venv_bin }}/gunicorn --preload -b {{ discern_gunicorn_host }}:{{ discern_gunicorn_port }} -w {{ ansible_processor|length * discern_worker_mult }} --timeout=30 --pythonpath={{ discern_code_dir }} {{ DISCERN_GUNICORN_EXTRA }} discern.wsgi {% else %} -command={{ discern_venv_bin }}/gunicorn --preload -b {{ discern_gunicorn_host }}:{{ discern_gunicorn_port }} -w {{ discern_worker_mult }} --timeout=30 --pythonpath={{ discern_code_dir }} discern.wsgi +command={{ discern_venv_bin }}/gunicorn --preload -b {{ discern_gunicorn_host }}:{{ discern_gunicorn_port }} -w {{ discern_worker_mult }} --timeout=30 --pythonpath={{ discern_code_dir }} {{ DISCERN_GUNICORN_EXTRA }} discern.wsgi {% endif %} user={{ common_web_user }} directory={{ discern_code_dir }} diff --git a/playbooks/roles/edx_ansible/templates/update.j2 b/playbooks/roles/edx_ansible/templates/update.j2 index d950464ac14..893cfaa8578 100644 --- a/playbooks/roles/edx_ansible/templates/update.j2 +++ b/playbooks/roles/edx_ansible/templates/update.j2 @@ -12,7 +12,7 @@ IFS="," -v add verbosity to edx_ansible run -h this - - must be one of edx-platform, xqueue, cs_comments_service, xserver, ease, edx-ora, configuration, read-only-certificate-code edx-analytics-data-api + - must be one of edx-platform, xqueue, cs_comments_service, xserver, ease, edx-ora, configuration, read-only-certificate-code, edx-analytics-data-api - can be a commit or tag EO @@ -51,6 +51,7 @@ repos_to_cmd["configuration"]="$edx_ansible_cmd edx_ansible.yml -e 'configuratio repos_to_cmd["read-only-certificate-code"]="$edx_ansible_cmd certs.yml -e 'certs_version=$2'" repos_to_cmd["edx-analytics-data-api"]="$edx_ansible_cmd analyticsapi.yml -e 'ANALYTICS_API_VERSION=$2'" repos_to_cmd["edx-ora2"]="$edx_ansible_cmd ora2.yml -e 'ora2_version=$2'" +repos_to_cmd["insights"]="$edx_ansible_cmd insights.yml -e 'INSIGHTS_VERSION=$2'" if [[ -z $1 || -z $2 ]]; then diff --git a/playbooks/roles/edxapp/defaults/main.yml b/playbooks/roles/edxapp/defaults/main.yml index 6a60f144268..ec73b21ec50 100644 --- a/playbooks/roles/edxapp/defaults/main.yml +++ b/playbooks/roles/edxapp/defaults/main.yml @@ -27,6 +27,9 @@ EDXAPP_LMS_BASE: "" EDXAPP_PREVIEW_LMS_BASE: "" EDXAPP_CMS_BASE: "" +EDXAPP_LMS_GUNICORN_EXTRA: "" +EDXAPP_CMS_GUNICORN_EXTRA: "" + # Set this to the maximum number # of requests for gunicorn for the lms and cms # gunicorn --max-requests @@ -63,6 +66,9 @@ EDXAPP_MYSQL_REPLICA_PORT: "{{ EDXAPP_MYSQL_PORT }}" EDXAPP_MYSQL_HOST: 'localhost' EDXAPP_MYSQL_PORT: '3306' +EDXAPP_LMS_ENV: 'lms.envs.aws' +EDXAPP_CMS_ENV: 'cms.envs.aws' + EDXAPP_EMAIL_BACKEND: 'django.core.mail.backends.smtp.EmailBackend' EDXAPP_EMAIL_HOST: 'localhost' EDXAPP_EMAIL_PORT: 25 @@ -105,7 +111,7 @@ EDXAPP_CAS_ATTRIBUTE_PACKAGE: "" EDXAPP_ENABLE_AUTO_AUTH: false # Settings for enabling and configuring third party authorization EDXAPP_ENABLE_THIRD_PARTY_AUTH: false -EDXAPP_THIRD_PARTY_AUTH: "None" +EDXAPP_THIRD_PARTY_AUTH: {} EDXAPP_MODULESTORE_MAPPINGS: 'preview\.': 'draft-preferred' @@ -114,12 +120,13 @@ EDXAPP_FEATURES: AUTH_USE_OPENID_PROVIDER: true CERTIFICATES_ENABLED: true ENABLE_DISCUSSION_SERVICE: true - ENABLE_INSTRUCTOR_ANALYTICS: true + ENABLE_INSTRUCTOR_ANALYTICS: false SUBDOMAIN_BRANDING: false SUBDOMAIN_COURSE_LISTINGS: false PREVIEW_LMS_BASE: "{{ EDXAPP_PREVIEW_LMS_BASE }}" ENABLE_S3_GRADE_DOWNLOADS: true USE_CUSTOM_THEME: $edxapp_use_custom_theme + ENABLE_MKTG_SITE: $EDXAPP_ENABLE_MKTG_SITE AUTOMATIC_AUTH_FOR_TESTING: $EDXAPP_ENABLE_AUTO_AUTH ENABLE_THIRD_PARTY_AUTH: $EDXAPP_ENABLE_THIRD_PARTY_AUTH @@ -147,6 +154,11 @@ EDXAPP_LMS_PREVIEW_NGINX_PORT: 18020 EDXAPP_CMS_NGINX_PORT: 18010 EDXAPP_CMS_SSL_NGINX_PORT: 48010 +# NGINX Rate limiting related vars +EDXAPP_ENABLE_RATE_LIMITING: false +EDXAPP_COURSE_REQUEST_RATE: '5r/s' +EDXAPP_COURSE_REQUEST_BURST_RATE: 10 + EDXAPP_LANG: 'en_US.UTF-8' EDXAPP_LANGUAGE_CODE : 'en' EDXAPP_TIME_ZONE: 'America/New_York' @@ -162,9 +174,16 @@ EDXAPP_UNIVERSITY_EMAIL: 'university@example.com' EDXAPP_PRESS_EMAIL: 'press@example.com' EDXAPP_PLATFORM_TWITTER_ACCOUNT: '@YourPlatformTwitterAccount' EDXAPP_PLATFORM_FACEBOOK_ACCOUNT: 'http://www.facebook.com/YourPlatformFacebookAccount' +EDXAPP_PLATFORM_TWITTER_URL: "https://twitter.com/YourPlatformTwitterAccount" +EDXAPP_PLATFORM_MEETUP_URL: "http://www.meetup.com/YourMeetup" +EDXAPP_PLATFORM_LINKEDIN_URL: "http://www.linkedin.com/company/YourPlatform" +EDXAPP_PLATFORM_GOOGLE_PLUS_URL: "https://plus.google.com/YourGooglePlusAccount/" EDXAPP_ENV_EXTRA: {} EDXAPP_AUTH_EXTRA: {} +EDXAPP_LMS_AUTH_EXTRA: "{{ EDXAPP_AUTH_EXTRA }}" +EDXAPP_CMS_AUTH_EXTRA: "{{ EDXAPP_AUTH_EXTRA }}" +EDXAPP_ENABLE_MKTG_SITE: false EDXAPP_MKTG_URL_LINK_MAP: {} EDXAPP_MKTG_URLS: {} # Set this sets the url for static files @@ -241,6 +260,7 @@ EDXAPP_PEARSON_TEST_PASSWORD: "" EDXAPP_SEGMENT_IO_LMS: false EDXAPP_SEGMENT_IO_LMS_KEY: "" EDXAPP_OPTIMIZELY_PROJECT_ID: "None" +EDXAPP_TRACKING_SEGMENTIO_WEBHOOK_SECRET: "" # For the CMS EDXAPP_SEGMENT_IO_KEY: "" EDXAPP_SEGMENT_IO: false @@ -266,22 +286,22 @@ EDXAPP_XML_FROM_GIT: false EDXAPP_XML_S3_BUCKET: !!null EDXAPP_XML_S3_KEY: !!null -EDXAPP_NEWRELIC_LMS_APPNAME: "{{COMMON_ENVIRONMENT}}-{{COMMON_DEPLOYMENT}}-edxapp-lms" -EDXAPP_NEWRELIC_CMS_APPNAME: "{{COMMON_ENVIRONMENT}}-{{COMMON_DEPLOYMENT}}-edxapp-cms" +EDXAPP_NEWRELIC_LMS_APPNAME: "{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}-edxapp-lms" +EDXAPP_NEWRELIC_CMS_APPNAME: "{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}-edxapp-cms" EDXAPP_AWS_STORAGE_BUCKET_NAME: 'edxuploads' -EDXAPP_ORA2_FILE_PREFIX: '{{COMMON_ENVIRONMENT}}-{{COMMON_DEPLOYMENT}}/ora2' +EDXAPP_ORA2_FILE_PREFIX: '{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}/ora2' EDXAPP_FILE_UPLOAD_STORAGE_BUCKET_NAME: '{{ EDXAPP_AWS_STORAGE_BUCKET_NAME }}' EDXAPP_FILE_UPLOAD_STORAGE_PREFIX: 'submissions_attachments' EDXAPP_CODE_JAIL_LIMITS: # Limit the memory of the jailed process to something high but not - # infinite (128MiB in bytes) - VMEM: 134217728 + # infinite (512MiB in bytes) + VMEM: 536870912 # Time in seconds that the jailed process has to run. - REALTIME: 1 + REALTIME: 3 # Needs to be non-zero so that jailed code can use it as their temp directory.(1MiB in bytes) FSIZE: 1048576 @@ -295,6 +315,44 @@ EDXAPP_SUBDOMAIN_BRANDING: {} EDXAPP_WORKERS: !!null EDXAPP_ANALYTICS_DATA_TOKEN: "" EDXAPP_ANALYTICS_DATA_URL: "" +# Dashboard URL, assumes that the insights role is installed locally +EDXAPP_ANALYTICS_DASHBOARD_URL: "http://localhost:18110/courses" + +EDXAPP_REGISTRATION_EXTRA_FIELDS: + level_of_education: "optional" + gender: "optional" + year_of_birth: "optional" + mailing_address: "optional" + goals: "optional" + honor_code: "required" + city: "hidden" + country: "hidden" + +EDXAPP_CELERY_WORKERS: + - queue: low + service_variant: cms + concurrency: 3 + - queue: default + service_variant: cms + concurrency: 4 + - queue: high + service_variant: cms + concurrency: 1 + - queue: low + service_variant: lms + concurrency: 1 + - queue: default + service_variant: lms + concurrency: 3 + - queue: high + service_variant: lms + concurrency: 4 + - queue: high_mem + service_variant: lms + concurrency: 2 + +EDXAPP_DEFAULT_CACHE_VERSION: "1" + #-------- Everything below this line is internal to the role ------------ #Use YAML references (& and *) and hash merge <<: to factor out shared settings @@ -326,28 +384,7 @@ edxapp_git_ssh: "/tmp/edxapp_git_ssh.sh" # TODO: This can be removed once VPC-122 is resolved edxapp_legacy_course_data_dir: "{{ edxapp_app_dir }}/data" -edxapp_workers: - - queue: low - service_variant: cms - concurrency: 3 - - queue: default - service_variant: cms - concurrency: 4 - - queue: high - service_variant: cms - concurrency: 1 - - queue: low - service_variant: lms - concurrency: 1 - - queue: default - service_variant: lms - concurrency: 3 - - queue: high - service_variant: lms - concurrency: 4 - - queue: high_mem - service_variant: lms - concurrency: 2 +edxapp_workers: "{{ EDXAPP_CELERY_WORKERS }}" # setup for python codejail edxapp_sandbox_venv_dir: '{{ edxapp_venvs_dir }}/edxapp-sandbox' @@ -384,30 +421,30 @@ edxapp_all_req_files: # for lists and dictionaries edxapp_environment: - LANG: $EDXAPP_LANG - NO_PREREQ_INSTALL: $EDXAPP_NO_PREREQ_INSTALL + LANG: "{{ EDXAPP_LANG }}" + NO_PREREQ_INSTALL: "{{ EDXAPP_NO_PREREQ_INSTALL }}" SKIP_WS_MIGRATIONS: 1 - RBENV_ROOT: $edxapp_rbenv_root - GEM_HOME: $edxapp_gem_root - GEM_PATH: $edxapp_gem_root - PATH: $edxapp_deploy_path + RBENV_ROOT: "{{ edxapp_rbenv_root }}" + GEM_HOME: "{{ edxapp_gem_root }}" + GEM_PATH: "{{ edxapp_gem_root }}" + PATH: "{{ edxapp_deploy_path }}" edxapp_generic_auth_config: &edxapp_generic_auth - ANALYTICS_DATA_TOKEN: $EDXAPP_ANALYTICS_DATA_TOKEN - AWS_ACCESS_KEY_ID: $EDXAPP_AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY: $EDXAPP_AWS_SECRET_ACCESS_KEY - SECRET_KEY: $EDXAPP_EDXAPP_SECRET_KEY + ANALYTICS_DATA_TOKEN: "{{ EDXAPP_ANALYTICS_DATA_TOKEN }}" + AWS_ACCESS_KEY_ID: "{{ EDXAPP_AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "{{ EDXAPP_AWS_SECRET_ACCESS_KEY }}" + SECRET_KEY: "{{ EDXAPP_EDXAPP_SECRET_KEY }}" XQUEUE_INTERFACE: - basic_auth: $EDXAPP_XQUEUE_BASIC_AUTH - django_auth: $EDXAPP_XQUEUE_DJANGO_AUTH - url: $EDXAPP_XQUEUE_URL + basic_auth: "{{ EDXAPP_XQUEUE_BASIC_AUTH }}" + django_auth: "{{ EDXAPP_XQUEUE_DJANGO_AUTH }}" + url: "{{ EDXAPP_XQUEUE_URL }}" DOC_STORE_CONFIG: &edxapp_generic_default_docstore - db: $EDXAPP_MONGO_DB_NAME - host: $EDXAPP_MONGO_HOSTS - password: $EDXAPP_MONGO_PASSWORD + db: "{{ EDXAPP_MONGO_DB_NAME }}" + host: "{{ EDXAPP_MONGO_HOSTS }}" + password: "{{ EDXAPP_MONGO_PASSWORD }}" port: $EDXAPP_MONGO_PORT - user: $EDXAPP_MONGO_USER + user: "{{ EDXAPP_MONGO_USER }}" collection: 'modulestore' CONTENTSTORE: ENGINE: 'xmodule.contentstore.mongo.MongoContentStore' @@ -416,18 +453,18 @@ edxapp_generic_auth_config: &edxapp_generic_auth # backward compatibility # OPTIONS: - db: $EDXAPP_MONGO_DB_NAME - host: $EDXAPP_MONGO_HOSTS - password: $EDXAPP_MONGO_PASSWORD + db: "{{ EDXAPP_MONGO_DB_NAME }}" + host: "{{ EDXAPP_MONGO_HOSTS }}" + password: "{{ EDXAPP_MONGO_PASSWORD }}" port: $EDXAPP_MONGO_PORT - user: $EDXAPP_MONGO_USER - ADDITIONAL_OPTIONS: $EDXAPP_CONTENTSTORE_ADDITIONAL_OPTS + user: "{{ EDXAPP_MONGO_USER }}" + ADDITIONAL_OPTIONS: "{{ EDXAPP_CONTENTSTORE_ADDITIONAL_OPTS }}" DOC_STORE_CONFIG: *edxapp_generic_default_docstore MODULESTORE: default: ENGINE: 'xmodule.modulestore.mixed.MixedModuleStore' OPTIONS: - mappings: $EDXAPP_XML_MAPPINGS + mappings: "{{ EDXAPP_XML_MAPPINGS }}" stores: - &edxapp_generic_draft_modulestore NAME: 'draft' @@ -435,13 +472,13 @@ edxapp_generic_auth_config: &edxapp_generic_auth DOC_STORE_CONFIG: *edxapp_generic_default_docstore OPTIONS: default_class: 'xmodule.hidden_module.HiddenDescriptor' - fs_root: $edxapp_course_data_dir + fs_root: "{{ edxapp_course_data_dir }}" render_template: 'edxmako.shortcuts.render_to_string' - &edxapp_generic_xml_modulestore NAME: 'xml' ENGINE: 'xmodule.modulestore.xml.XMLModuleStore' OPTIONS: - data_dir: $edxapp_course_data_dir + data_dir: "{{ edxapp_course_data_dir }}" default_class: 'xmodule.hidden_module.HiddenDescriptor' - &edxapp_generic_split_modulestore NAME: 'split' @@ -449,86 +486,96 @@ edxapp_generic_auth_config: &edxapp_generic_auth DOC_STORE_CONFIG: *edxapp_generic_default_docstore OPTIONS: default_class: 'xmodule.hidden_module.HiddenDescriptor' - fs_root: $edxapp_course_data_dir + fs_root: "{{ edxapp_course_data_dir }}" render_template: 'edxmako.shortcuts.render_to_string' DATABASES: read_replica: ENGINE: 'django.db.backends.mysql' - NAME: $EDXAPP_MYSQL_REPLICA_DB_NAME - USER: $EDXAPP_MYSQL_REPLICA_USER - PASSWORD: $EDXAPP_MYSQL_REPLICA_PASSWORD - HOST: $EDXAPP_MYSQL_REPLICA_HOST - PORT: $EDXAPP_MYSQL_REPLICA_PORT + NAME: "{{ EDXAPP_MYSQL_REPLICA_DB_NAME }}" + USER: "{{ EDXAPP_MYSQL_REPLICA_USER }}" + PASSWORD: "{{ EDXAPP_MYSQL_REPLICA_PASSWORD }}" + HOST: "{{ EDXAPP_MYSQL_REPLICA_HOST }}" + PORT: "{{ EDXAPP_MYSQL_REPLICA_PORT }}" default: ENGINE: 'django.db.backends.mysql' - NAME: $EDXAPP_MYSQL_DB_NAME - USER: $EDXAPP_MYSQL_USER - PASSWORD: $EDXAPP_MYSQL_PASSWORD - HOST: $EDXAPP_MYSQL_HOST - PORT: $EDXAPP_MYSQL_PORT + NAME: "{{ EDXAPP_MYSQL_DB_NAME }}" + USER: "{{ EDXAPP_MYSQL_USER }}" + PASSWORD: "{{ EDXAPP_MYSQL_PASSWORD }}" + HOST: "{{ EDXAPP_MYSQL_HOST }}" + PORT: "{{ EDXAPP_MYSQL_PORT }}" OPEN_ENDED_GRADING_INTERFACE: - url: $EDXAPP_OEE_URL - password: $EDXAPP_OEE_PASSWORD + url: "{{ EDXAPP_OEE_URL }}" + password: "{{ EDXAPP_OEE_PASSWORD }}" peer_grading: 'peer_grading' staff_grading: 'staff_grading' grading_controller: 'grading_controller' - username: $EDXAPP_OEE_USER - ANALYTICS_API_KEY: $EDXAPP_ANALYTICS_API_KEY - EMAIL_HOST_USER: $EDXAPP_EMAIL_HOST_USER - EMAIL_HOST_PASSWORD: $EDXAPP_EMAIL_HOST_PASSWORD - ZENDESK_USER: $EDXAPP_ZENDESK_USER - ZENDESK_API_KEY: $EDXAPP_ZENDESK_API_KEY - CELERY_BROKER_USER: $EDXAPP_CELERY_USER - CELERY_BROKER_PASSWORD: $EDXAPP_CELERY_PASSWORD - GOOGLE_ANALYTICS_ACCOUNT: $EDXAPP_GOOGLE_ANALYTICS_ACCOUNT - THIRD_PARTY_AUTH: $EDXAPP_THIRD_PARTY_AUTH + username: "{{ EDXAPP_OEE_USER }}" + ANALYTICS_API_KEY: "{{ EDXAPP_ANALYTICS_API_KEY }}" + EMAIL_HOST_USER: "{{ EDXAPP_EMAIL_HOST_USER }}" + EMAIL_HOST_PASSWORD: "{{ EDXAPP_EMAIL_HOST_PASSWORD }}" + ZENDESK_USER: "{{ EDXAPP_ZENDESK_USER }}" + ZENDESK_API_KEY: "{{ EDXAPP_ZENDESK_API_KEY }}" + CELERY_BROKER_USER: "{{ EDXAPP_CELERY_USER }}" + CELERY_BROKER_PASSWORD: "{{ EDXAPP_CELERY_PASSWORD }}" + GOOGLE_ANALYTICS_ACCOUNT: "{{ EDXAPP_GOOGLE_ANALYTICS_ACCOUNT }}" + THIRD_PARTY_AUTH: "{{ EDXAPP_THIRD_PARTY_AUTH }}" AWS_STORAGE_BUCKET_NAME: "{{ EDXAPP_AWS_STORAGE_BUCKET_NAME }}" + DJFS: $EDXAPP_DJFS + +generic_cache_config: &default_generic_cache + BACKEND: 'django.core.cache.backends.memcached.MemcachedCache' + KEY_FUNCTION: 'util.memcache.safe_key' + KEY_PREFIX: 'default' + LOCATION: "{{ EDXAPP_MEMCACHE }}" generic_env_config: &edxapp_generic_env - ANALYTICS_DATA_URL: $EDXAPP_ANALYTICS_DATA_URL - CELERY_BROKER_VHOST: $EDXAPP_CELERY_BROKER_VHOST - PAYMENT_SUPPORT_EMAIL: $EDXAPP_PAYMENT_SUPPORT_EMAIL - ZENDESK_URL: $EDXAPP_ZENDESK_URL - COURSES_WITH_UNSAFE_CODE: $EDXAPP_COURSES_WITH_UNSAFE_CODE + OAUTH_OIDC_ISSUER: "https://{{ EDXAPP_LMS_BASE }}/oauth2" + XBLOCK_FS_STORAGE_BUCKET: "{{ EDXAPP_XBLOCK_FS_STORAGE_BUCKET }}" + XBLOCK_FS_STORAGE_PREFIX: "{{ EDXAPP_XBLOCK_FS_STORAGE_PREFIX }}" + ANALYTICS_DATA_URL: "{{ EDXAPP_ANALYTICS_DATA_URL }}" + ANALYTICS_DASHBOARD_URL: '{{ EDXAPP_ANALYTICS_DASHBOARD_URL }}' + CELERY_BROKER_VHOST: "{{ EDXAPP_CELERY_BROKER_VHOST }}" + PAYMENT_SUPPORT_EMAIL: "{{ EDXAPP_PAYMENT_SUPPORT_EMAIL }}" + ZENDESK_URL: "{{ EDXAPP_ZENDESK_URL }}" + COURSES_WITH_UNSAFE_CODE: "{{ EDXAPP_COURSES_WITH_UNSAFE_CODE }}" BULK_EMAIL_EMAILS_PER_TASK: $EDXAPP_BULK_EMAIL_EMAILS_PER_TASK - MICROSITE_ROOT_DIR: $EDXAPP_MICROSITE_ROOT_DIR + MICROSITE_ROOT_DIR: "{{ EDXAPP_MICROSITE_ROOT_DIR }}" MICROSITE_CONFIGURATION: $EDXAPP_MICROSITE_CONFIGURATION GRADES_DOWNLOAD: - STORAGE_TYPE: $EDXAPP_GRADE_STORAGE_TYPE - BUCKET: $EDXAPP_GRADE_BUCKET - ROOT_PATH: $EDXAPP_GRADE_ROOT_PATH - STATIC_URL_BASE: $EDXAPP_STATIC_URL_BASE - STATIC_ROOT_BASE: $edxapp_staticfile_dir - LMS_BASE: $EDXAPP_LMS_BASE - CMS_BASE: $EDXAPP_CMS_BASE - BOOK_URL: $EDXAPP_BOOK_URL - PLATFORM_NAME: $EDXAPP_PLATFORM_NAME + STORAGE_TYPE: "{{ EDXAPP_GRADE_STORAGE_TYPE }}" + BUCKET: "{{ EDXAPP_GRADE_BUCKET }}" + ROOT_PATH: "{{ EDXAPP_GRADE_ROOT_PATH }}" + STATIC_URL_BASE: "{{ EDXAPP_STATIC_URL_BASE }}" + STATIC_ROOT_BASE: "{{ edxapp_staticfile_dir }}" + LMS_BASE: "{{ EDXAPP_LMS_BASE }}" + CMS_BASE: "{{ EDXAPP_CMS_BASE }}" + BOOK_URL: "{{ EDXAPP_BOOK_URL }}" + PLATFORM_NAME: "{{ EDXAPP_PLATFORM_NAME }}" CERT_QUEUE: 'certificates' - LOCAL_LOGLEVEL: $EDXAPP_LOG_LEVEL + LOCAL_LOGLEVEL: "{{ EDXAPP_LOG_LEVEL }}" # default email backed set to local SMTP - EMAIL_BACKEND: $EDXAPP_EMAIL_BACKEND - EMAIL_HOST: $EDXAPP_EMAIL_HOST + EMAIL_BACKEND: "{{ EDXAPP_EMAIL_BACKEND }}" + EMAIL_HOST: "{{ EDXAPP_EMAIL_HOST }}" EMAIL_PORT: $EDXAPP_EMAIL_PORT EMAIL_USE_TLS: $EDXAPP_EMAIL_USE_TLS - FEATURES: $EDXAPP_FEATURES + FEATURES: "{{ EDXAPP_FEATURES }}" WIKI_ENABLED: true - SYSLOG_SERVER: $EDXAPP_SYSLOG_SERVER + SYSLOG_SERVER: "{{ EDXAPP_SYSLOG_SERVER }}" LOG_DIR: "{{ COMMON_DATA_DIR }}/logs/edx" - MEDIA_URL: $EDXAPP_MEDIA_URL - ANALYTICS_SERVER_URL: $EDXAPP_ANALYTICS_SERVER_URL - FEEDBACK_SUBMISSION_EMAIL: $EDXAPP_FEEDBACK_SUBMISSION_EMAIL - TIME_ZONE: $EDXAPP_TIME_ZONE - LANGUAGE_CODE : $EDXAPP_LANGUAGE_CODE - MKTG_URL_LINK_MAP: $EDXAPP_MKTG_URL_LINK_MAP - MKTG_URLS: $EDXAPP_MKTG_URLS + MEDIA_URL: "{{ EDXAPP_MEDIA_URL }}" + ANALYTICS_SERVER_URL: "{{ EDXAPP_ANALYTICS_SERVER_URL }}" + FEEDBACK_SUBMISSION_EMAIL: "{{ EDXAPP_FEEDBACK_SUBMISSION_EMAIL }}" + TIME_ZONE: "{{ EDXAPP_TIME_ZONE }}" + LANGUAGE_CODE: "{{ EDXAPP_LANGUAGE_CODE }}" + MKTG_URL_LINK_MAP: "{{ EDXAPP_MKTG_URL_LINK_MAP }}" + MKTG_URLS: "{{ EDXAPP_MKTG_URLS }}" # repo root for courses - GITHUB_REPO_ROOT: $edxapp_course_data_dir + GITHUB_REPO_ROOT: "{{ edxapp_course_data_dir }}" CACHES: - default: &default_generic_cache - BACKEND: 'django.core.cache.backends.memcached.MemcachedCache' - KEY_FUNCTION: 'util.memcache.safe_key' + default: + <<: *default_generic_cache KEY_PREFIX: 'default' - LOCATION: $EDXAPP_MEMCACHE + VERSION: "{{ EDXAPP_DEFAULT_CACHE_VERSION }}" general: <<: *default_generic_cache KEY_PREFIX: 'general' @@ -544,67 +591,72 @@ generic_env_config: &edxapp_generic_env KEY_PREFIX: 'celery' TIMEOUT: "7200" CELERY_BROKER_TRANSPORT: 'amqp' - CELERY_BROKER_HOSTNAME: $EDXAPP_RABBIT_HOSTNAME - COMMENTS_SERVICE_URL: $EDXAPP_COMMENTS_SERVICE_URL - LOGGING_ENV: $EDXAPP_LOGGING_ENV - SESSION_COOKIE_DOMAIN: $EDXAPP_SESSION_COOKIE_DOMAIN - SESSION_COOKIE_NAME: $EDXAPP_SESSION_COOKIE_NAME - COMMENTS_SERVICE_KEY: $EDXAPP_COMMENTS_SERVICE_KEY + CELERY_BROKER_HOSTNAME: "{{ EDXAPP_RABBIT_HOSTNAME }}" + COMMENTS_SERVICE_URL: "{{ EDXAPP_COMMENTS_SERVICE_URL }}" + LOGGING_ENV: "{{ EDXAPP_LOGGING_ENV }}" + SESSION_COOKIE_DOMAIN: "{{ EDXAPP_SESSION_COOKIE_DOMAIN }}" + SESSION_COOKIE_NAME: "{{ EDXAPP_SESSION_COOKIE_NAME }}" + COMMENTS_SERVICE_KEY: "{{ EDXAPP_COMMENTS_SERVICE_KEY }}" SEGMENT_IO_LMS: $EDXAPP_SEGMENT_IO_LMS SEGMENT_IO: $EDXAPP_SEGMENT_IO - THEME_NAME: $edxapp_theme_name - TECH_SUPPORT_EMAIL: $EDXAPP_TECH_SUPPORT_EMAIL - CONTACT_EMAIL: $EDXAPP_CONTACT_EMAIL - BUGS_EMAIL: $EDXAPP_BUGS_EMAIL - DEFAULT_FROM_EMAIL: $EDXAPP_DEFAULT_FROM_EMAIL - DEFAULT_FEEDBACK_EMAIL: $EDXAPP_DEFAULT_FEEDBACK_EMAIL - SERVER_EMAIL: $EDXAPP_DEFAULT_SERVER_EMAIL - BULK_EMAIL_DEFAULT_FROM_EMAIL: $EDXAPP_BULK_EMAIL_DEFAULT_FROM_EMAIL - CAS_SERVER_URL: $EDXAPP_CAS_SERVER_URL - CAS_EXTRA_LOGIN_PARAMS: $EDXAPP_CAS_EXTRA_LOGIN_PARAMS - CAS_ATTRIBUTE_CALLBACK: $EDXAPP_CAS_ATTRIBUTE_CALLBACK + THEME_NAME: "{{ edxapp_theme_name }}" + TECH_SUPPORT_EMAIL: "{{ EDXAPP_TECH_SUPPORT_EMAIL }}" + CONTACT_EMAIL: "{{ EDXAPP_CONTACT_EMAIL }}" + BUGS_EMAIL: "{{ EDXAPP_BUGS_EMAIL }}" + DEFAULT_FROM_EMAIL: "{{ EDXAPP_DEFAULT_FROM_EMAIL }}" + DEFAULT_FEEDBACK_EMAIL: "{{ EDXAPP_DEFAULT_FEEDBACK_EMAIL }}" + SERVER_EMAIL: "{{ EDXAPP_DEFAULT_SERVER_EMAIL }}" + BULK_EMAIL_DEFAULT_FROM_EMAIL: "{{ EDXAPP_BULK_EMAIL_DEFAULT_FROM_EMAIL }}" + CAS_SERVER_URL: "{{ EDXAPP_CAS_SERVER_URL }}" + CAS_EXTRA_LOGIN_PARAMS: "{{ EDXAPP_CAS_EXTRA_LOGIN_PARAMS }}" + CAS_ATTRIBUTE_CALLBACK: "{{ EDXAPP_CAS_ATTRIBUTE_CALLBACK }}" HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS: "{{ EDXAPP_MODULESTORE_MAPPINGS }}" - UNIVERSITY_EMAIL: $EDXAPP_UNIVERSITY_EMAIL - PRESS_EMAIL: $EDXAPP_PRESS_EMAIL - PLATFORM_TWITTER_ACCOUNT: $EDXAPP_PLATFORM_TWITTER_ACCOUNT - PLATFORM_FACEBOOK_ACCOUNT: $EDXAPP_PLATFORM_FACEBOOK_ACCOUNT - ORA2_FILE_PREFIX: $EDXAPP_ORA2_FILE_PREFIX - FILE_UPLOAD_STORAGE_BUCKET_NAME: $EDXAPP_FILE_UPLOAD_STORAGE_BUCKET_NAME - FILE_UPLOAD_STORAGE_PREFIX: $EDXAPP_FILE_UPLOAD_STORAGE_PREFIX - VIRTUAL_UNIVERSITIES: $EDXAPP_VIRTUAL_UNIVERSITIES - SUBDOMAIN_BRANDING: $EDXAPP_SUBDOMAIN_BRANDING - + UNIVERSITY_EMAIL: "{{ EDXAPP_UNIVERSITY_EMAIL }}" + PRESS_EMAIL: "{{ EDXAPP_PRESS_EMAIL }}" + PLATFORM_TWITTER_ACCOUNT: "{{ EDXAPP_PLATFORM_TWITTER_ACCOUNT }}" + PLATFORM_FACEBOOK_ACCOUNT: "{{ EDXAPP_PLATFORM_FACEBOOK_ACCOUNT }}" + PLATFORM_TWITTER_URL: "{{ EDXAPP_PLATFORM_TWITTER_URL }}" + PLATFORM_MEETUP_URL: "{{ EDXAPP_PLATFORM_MEETUP_URL }}" + PLATFORM_LINKEDIN_URL: "{{ EDXAPP_PLATFORM_LINKEDIN_URL }}" + PLATFORM_GOOGLE_PLUS_URL: "{{ EDXAPP_PLATFORM_GOOGLE_PLUS_URL }}" + ORA2_FILE_PREFIX: "{{ EDXAPP_ORA2_FILE_PREFIX }}" + FILE_UPLOAD_STORAGE_BUCKET_NAME: "{{ EDXAPP_FILE_UPLOAD_STORAGE_BUCKET_NAME }}" + FILE_UPLOAD_STORAGE_PREFIX: "{{ EDXAPP_FILE_UPLOAD_STORAGE_PREFIX }}" + VIRTUAL_UNIVERSITIES: "{{ EDXAPP_VIRTUAL_UNIVERSITIES }}" + SUBDOMAIN_BRANDING: "{{ EDXAPP_SUBDOMAIN_BRANDING }}" + REGISTRATION_EXTRA_FIELDS: "{{ EDXAPP_REGISTRATION_EXTRA_FIELDS }}" lms_auth_config: <<: *edxapp_generic_auth - PEARSON_TEST_PASSWORD: $EDXAPP_PEARSON_TEST_PASSWORD - SEGMENT_IO_LMS_KEY: $EDXAPP_SEGMENT_IO_LMS_KEY - OPTIMIZELY_PROJECT_ID: $EDXAPP_OPTIMIZELY_PROJECT_ID - EDX_API_KEY: $EDXAPP_EDX_API_KEY - VERIFY_STUDENT: $EDXAPP_VERIFY_STUDENT - GOOGLE_ANALYTICS_LINKEDIN: $EDXAPP_GOOGLE_ANALYTICS_LINKEDIN - CC_PROCESSOR_NAME: $EDXAPP_CC_PROCESSOR_NAME - CC_PROCESSOR: $EDXAPP_CC_PROCESSOR + PEARSON_TEST_PASSWORD: "{{ EDXAPP_PEARSON_TEST_PASSWORD }}" + SEGMENT_IO_LMS_KEY: "{{ EDXAPP_SEGMENT_IO_LMS_KEY }}" + OPTIMIZELY_PROJECT_ID: "{{ EDXAPP_OPTIMIZELY_PROJECT_ID }}" + EDX_API_KEY: "{{ EDXAPP_EDX_API_KEY }}" + VERIFY_STUDENT: "{{ EDXAPP_VERIFY_STUDENT }}" + GOOGLE_ANALYTICS_LINKEDIN: "{{ EDXAPP_GOOGLE_ANALYTICS_LINKEDIN }}" + CC_PROCESSOR_NAME: "{{ EDXAPP_CC_PROCESSOR_NAME }}" + CC_PROCESSOR: "{{ EDXAPP_CC_PROCESSOR }}" + TRACKING_SEGMENTIO_WEBHOOK_SECRET: "{{ EDXAPP_TRACKING_SEGMENTIO_WEBHOOK_SECRET }}" lms_env_config: <<: *edxapp_generic_env PAID_COURSE_REGISTRATION_CURRENCY: $EDXAPP_PAID_COURSE_REGISTRATION_CURRENCY - SITE_NAME: $EDXAPP_LMS_SITE_NAME - VIDEO_CDN_URL: $EDXAPP_VIDEO_CDN_URLS + SITE_NAME: "{{ EDXAPP_LMS_SITE_NAME }}" + VIDEO_CDN_URL: "{{ EDXAPP_VIDEO_CDN_URLS }}" CODE_JAIL: # from https://github.com/edx/codejail/blob/master/codejail/django_integration.py#L24, '' should be same as None python_bin: '{% if EDXAPP_PYTHON_SANDBOX %}{{ edxapp_sandbox_venv_dir }}/bin/python{% endif %}' - limits: $EDXAPP_CODE_JAIL_LIMITS + limits: "{{ EDXAPP_CODE_JAIL_LIMITS }}" user: '{{ edxapp_sandbox_user }}' cms_auth_config: <<: *edxapp_generic_auth - SEGMENT_IO_KEY: $EDXAPP_SEGMENT_IO_KEY + SEGMENT_IO_KEY: "{{ EDXAPP_SEGMENT_IO_KEY }}" MODULESTORE: default: ENGINE: 'xmodule.modulestore.mixed.MixedModuleStore' OPTIONS: # See commented section below. LMS-11258 -# mappings: $EDXAPP_XML_MAPPINGS +# mappings: "{{ EDXAPP_XML_MAPPINGS }}" mappings: {} stores: - *edxapp_generic_draft_modulestore @@ -613,7 +665,7 @@ cms_auth_config: - *edxapp_generic_split_modulestore cms_env_config: <<: *edxapp_generic_env - SITE_NAME: $EDXAPP_CMS_SITE_NAME + SITE_NAME: "{{ EDXAPP_CMS_SITE_NAME }}" # install dir for the edx-platform repo edxapp_code_dir: "{{ edxapp_app_dir }}/edx-platform" @@ -635,9 +687,6 @@ service_variants_enabled: - lms - cms -edxapp_lms_env: 'lms.envs.aws' -edxapp_cms_env: 'cms.envs.aws' - #Number of gunicorn worker processes to spawn, as a multiplier to number of virtual cores worker_core_mult: lms: 4 diff --git a/playbooks/roles/edxapp/tasks/deploy.yml b/playbooks/roles/edxapp/tasks/deploy.yml index f6881ac5a42..77eaf4e5e9d 100644 --- a/playbooks/roles/edxapp/tasks/deploy.yml +++ b/playbooks/roles/edxapp/tasks/deploy.yml @@ -27,11 +27,11 @@ when: EDXAPP_USE_GIT_IDENTITY # Do A Checkout -- name: checkout edx-platform repo into {{edxapp_code_dir}} +- name: checkout edx-platform repo into {{ edxapp_code_dir }} git: > - dest={{edxapp_code_dir}} - repo={{edx_platform_repo}} - version={{edx_platform_version}} + dest={{ edxapp_code_dir }} + repo={{ edx_platform_repo }} + version={{ edx_platform_version }} accept_hostkey=yes sudo_user: "{{ edxapp_user }}" environment: @@ -42,7 +42,7 @@ - "restart edxapp_workers" - name: git clean after checking out edx-platform - shell: cd {{edxapp_code_dir}} && git clean -xdf + shell: cd {{ edxapp_code_dir }} && git clean -xdf sudo_user: "{{ edxapp_user }}" notify: - "restart edxapp" @@ -50,9 +50,9 @@ - name: checkout theme git: > - dest={{ edxapp_app_dir }}/themes/{{edxapp_theme_name}} - repo={{edxapp_theme_source_repo}} - version={{edxapp_theme_version}} + dest={{ edxapp_app_dir }}/themes/{{ edxapp_theme_name }} + repo={{ edxapp_theme_source_repo }} + version={{ edxapp_theme_version }} accept_hostkey=yes when: edxapp_theme_name != '' sudo_user: "{{ edxapp_user }}" @@ -91,8 +91,8 @@ - name: gem install bundler shell: > gem install bundle - chdir={{ edxapp_code_dir }} - executable=/bin/bash + chdir={{ edxapp_code_dir }} + executable=/bin/bash environment: "{{ edxapp_environment }}" sudo_user: "{{ edxapp_user }}" notify: @@ -102,8 +102,8 @@ - name: bundle install shell: > bundle install --binstubs - chdir={{ edxapp_code_dir }} - executable=/bin/bash + chdir={{ edxapp_code_dir }} + executable=/bin/bash sudo_user: "{{ edxapp_user }}" environment: "{{ edxapp_environment }}" notify: @@ -144,8 +144,8 @@ # Install the python pre requirements into {{ edxapp_venv_dir }} - name : install python pre-requirements pip: > - requirements="{{pre_requirements_file}}" - virtualenv="{{edxapp_venv_dir}}" + requirements="{{ pre_requirements_file }}" + virtualenv="{{ edxapp_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ edxapp_user }}" @@ -173,8 +173,8 @@ # Install the python post requirements into {{ edxapp_venv_dir }} - name : install python post-requirements pip: > - requirements="{{post_requirements_file}}" - virtualenv="{{edxapp_venv_dir}}" + requirements="{{ post_requirements_file }}" + virtualenv="{{ edxapp_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ edxapp_user }}" @@ -187,8 +187,8 @@ # Install the python paver requirements into {{ edxapp_venv_dir }} - name : install python paver-requirements pip: > - requirements="{{paver_requirements_file}}" - virtualenv="{{edxapp_venv_dir}}" + requirements="{{ paver_requirements_file }}" + virtualenv="{{ edxapp_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ edxapp_user }}" @@ -257,7 +257,7 @@ - name: install CAS attribute module pip: > name="{{ EDXAPP_CAS_ATTRIBUTE_PACKAGE }}" - virtualenv="{{edxapp_venv_dir}}" + virtualenv="{{ edxapp_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }} --exists-action w --use-mirrors" sudo_user: "{{ edxapp_user }}" @@ -294,8 +294,8 @@ - name: code sandbox | Install base sandbox requirements and create sandbox virtualenv pip: > - requirements="{{sandbox_base_requirements}}" - virtualenv="{{edxapp_sandbox_venv_dir}}" + requirements="{{ sandbox_base_requirements }}" + virtualenv="{{ edxapp_sandbox_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }} --exists-action w --use-mirrors" sudo_user: "{{ edxapp_sandbox_user }}" diff --git a/playbooks/roles/edxapp/tasks/main.yml b/playbooks/roles/edxapp/tasks/main.yml index 53530096e67..8f0b1b7442e 100644 --- a/playbooks/roles/edxapp/tasks/main.yml +++ b/playbooks/roles/edxapp/tasks/main.yml @@ -26,6 +26,7 @@ - "{{ edxapp_theme_dir }}" - "{{ edxapp_staticfile_dir }}" - "{{ edxapp_course_static_dir }}" + - "{{ edxapp_course_data_dir }}" # This is a symlink that has to exist because # we currently can't override the DATA_DIR var @@ -38,7 +39,6 @@ state=link owner="{{ edxapp_user }}" group="{{ common_web_group }}" - - name: create edxapp log dir file: > @@ -70,6 +70,12 @@ - "restart edxapp" - "restart edxapp_workers" +- name: set up edxapp .npmrc + template: + src=.npmrc.j2 dest={{ edxapp_app_dir }}/.npmrc + owner={{ edxapp_user }} group={{ common_web_group }} + mode=0600 + - name: create log directories for service variants notify: - "restart edxapp" @@ -81,7 +87,7 @@ with_items: service_variants_enabled # Set up the python sandbox execution environment -- include: python_sandbox_env.yml +- include: python_sandbox_env.yml tags=deploy when: EDXAPP_PYTHON_SANDBOX - include: deploy.yml tags=deploy diff --git a/playbooks/roles/edxapp/tasks/python_sandbox_env.yml b/playbooks/roles/edxapp/tasks/python_sandbox_env.yml index 9d144ad709f..e582e579bd5 100644 --- a/playbooks/roles/edxapp/tasks/python_sandbox_env.yml +++ b/playbooks/roles/edxapp/tasks/python_sandbox_env.yml @@ -1,3 +1,13 @@ +# Set the alternatives this way for blas and lapack to work correctly for the +# MITx 6.341x course. +# TODO: Switch to using alternatives module in 1.6 +- name: code sandbox | Use libblas for 3gf + command: update-alternatives --set libblas.so.3gf /usr/lib/libblas/libblas.so.3gf + +# TODO: Switch to using alternatives module in 1.6 +- name: code sandbox | Use liblapac for 3gf + command: update-alternatives --set liblapack.so.3gf /usr/lib/lapack/liblapack.so.3gf + - name: code sandbox | Create edxapp sandbox user user: name={{ edxapp_sandbox_user }} shell=/bin/false home={{ edxapp_sandbox_venv_dir }} notify: diff --git a/playbooks/roles/edxapp/tasks/xml.yml b/playbooks/roles/edxapp/tasks/xml.yml index 988bf867798..bb15266ee4f 100644 --- a/playbooks/roles/edxapp/tasks/xml.yml +++ b/playbooks/roles/edxapp/tasks/xml.yml @@ -1,3 +1,11 @@ +- name: make the course data updatable by the edxapp user + file: + path="{{ edxapp_course_data_dir }}" + state=directory + recurse=yes + owner="{{ edxapp_user }}" + group="{{ edxapp_user }}" + - name: clone the xml course repo git: > repo="{{ item.repo_url }}" diff --git a/playbooks/roles/edxapp/templates/.npmrc.j2 b/playbooks/roles/edxapp/templates/.npmrc.j2 new file mode 100644 index 00000000000..3a82b3419dd --- /dev/null +++ b/playbooks/roles/edxapp/templates/.npmrc.j2 @@ -0,0 +1 @@ +registry={{ COMMON_NPM_MIRROR_URL }} diff --git a/playbooks/roles/edxapp/templates/95-sandbox-sudoer.j2 b/playbooks/roles/edxapp/templates/95-sandbox-sudoer.j2 index 71f624335a0..b291add83a0 100644 --- a/playbooks/roles/edxapp/templates/95-sandbox-sudoer.j2 +++ b/playbooks/roles/edxapp/templates/95-sandbox-sudoer.j2 @@ -1,11 +1,11 @@ {% if devstack %} {{ edxapp_user }} ALL=({{ edxapp_sandbox_user }}) SETENV:NOPASSWD:{{ edxapp_sandbox_venv_dir }}/bin/python -{{ edxapp_user }} ALL=({{ edxapp_sandbox_user }}) SETENV:NOPASSWD:/bin/rm /tmp/codejail-*/tmp +{{ edxapp_user }} ALL=({{ edxapp_sandbox_user }}) SETENV:NOPASSWD:/usr/bin/find /tmp/codejail-*/tmp -mindepth 1 -maxdepth 1 -exec rm -rf {} ; {{ edxapp_user }} ALL=(ALL) NOPASSWD:/bin/kill {{ edxapp_user }} ALL=(ALL) NOPASSWD:/usr/bin/pkill {% else %} {{ common_web_user }} ALL=({{ edxapp_sandbox_user }}) SETENV:NOPASSWD:{{ edxapp_sandbox_venv_dir }}/bin/python -{{ common_web_user }} ALL=({{ edxapp_sandbox_user }}) SETENV:NOPASSWD:/bin/rm /tmp/codejail-*/tmp +{{ common_web_user }} ALL=({{ edxapp_sandbox_user }}) SETENV:NOPASSWD:/usr/bin/find /tmp/codejail-*/tmp -mindepth 1 -maxdepth 1 -exec rm -rf {} ; {{ common_web_user }} ALL=(ALL) NOPASSWD:/bin/kill {{ common_web_user }} ALL=(ALL) NOPASSWD:/usr/bin/pkill {% endif %} diff --git a/playbooks/roles/edxapp/templates/cms.auth.json.j2 b/playbooks/roles/edxapp/templates/cms.auth.json.j2 index 07bb9f00e09..b6cb39bb5cd 100644 --- a/playbooks/roles/edxapp/templates/cms.auth.json.j2 +++ b/playbooks/roles/edxapp/templates/cms.auth.json.j2 @@ -1,4 +1,4 @@ -{% do cms_auth_config.update(EDXAPP_AUTH_EXTRA) %} +{% do cms_auth_config.update(EDXAPP_CMS_AUTH_EXTRA) %} {% for key, value in cms_auth_config.iteritems() %} {% if value == 'None' %} {% do cms_auth_config.update({key: None }) %} diff --git a/playbooks/roles/edxapp/templates/cms.conf.j2 b/playbooks/roles/edxapp/templates/cms.conf.j2 index b9657559622..cb888fb8c17 100644 --- a/playbooks/roles/edxapp/templates/cms.conf.j2 +++ b/playbooks/roles/edxapp/templates/cms.conf.j2 @@ -16,15 +16,15 @@ command={{ executable }} {{ max_req }} --preload -b {{ edxapp_cms_gunicorn_host {% else -%} {# This is for backwards compatibility, set workers explicitely using EDXAPP_WORKERS #} {% if ansible_processor|length > 0 -%} -command={{ executable }} {{ max_req }} --preload -b {{ edxapp_cms_gunicorn_host }}:{{ edxapp_cms_gunicorn_port }} -w {{ ansible_processor|length * worker_core_mult.cms }} --timeout=300 --pythonpath={{ edxapp_code_dir }} cms.wsgi +command={{ executable }} {{ max_req }} --preload -b {{ edxapp_cms_gunicorn_host }}:{{ edxapp_cms_gunicorn_port }} -w {{ ansible_processor|length * worker_core_mult.cms }} --timeout=300 --pythonpath={{ edxapp_code_dir }} {{ EDXAPP_CMS_GUNICORN_EXTRA }} cms.wsgi {% else -%} -command={{ executable }} {{ max_req }} --preload -b {{ edxapp_cms_gunicorn_host }}:{{ edxapp_cms_gunicorn_port }} -w {{ worker_core_mult.cms }} --timeout=300 --pythonpath={{ edxapp_code_dir }} cms.wsgi +command={{ executable }} {{ max_req }} --preload -b {{ edxapp_cms_gunicorn_host }}:{{ edxapp_cms_gunicorn_port }} -w {{ worker_core_mult.cms }} --timeout=300 --pythonpath={{ edxapp_code_dir }} {{ EDXAPP_CMS_GUNICORN_EXTRA }} cms.wsgi {% endif -%} {% endif -%} user={{ common_web_user }} directory={{ edxapp_code_dir }} -environment={% if COMMON_ENABLE_NEWRELIC_APP %}NEW_RELIC_APP_NAME={{ EDXAPP_NEWRELIC_CMS_APPNAME }},NEW_RELIC_LICENSE_KEY={{ NEWRELIC_LICENSE_KEY }},{% endif -%}PORT={{edxapp_cms_gunicorn_port}},ADDRESS={{edxapp_cms_gunicorn_host}},LANG={{ EDXAPP_LANG }},DJANGO_SETTINGS_MODULE={{ edxapp_cms_env }},SERVICE_VARIANT="cms" +environment={% if COMMON_ENABLE_NEWRELIC_APP %}NEW_RELIC_APP_NAME={{ EDXAPP_NEWRELIC_CMS_APPNAME }},NEW_RELIC_LICENSE_KEY={{ NEWRELIC_LICENSE_KEY }},{% endif -%}PORT={{ edxapp_cms_gunicorn_port }},ADDRESS={{ edxapp_cms_gunicorn_host }},LANG={{ EDXAPP_LANG }},DJANGO_SETTINGS_MODULE={{ EDXAPP_CMS_ENV }},SERVICE_VARIANT="cms" stdout_logfile={{ supervisor_log_dir }}/%(program_name)-stdout.log stderr_logfile={{ supervisor_log_dir }}/%(program_name)-stderr.log killasgroup=true diff --git a/playbooks/roles/edxapp/templates/code.sandbox.j2 b/playbooks/roles/edxapp/templates/code.sandbox.j2 index 354d8726b6b..6074886121a 100644 --- a/playbooks/roles/edxapp/templates/code.sandbox.j2 +++ b/playbooks/roles/edxapp/templates/code.sandbox.j2 @@ -9,7 +9,7 @@ /tmp/codejail-*/** wrix, # - # Whitelist particiclar shared objects from the system + # Whitelist particular shared objects from the system # python installation # /usr/lib/python2.7/lib-dynload/_json.so mr, @@ -21,6 +21,22 @@ /usr/lib/python2.7/lib-dynload/_elementtree.so mr, /usr/lib/python2.7/lib-dynload/pyexpat.so mr, + # Matplot lib needs a place for temp caches + {{ edxapp_sandbox_venv_dir }}/.config/ wrix, + {{ edxapp_sandbox_venv_dir }}/.cache/ wrix, + {{ edxapp_sandbox_venv_dir }}/.config/** wrix, + {{ edxapp_sandbox_venv_dir }}/.cache/** wrix, + + # Matplotlib related libraries + /usr/lib/python2.7/lib-dynload/termios.so mr, + /usr/lib/python2.7/lib-dynload/parser.so mr, + + # Matplot lib needs fonts to make graphs + /usr/share/fonts/ r, + /usr/share/fonts/** r, + /usr/local/share/fonts/ r, + /usr/local/share/fonts/** r, + # # Allow access to selections from /proc # diff --git a/playbooks/roles/edxapp/templates/lms.auth.json.j2 b/playbooks/roles/edxapp/templates/lms.auth.json.j2 index 94b4946f055..4538e7ff4a3 100644 --- a/playbooks/roles/edxapp/templates/lms.auth.json.j2 +++ b/playbooks/roles/edxapp/templates/lms.auth.json.j2 @@ -1,4 +1,4 @@ -{% do lms_auth_config.update(EDXAPP_AUTH_EXTRA) %} +{% do lms_auth_config.update(EDXAPP_LMS_AUTH_EXTRA) %} {% for key, value in lms_auth_config.iteritems() %} {% if value == 'None' %} {% do lms_auth_config.update({key: None }) %} diff --git a/playbooks/roles/edxapp/templates/lms.conf.j2 b/playbooks/roles/edxapp/templates/lms.conf.j2 index fe563509a4b..554bac72b2c 100644 --- a/playbooks/roles/edxapp/templates/lms.conf.j2 +++ b/playbooks/roles/edxapp/templates/lms.conf.j2 @@ -17,15 +17,15 @@ command={{ executable }} {{ max_req }} --preload -b {{ edxapp_lms_gunicorn_host {% else -%} {# This is for backwards compatibility, set workers explicitely using EDXAPP_WORKERS #} {% if ansible_processor|length > 0 -%} -command={{ executable }} {{ max_req }} --preload -b {{ edxapp_lms_gunicorn_host }}:{{ edxapp_lms_gunicorn_port }} -w {{ ansible_processor|length * worker_core_mult.lms }} --timeout=300 --pythonpath={{ edxapp_code_dir }} lms.wsgi +command={{ executable }} {{ max_req }} --preload -b {{ edxapp_lms_gunicorn_host }}:{{ edxapp_lms_gunicorn_port }} -w {{ ansible_processor|length * worker_core_mult.lms }} --timeout=300 --pythonpath={{ edxapp_code_dir }} {{ EDXAPP_LMS_GUNICORN_EXTRA }} lms.wsgi {% else -%} -command={{ executable }} {{ max_req }} --preload -b {{ edxapp_lms_gunicorn_host }}:{{ edxapp_lms_gunicorn_port }} -w {{ worker_core_mult.lms }} --timeout=300 --pythonpath={{ edxapp_code_dir }} lms.wsgi +command={{ executable }} {{ max_req }} --preload -b {{ edxapp_lms_gunicorn_host }}:{{ edxapp_lms_gunicorn_port }} -w {{ worker_core_mult.lms }} --timeout=300 --pythonpath={{ edxapp_code_dir }} {{ EDXAPP_LMS_GUNICORN_EXTRA }} lms.wsgi {% endif %} {% endif %} user={{ common_web_user }} directory={{ edxapp_code_dir }} -environment={% if COMMON_ENABLE_NEWRELIC_APP %}NEW_RELIC_APP_NAME={{ EDXAPP_NEWRELIC_LMS_APPNAME }},NEW_RELIC_LICENSE_KEY={{ NEWRELIC_LICENSE_KEY }},{% endif -%} PORT={{edxapp_lms_gunicorn_port}},ADDRESS={{edxapp_lms_gunicorn_host}},LANG={{ EDXAPP_LANG }},DJANGO_SETTINGS_MODULE={{ edxapp_lms_env }},SERVICE_VARIANT="lms",PATH="{{ edxapp_deploy_path }}" +environment={% if COMMON_ENABLE_NEWRELIC_APP %}NEW_RELIC_APP_NAME={{ EDXAPP_NEWRELIC_LMS_APPNAME }},NEW_RELIC_LICENSE_KEY={{ NEWRELIC_LICENSE_KEY }},{% endif -%} PORT={{ edxapp_lms_gunicorn_port }},ADDRESS={{ edxapp_lms_gunicorn_host }},LANG={{ EDXAPP_LANG }},DJANGO_SETTINGS_MODULE={{ EDXAPP_LMS_ENV }},SERVICE_VARIANT="lms",PATH="{{ edxapp_deploy_path }}" stdout_logfile={{ supervisor_log_dir }}/%(program_name)-stdout.log stderr_logfile={{ supervisor_log_dir }}/%(program_name)-stderr.log killasgroup=true diff --git a/playbooks/roles/edxlocal/tasks/main.yml b/playbooks/roles/edxlocal/tasks/main.yml index a6e9d183c9f..a83c5e09d5e 100644 --- a/playbooks/roles/edxlocal/tasks/main.yml +++ b/playbooks/roles/edxlocal/tasks/main.yml @@ -17,7 +17,7 @@ mysql_user: > name={{ EDXAPP_MYSQL_USER }} password={{ EDXAPP_MYSQL_PASSWORD }} - priv='{{EDXAPP_MYSQL_DB_NAME}}.*:ALL' + priv='{{ EDXAPP_MYSQL_DB_NAME }}.*:ALL' when: EDXAPP_MYSQL_USER is defined - name: create a database for edxapp @@ -31,7 +31,7 @@ mysql_user: > name={{ XQUEUE_MYSQL_USER }} password={{ XQUEUE_MYSQL_PASSWORD }} - priv='{{XQUEUE_MYSQL_DB_NAME}}.*:ALL' + priv='{{ XQUEUE_MYSQL_DB_NAME }}.*:ALL' when: XQUEUE_MYSQL_USER is defined - name: create a database for xqueue @@ -45,7 +45,7 @@ mysql_user: > name={{ ORA_MYSQL_USER }} password={{ ORA_MYSQL_PASSWORD }} - priv='{{ORA_MYSQL_DB_NAME}}.*:ALL' + priv='{{ ORA_MYSQL_DB_NAME }}.*:ALL' when: ORA_MYSQL_USER is defined - name: create a database for ora diff --git a/playbooks/roles/elasticsearch/templates/edx/etc/elasticsearch/elasticsearch.yml.j2 b/playbooks/roles/elasticsearch/templates/edx/etc/elasticsearch/elasticsearch.yml.j2 index d129ffca73c..39aadaddec2 100644 --- a/playbooks/roles/elasticsearch/templates/edx/etc/elasticsearch/elasticsearch.yml.j2 +++ b/playbooks/roles/elasticsearch/templates/edx/etc/elasticsearch/elasticsearch.yml.j2 @@ -3,11 +3,11 @@ # Path to directory where to store index data allocated for this node. # -path.data: {{elasticsearch_data_dir}} +path.data: {{ elasticsearch_data_dir }} # Path to log files: # -path.logs: {{elasticsearch_log_dir}} +path.logs: {{ elasticsearch_log_dir }} # ElasticSearch performs poorly when JVM starts swapping: you should ensure that # it _never_ swaps. @@ -43,3 +43,8 @@ script.disable_dynamic: true discovery.zen.ping.unicast.hosts: ['{{hosts|join("\',\'") }}'] {% endif -%} + +{% if vagrant_cluster|bool %} +network: + host: {{ ansible_ssh_host }} +{% endif %} diff --git a/playbooks/roles/flower/defaults/main.yml b/playbooks/roles/flower/defaults/main.yml index 62ebb32b128..d757638826d 100644 --- a/playbooks/roles/flower/defaults/main.yml +++ b/playbooks/roles/flower/defaults/main.yml @@ -24,4 +24,4 @@ flower_deploy_path: "{{ flower_venv_bin }}:/usr/local/sbin:/usr/local/bin:/usr/b flower_broker: "amqp://{{ FLOWER_BROKER_USERNAME }}:{{ FLOWER_BROKER_PASSWORD }}@{{ FLOWER_BROKER_HOST }}:{{ FLOWER_BROKER_PORT }}" flower_environment: - PATH: $flower_deploy_path + PATH: "{{ flower_deploy_path }}" diff --git a/playbooks/roles/forum/defaults/main.yml b/playbooks/roles/forum/defaults/main.yml index e977526cd54..51d14900b50 100644 --- a/playbooks/roles/forum/defaults/main.yml +++ b/playbooks/roles/forum/defaults/main.yml @@ -18,7 +18,7 @@ FORUM_MONGO_HOSTS: FORUM_MONGO_TAGS: !!null FORUM_MONGO_PORT: "27017" FORUM_MONGO_DATABASE: "cs_comments_service" -FORUM_MONGO_URL: "mongodb://{{ FORUM_MONGO_USER }}:{{ FORUM_MONGO_PASSWORD }}@{%- for host in FORUM_MONGO_HOSTS -%}{{host}}:{{ FORUM_MONGO_PORT }}{%- if not loop.last -%},{%- endif -%}{%- endfor -%}/{{ FORUM_MONGO_DATABASE }}{%- if FORUM_MONGO_TAGS -%}?tags={{ FORUM_MONGO_TAGS }}{%- endif -%}" +FORUM_MONGO_URL: "mongodb://{{ FORUM_MONGO_USER }}:{{ FORUM_MONGO_PASSWORD }}@{%- for host in FORUM_MONGO_HOSTS -%}{{ host }}:{{ FORUM_MONGO_PORT }}{%- if not loop.last -%},{%- endif -%}{%- endfor -%}/{{ FORUM_MONGO_DATABASE }}{%- if FORUM_MONGO_TAGS -%}?tags={{ FORUM_MONGO_TAGS }}{%- endif -%}" FORUM_SINATRA_ENV: "development" FORUM_RACK_ENV: "development" FORUM_NGINX_PORT: "18080" @@ -28,8 +28,8 @@ FORUM_ELASTICSEARCH_PORT: "9200" FORUM_ELASTICSEARCH_URL: "http://{{ FORUM_ELASTICSEARCH_HOST }}:{{ FORUM_ELASTICSEARCH_PORT }}" # This needs to be a string, set to 'false' to disable -FORUM_NEW_RELIC_ENABLE: 'true' -FORUM_NEW_RELIC_LICENSE_KEY: "new-relic-license-key" +FORUM_NEW_RELIC_ENABLE: '{{ COMMON_ENABLE_NEWRELIC }}' +FORUM_NEW_RELIC_LICENSE_KEY: '{{ NEWRELIC_LICENSE_KEY | default("") }}' FORUM_NEW_RELIC_APP_NAME: "{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}-forum" FORUM_WORKER_PROCESSES: "4" @@ -72,4 +72,3 @@ forum_version: "master" # forum_services: - {service: "elasticsearch", host: "{{ FORUM_ELASTICSEARCH_HOST }}", port: "{{ FORUM_ELASTICSEARCH_PORT }}"} - diff --git a/playbooks/roles/forum/templates/cs_comments_service.conf.j2 b/playbooks/roles/forum/templates/cs_comments_service.conf.j2 index 9022fdc623e..523261bf436 100644 --- a/playbooks/roles/forum/templates/cs_comments_service.conf.j2 +++ b/playbooks/roles/forum/templates/cs_comments_service.conf.j2 @@ -10,7 +10,7 @@ env PID=/var/tmp/comments_service.pid chdir {{ forum_code_dir }} script - . {{forum_app_dir}}/forum_env - {{forum_app_dir}}/.rbenv/shims/ruby app.rb + . {{ forum_app_dir }}/forum_env + {{ forum_app_dir }}/.rbenv/shims/ruby app.rb end script diff --git a/playbooks/roles/haproxy/defaults/main.yml b/playbooks/roles/haproxy/defaults/main.yml index b42adf89015..243c3f7e83d 100644 --- a/playbooks/roles/haproxy/defaults/main.yml +++ b/playbooks/roles/haproxy/defaults/main.yml @@ -49,11 +49,30 @@ haproxy_default_config: | # desired applications haproxy_applications: - | - listen rabbitmq 127.0.0.1:5672 + listen rabbitmq 127.0.0.1:35672 mode tcp balance roundrobin option tcplog option tcpka - server rabbit01 172.23.128.10:5672 check inter 5000 rise 2 fall 3 - server rabbit02 172.23.129.10:5672 backup check inter 5000 rise 2 fall 3 - server rabbit03 172.23.130.10:5672 backup check inter 5000 rise 2 fall 3 + server rabbit01 192.168.33.100:5672 check inter 5000 rise 2 fall 3 + server rabbit02 192.168.33.110:5672 check inter 5000 rise 2 fall 3 + server rabbit03 192.168.33.120:5672 check inter 5000 rise 2 fall 3 + + listen mariadb 127.0.0.1:13306 + mode tcp + balance roundrobin + option tcplog + option tcpka + option mysql-check user haproxy + server galera1 192.168.33.100:3306 check weight 1 + server galera2 192.168.33.110:3306 check weight 1 + server galera3 192.168.33.120:3306 check weight 1 + + listen elasticsearch 127.0.0.1:19200 + mode tcp + balance roundrobin + option tcplog + option tcpka + server galera1 192.168.33.100:9200 check weight 1 + server galera2 192.168.33.110:9200 check weight 1 + server galera3 192.168.33.120:9200 check weight 1 diff --git a/playbooks/roles/haproxy/meta/main.yml b/playbooks/roles/haproxy/meta/main.yml index 0fbe1d40ef9..ae1075a8c1d 100644 --- a/playbooks/roles/haproxy/meta/main.yml +++ b/playbooks/roles/haproxy/meta/main.yml @@ -18,3 +18,6 @@ # my_role_var0: "foo" # my_role_var1: "bar" # } + +dependencies: + - common diff --git a/playbooks/roles/haproxy/templates/haproxy.cfg.j2 b/playbooks/roles/haproxy/templates/haproxy.cfg.j2 index c2bd1c6fe2b..3ca147d2b3a 100644 --- a/playbooks/roles/haproxy/templates/haproxy.cfg.j2 +++ b/playbooks/roles/haproxy/templates/haproxy.cfg.j2 @@ -1,8 +1,8 @@ # this config needs haproxy-1.1.28 or haproxy-1.2.1 global - log 127.0.0.1 local0 - log 127.0.0.1 local1 notice + log /dev/log local0 info + log /dev/log local0 notice #log loghost local0 info maxconn 4096 #chroot /usr/share/haproxy diff --git a/playbooks/roles/insights/defaults/main.yml b/playbooks/roles/insights/defaults/main.yml new file mode 100644 index 00000000000..2521f2c86a0 --- /dev/null +++ b/playbooks/roles/insights/defaults/main.yml @@ -0,0 +1,154 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +# +# Defaults for role insights +# + +INSIGHTS_GIT_IDENTITY: !!null + +INSIGHTS_MEMCACHE: [ 'localhost:11211' ] +INSIGHTS_FEEDBACK_EMAIL: 'dashboard@example.com' +INSIGHTS_MKTG_BASE: 'http://example.com' +INSIGHTS_PRIVACY_POLICY_URL: '{{ INSIGHTS_MKTG_BASE }}/privacy-policy' +INSIGHTS_TERMS_OF_SERVICE_URL: '{{ INSIGHTS_MKTG_BASE }}/terms-service' +INSIGHTS_SUPPORT_URL: '' +INSIGHTS_OAUTH2_SECRET: 'secret' +INSIGHTS_OATH2_KEY: 'key' +INSIGHTS_OAUTH2_URL_ROOT: 'url_root' +INSIGHTS_SECRET_KEY: 'YOUR_SECRET_KEY_HERE' +INSIGHTS_OAUTH2_KEY: 'YOUR_OAUTH2_KEY' +# This will not work on single instance sandboxes +INSIGHTS_DOC_BASE: 'http://localhost/en/latest' +INSIGHTS_LMS_BASE: 'http://localhost:18000' +ANALYTICS_API_ENDPOINT: 'http://localhost:18010' +INSIGHTS_DATA_API_AUTH_TOKEN: 'YOUR_DATA_API_AUTH_TOKEN' +INSIGHTS_PLATFORM_NAME: 'edX' +INSIGHTS_APPLICATION_NAME: 'Insights' +INSIGHTS_SEGMENT_IO_KEY: 'YOUR_KEY' +# should match the timezone of your map reduce pipeline +INSIGHTS_TIME_ZONE: 'UTC' +INSIGHTS_LANGUAGE_CODE: 'en-us' +# email config +INSIGHTS_EMAIL_HOST: 'smtp.example.com' +INSIGHTS_EMAIL_HOST_PASSWORD: "mail_password" +INSIGHTS_EMAIL_HOST_USER: "mail_user" +INSIGHTS_EMAIL_PORT: 587 +INSIGHTS_ENABLE_AUTO_AUTH: false + +INSIGHTS_DATABASES: + # rw user + default: + ENGINE: 'django.db.backends.mysql' + NAME: 'dashboard' + USER: 'rosencrantz' + PASSWORD: 'secret' + HOST: 'localhost' + PORT: '3306' + +# +# This block of config is dropped into /edx/etc/insights.yml +# and is read in by analytics_dashboard/settings/production.py +INSIGHTS_CONFIG: + SUPPORT_URL: '{{ INSIGHTS_SUPPORT_URL }}' + DOCUMENTATION_LOAD_ERROR_URL: '{{ INSIGHTS_DOC_BASE }}/Reference.html#error-conditions' + SEGMENT_IO_KEY: '{{ INSIGHTS_SEGMENT_IO_KEY }}' + FEEDBACK_EMAIL: '{{ INSIGHTS_FEEDBACK_EMAIL }}' + PRIVACY_POLICY_URL: '{{ INSIGHTS_PRIVACY_POLICY_URL }}' + TERMS_OF_SERVICE_URL: '{{ INSIGHTS_TERMS_OF_SERVICE_URL }}' + HELP_URL: '{{ INSIGHTS_DOC_BASE }}' + SECRET_KEY: '{{ INSIGHTS_SECRET_KEY }}' + DATA_API_URL: '{{ ANALYTICS_API_ENDPOINT }}' + DATA_API_AUTH_TOKEN: '{{ INSIGHTS_DATA_API_AUTH_TOKEN }}' + SOCIAL_AUTH_REDIRECT_IS_HTTPS: true + SOCIAL_AUTH_EDX_OIDC_KEY: '{{ INSIGHTS_OAUTH2_KEY }}' + SOCIAL_AUTH_EDX_OIDC_SECRET: '{{ INSIGHTS_OAUTH2_SECRET }}' + SOCIAL_AUTH_EDX_OIDC_URL_ROOT: '{{ INSIGHTS_OAUTH2_URL_ROOT }}' + # This value should be the same as SOCIAL_AUTH_EDX_OIDC_SECRET + SOCIAL_AUTH_EDX_OIDC_ID_TOKEN_DECRYPTION_KEY: '{{ INSIGHTS_OAUTH2_SECRET }}' + ENABLE_AUTO_AUTH: $INSIGHTS_ENABLE_AUTO_AUTH + PLATFORM_NAME: '{{ INSIGHTS_PLATFORM_NAME }}' + APPLICATION_NAME: '{{ INSIGHTS_APPLICATION_NAME }}' + CACHES: + default: &default_generic_cache + BACKEND: 'django.core.cache.backends.memcached.MemcachedCache' + KEY_PREFIX: '{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}-insights' + LOCATION: "{{ INSIGHTS_MEMCACHE }}" + TIME_ZONE: '{{ INSIGHTS_TIME_ZONE }}' + LANGUAGE_CODE: '{{ INSIGHTS_LANGUAGE_CODE }}' + # email config + EMAIL_HOST: '{{ INSIGHTS_EMAIL_HOST }}' + EMAIL_HOST_PASSWORD: '{{ INSIGHTS_EMAIL_HOST_PASSWORD }}' + EMAIL_HOST_USER: '{{ INSIGHTS_EMAIL_HOST_USER }}' + EMAIL_PORT: $INSIGHTS_EMAIL_PORT + # static file config + STATICFILES_DIRS: ["{{ insights_python_path }}/static"] + STATIC_ROOT: "{{ COMMON_DATA_DIR }}/{{ insights_service_name }}/staticfiles" + # db config + DATABASE_OPTIONS: + connect_timeout: 10 + DATABASES: "{{ INSIGHTS_DATABASES }}" + + +INSIGHTS_VERSION: "master" +INSIGHTS_NEWRELIC_APPNAME: "{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}-analytics-api" +INSIGHTS_PIP_EXTRA_ARGS: "-i {{ COMMON_PYPI_MIRROR_URL }}" +INSIGHTS_NGINX_PORT: "18110" +INSIGHTS_GUNICORN_WORKERS: "2" +INSIGHTS_GUNICORN_EXTRA: "" +# +# vars are namespace with the module name. +# +insights_environment: + DJANGO_SETTINGS_MODULE: "analytics_dashboard.settings.production" + ANALYTICS_DASHBOARD_CFG: "{{ COMMON_CFG_DIR }}/{{ insights_service_name }}.yaml" + +insights_role_name: "insights" +insights_service_name: "{{ insights_role_name }}" +insights_user: "{{ insights_role_name }}" +insights_app_dir: "{{ COMMON_APP_DIR }}/{{ insights_service_name }}" +insights_home: "{{ COMMON_APP_DIR }}/{{ insights_service_name }}" +insights_venv_base: "{{ insights_home }}/venvs" +insights_venv_dir: "{{ insights_venv_base }}/{{ insights_service_name }}" +insights_venv_bin: "{{ insights_venv_dir }}/bin" +insights_code_dir: "{{ insights_app_dir }}/edx-analytics-dashboard" +insights_python_path: "{{ insights_code_dir }}/analytics_dashboard" +insights_conf_dir: "{{ insights_home }}" +insights_log_dir: "{{ COMMON_LOG_DIR }}/{{ insights_service_name }}" + +insights_nodeenv_dir: "{{ insights_home }}/nodeenvs/{{ insights_service_name }}" +insights_nodeenv_bin: "{{ insights_nodeenv_dir }}/bin" +insights_node_modules_dir: "{{ insights_code_dir }}/node_modules" +insights_node_bin: "{{ insights_node_modules_dir }}/.bin" + +insights_gunicorn_host: "127.0.0.1" +insights_gunicorn_port: "8110" +insights_gunicorn_timeout: "300" +insights_wsgi: "analytics_dashboard.wsgi:application" + +insights_django_settings: "analytics_dashboard.settings.production" +insights_source_repo: "git@{{ COMMON_GIT_MIRROR }}:/edx/edx-analytics-dashboard" +insights_git_ssh_opts: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i {{ insights_git_identity_file }}" +insights_git_identity_file: "{{ insights_home }}/git-identity" +insights_manage: "{{ insights_code_dir }}/analytics_dashboard/manage.py" + +insights_requirements_base: "{{ insights_code_dir }}/requirements" +insights_requirements: + - production.txt + - optional.txt + +# +# OS packages +# +insights_debian_pkgs: + - 'libmysqlclient-dev' + - 'build-essential' + +insights_redhat_pkgs: + - 'community-mysql-devel' diff --git a/playbooks/roles/insights/handlers/main.yml b/playbooks/roles/insights/handlers/main.yml new file mode 100644 index 00000000000..40f47ebe17c --- /dev/null +++ b/playbooks/roles/insights/handlers/main.yml @@ -0,0 +1,24 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +# +# +# Handlers for role insights +# +# Overview: +# +# + +- name: "restart insights" + supervisorctl_local: > + name={{ insights_service_name }} + supervisorctl_path={{ supervisor_ctl }} + config={{ supervisor_cfg }} + state=restarted + when: not disable_edx_services diff --git a/playbooks/roles/insights/meta/main.yml b/playbooks/roles/insights/meta/main.yml new file mode 100644 index 00000000000..db98ba999c0 --- /dev/null +++ b/playbooks/roles/insights/meta/main.yml @@ -0,0 +1,17 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +## +# Role includes for role insights +# +dependencies: + - role: edx_service + edx_role_name: "{{ insights_role_name }}" + edx_service_name: "{{ insights_service_name }}" + - supervisor diff --git a/playbooks/roles/insights/tasks/deploy.yml b/playbooks/roles/insights/tasks/deploy.yml new file mode 100644 index 00000000000..d5d5a5d3ff8 --- /dev/null +++ b/playbooks/roles/insights/tasks/deploy.yml @@ -0,0 +1,130 @@ +--- + +- name: install read-only ssh key + copy: > + content="{{ INSIGHTS_GIT_IDENTITY }}" dest={{ insights_git_identity_file }} + owner={{ insights_user }} group={{ insights_user }} mode=0600 + +- name: setup the insights env file + template: > + src="edx/app/insights/insights_env.j2" + dest="{{ insights_app_dir }}/insights_env" + owner={{ insights_user }} + group={{ insights_user }} + mode=0644 + +- name: checkout code + git: > + dest={{ insights_code_dir }} repo={{ insights_source_repo }} version={{ INSIGHTS_VERSION }} + accept_hostkey=yes + ssh_opts="{{ insights_git_ssh_opts }}" + register: insights_code_checkout + notify: restart insights + sudo_user: "{{ insights_user }}" + +- name: write out app config file + template: > + src=edx/app/insights/insights.yaml.j2 + dest={{ COMMON_CFG_DIR }}/{{ insights_service_name }}.yaml + mode=0644 owner={{ insights_user }} group={{ insights_user }} + notify: restart insights + +- name: install application requirements + pip: > + requirements="{{ insights_requirements_base }}/{{ item }}" + virtualenv="{{ insights_venv_dir }}" state=present + extra_args="--exists-action w" + sudo_user: "{{ insights_user }}" + notify: restart insights + with_items: insights_requirements + +- name: create nodeenv + shell: > + creates={{ insights_nodeenv_dir }} + {{ insights_venv_bin }}/nodeenv {{ insights_nodeenv_dir }} + sudo_user: "{{ insights_user }}" + +- name: install node dependencies + npm: executable={{ insights_nodeenv_bin }}/npm path={{ insights_code_dir }} production=yes + sudo_user: "{{ insights_user }}" + +- name: install bower dependencies + shell: > + chdir={{ insights_code_dir }} + . {{ insights_nodeenv_bin }}/activate && {{ insights_node_bin }}/bower install --production --config.interactive=false + sudo_user: "{{ insights_user }}" + +- name: syncdb and migrate + shell: > + chdir={{ insights_code_dir }} + DB_MIGRATION_USER={{ COMMON_MYSQL_MIGRATE_USER }} + DB_MIGRATION_PASS={{ COMMON_MYSQL_MIGRATE_PASS }} + {{ insights_venv_bin }}/python {{ insights_manage }} syncdb --migrate --noinput + sudo_user: "{{ insights_user }}" + environment: "{{ insights_environment }}" + when: migrate_db is defined and migrate_db|lower == "yes" + +- name: run r.js optimizer + shell: > + chdir={{ insights_code_dir }} + . {{ insights_nodeenv_bin }}/activate && {{ insights_node_bin }}/r.js -o build.js + sudo_user: "{{ insights_user }}" + +- name: run collectstatic + shell: > + chdir={{ insights_code_dir }} + {{ insights_venv_bin }}/python {{ insights_manage }} {{ item }} + sudo_user: "{{ insights_user }}" + environment: "{{ insights_environment }}" + with_items: + - "collectstatic --noinput" + - "compress" + +- name: write out the supervisior wrapper + template: > + src=edx/app/insights/insights.sh.j2 + dest={{ insights_app_dir }}/{{ insights_service_name }}.sh + mode=0650 owner={{ supervisor_user }} group={{ common_web_user }} + notify: restart insights + +- name: write supervisord config + template: > + src=edx/app/supervisor/conf.d.available/insights.conf.j2 + dest="{{ supervisor_available_dir }}/{{ insights_service_name }}.conf" + owner={{ supervisor_user }} group={{ common_web_user }} mode=0644 + notify: restart insights + +- name: enable supervisor script + file: > + src={{ supervisor_available_dir }}/{{ insights_service_name }}.conf + dest={{ supervisor_cfg_dir }}/{{ insights_service_name }}.conf + state=link + force=yes + notify: restart insights + when: not disable_edx_services + +- name: update supervisor configuration + shell: "{{ supervisor_ctl }} -c {{ supervisor_cfg }} update" + when: not disable_edx_services + +- name: create symlinks from the venv bin dir + file: > + src="{{ insights_venv_bin }}/{{ item }}" + dest="{{ COMMON_BIN_DIR }}/{{ item.split('.')[0] }}.{{ insights_role_name }}" + state=link + with_items: + - python + - pip + - django-admin.py + +- name: create manage.py symlink + file: > + src="{{ insights_manage }}" + dest="{{ COMMON_BIN_DIR }}/manage.{{ insights_role_name }}" + state=link + +- name: remove read-only ssh key for the content repo + file: path={{ insights_git_identity_file }} state=absent + +- include: tag_ec2.yml tags=deploy + when: COMMON_TAG_EC2_INSTANCE diff --git a/playbooks/roles/insights/tasks/main.yml b/playbooks/roles/insights/tasks/main.yml new file mode 100644 index 00000000000..b9bf71a0e46 --- /dev/null +++ b/playbooks/roles/insights/tasks/main.yml @@ -0,0 +1,27 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +# +# +# Tasks for role insights +# +# Overview: +# +# +# Dependencies: +# +# +# Example play: +# +# + +- fail: msg="You must provide a private key for the Insights repo" + when: not INSIGHTS_GIT_IDENTITY + +- include: deploy.yml tags=deploy diff --git a/playbooks/roles/insights/tasks/tag_ec2.yml b/playbooks/roles/insights/tasks/tag_ec2.yml new file mode 100644 index 00000000000..df84fd5ccbf --- /dev/null +++ b/playbooks/roles/insights/tasks/tag_ec2.yml @@ -0,0 +1,11 @@ +--- + +- name: get instance information + action: ec2_facts + +- name: tag instance + ec2_tag: resource={{ ansible_ec2_instance_id }} region={{ ansible_ec2_placement_region }} + args: + tags: + "version:insights" : "{{ insights_source_repo }} {{ insights_code_checkout.after |truncate(7,True,'')}}" + when: insights_code_checkout.after is defined diff --git a/playbooks/roles/insights/templates/edx/app/insights/insights.sh.j2 b/playbooks/roles/insights/templates/edx/app/insights/insights.sh.j2 new file mode 100644 index 00000000000..01fd0619343 --- /dev/null +++ b/playbooks/roles/insights/templates/edx/app/insights/insights.sh.j2 @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# {{ ansible_managed }} + +{% if COMMON_ENABLE_NEWRELIC_APP %} +{% set executable = insights_venv_bin + '/newrelic-admin run-program ' + insights_venv_bin + '/gunicorn' %} +{% else %} +{% set executable = insights_venv_bin + '/gunicorn' %} +{% endif %} + +{% if COMMON_ENABLE_NEWRELIC_APP %} +export NEW_RELIC_APP_NAME="{{ INSIGHTS_NEWRELIC_APPNAME }}" +export NEW_RELIC_LICENSE_KEY="{{ NEWRELIC_LICENSE_KEY }}" +{% endif -%} + +source {{ insights_app_dir }}/insights_env + +{{ executable }} --pythonpath={{ insights_python_path }} -b {{ insights_gunicorn_host }}:{{ insights_gunicorn_port }} -w {{ INSIGHTS_GUNICORN_WORKERS }} --timeout={{ insights_gunicorn_timeout }} {{ INSIGHTS_GUNICORN_EXTRA }} {{ insights_wsgi }} diff --git a/playbooks/roles/insights/templates/edx/app/insights/insights.yaml.j2 b/playbooks/roles/insights/templates/edx/app/insights/insights.yaml.j2 new file mode 100644 index 00000000000..7532cbc29b1 --- /dev/null +++ b/playbooks/roles/insights/templates/edx/app/insights/insights.yaml.j2 @@ -0,0 +1,4 @@ +--- +# {{ ansible_managed }} + +{{ INSIGHTS_CONFIG | to_nice_yaml }} diff --git a/playbooks/roles/insights/templates/edx/app/insights/insights_env.j2 b/playbooks/roles/insights/templates/edx/app/insights/insights_env.j2 new file mode 100644 index 00000000000..896a3693149 --- /dev/null +++ b/playbooks/roles/insights/templates/edx/app/insights/insights_env.j2 @@ -0,0 +1,7 @@ +# {{ ansible_managed }} + +{% for name,value in insights_environment.items() -%} +{%- if value -%} +export {{ name }}="{{ value }}" +{% endif %} +{%- endfor %} diff --git a/playbooks/roles/insights/templates/edx/app/supervisor/conf.d.available/insights.conf.j2 b/playbooks/roles/insights/templates/edx/app/supervisor/conf.d.available/insights.conf.j2 new file mode 100644 index 00000000000..4ccf8cfedf3 --- /dev/null +++ b/playbooks/roles/insights/templates/edx/app/supervisor/conf.d.available/insights.conf.j2 @@ -0,0 +1,11 @@ +# {{ ansible_managed }} + +[program:{{ insights_service_name }}] + +command={{ insights_app_dir }}/insights.sh +user={{ common_web_user }} +directory={{ insights_code_dir }} +stdout_logfile={{ supervisor_log_dir }}/%(program_name)-stdout.log +stderr_logfile={{ supervisor_log_dir }}/%(program_name)-stderr.log +killasgroup=true +stopasgroup=true diff --git a/playbooks/roles/jenkins_admin/defaults/main.yml b/playbooks/roles/jenkins_admin/defaults/main.yml index 9ba98e61bc3..95321c2aa4d 100644 --- a/playbooks/roles/jenkins_admin/defaults/main.yml +++ b/playbooks/roles/jenkins_admin/defaults/main.yml @@ -119,7 +119,7 @@ jenkins_admin_plugins: - { name: "jquery", version: "1.7.2-1" } - { name: "dashboard-view", version: "2.9.4" } - { name: "build-pipeline-plugin", version: "1.4.3" } - - { name: "s3", version: "0.5" } + - { name: "s3", version: "0.6" } - { name: "tmpcleaner", version: "1.1" } - { name: "jobConfigHistory", version: "2.8" } - { name: "build-timeout", version: "1.14" } diff --git a/playbooks/roles/jenkins_admin/meta/main.yml b/playbooks/roles/jenkins_admin/meta/main.yml index a20cb27a12f..138fff60852 100644 --- a/playbooks/roles/jenkins_admin/meta/main.yml +++ b/playbooks/roles/jenkins_admin/meta/main.yml @@ -9,12 +9,12 @@ # ## # Role includes for role jenkins_admin -# +# # Example: # # dependencies: # - { -# role: my_role +# role: my_role # my_role_var0: "foo" # my_role_var1: "bar" # } @@ -22,7 +22,7 @@ dependencies: - common - aws - role: jenkins_master - jenkins_plugins: $jenkins_admin_plugins + jenkins_plugins: "{{ jenkins_admin_plugins }}" - role: supervisor supervisor_app_dir: "{{ jenkins_supervisor_app_dir }}" supervisor_data_dir: "{{ jenkins_supervisor_data_dir }}" diff --git a/playbooks/roles/jenkins_admin/tasks/nat_monitor.yml b/playbooks/roles/jenkins_admin/tasks/nat_monitor.yml index 968e2703dfd..f1c539add40 100644 --- a/playbooks/roles/jenkins_admin/tasks/nat_monitor.yml +++ b/playbooks/roles/jenkins_admin/tasks/nat_monitor.yml @@ -25,6 +25,7 @@ group="{{ jenkins_group }}" mode="755" sudo_user: "{{ jenkins_user }}" + notify: restart nat monitor - name: create a supervisor config template: diff --git a/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/aws_config.j2 b/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/aws_config.j2 index f3b34b18649..2ebf8796e63 100644 --- a/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/aws_config.j2 +++ b/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/aws_config.j2 @@ -1,5 +1,5 @@ {% for deployment, creds in JENKINS_ADMIN_AWS_CREDENTIALS.iteritems() %} -[profile {{deployment}}] +[profile {{ deployment }}] aws_access_key_id = {{ creds.access_id }} aws_secret_access_key = {{ creds.secret_key }} diff --git a/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/boto.j2 b/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/boto.j2 index f3b34b18649..2ebf8796e63 100644 --- a/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/boto.j2 +++ b/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/boto.j2 @@ -1,5 +1,5 @@ {% for deployment, creds in JENKINS_ADMIN_AWS_CREDENTIALS.iteritems() %} -[profile {{deployment}}] +[profile {{ deployment }}] aws_access_key_id = {{ creds.access_id }} aws_secret_access_key = {{ creds.secret_key }} diff --git a/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/jobs/backup-jenkins/config.xml.j2 b/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/jobs/backup-jenkins/config.xml.j2 index 3357f9fb004..5369df37198 100644 --- a/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/jobs/backup-jenkins/config.xml.j2 +++ b/playbooks/roles/jenkins_admin/templates/edx/var/jenkins/jobs/backup-jenkins/config.xml.j2 @@ -46,7 +46,7 @@ rm -rf $BUILD_ID {{ JENKINS_ADMIN_S3_PROFILE.name }} - edx-jenkins-backups/{{JENKINS_ADMIN_NAME}} + edx-jenkins-backups/{{ JENKINS_ADMIN_NAME }} ${BUILD_ID}.tar.gz STANDARD US_EAST_1 diff --git a/playbooks/roles/jenkins_master/defaults/main.yml b/playbooks/roles/jenkins_master/defaults/main.yml index 05b10fdb066..5cef7b6a6f3 100644 --- a/playbooks/roles/jenkins_master/defaults/main.yml +++ b/playbooks/roles/jenkins_master/defaults/main.yml @@ -12,6 +12,7 @@ jenkins_plugins: - { name: "build-name-setter", version: "1.3" } - { name: "build-pipeline-plugin", version: "1.4" } - { name: "build-timeout", version: "1.11" } + - { name: "cloudbees-folder", version: "4.6.1" } - { name: "cobertura", version: "1.9.2" } - { name: "copyartifact", version: "1.28" } - { name: "copy-to-slave", version: "1.4.3" } @@ -35,7 +36,7 @@ jenkins_plugins: - { name: "postbuild-task", version: "1.8" } - { name: "PrioritySorter", version: "2.8" } - { name: "sauce-ondemand", version: "1.61" } - - { name: "s3", version: "0.5" } + - { name: "s3", version: "0.6" } - { name: "ssh-agent", version: "1.3" } - { name: "ssh-credentials", version: "1.5.1" } - { name: "ssh-slaves", version: "1.4" } diff --git a/playbooks/roles/jenkins_master/tasks/main.yml b/playbooks/roles/jenkins_master/tasks/main.yml index c15eb42f2da..08f57308099 100644 --- a/playbooks/roles/jenkins_master/tasks/main.yml +++ b/playbooks/roles/jenkins_master/tasks/main.yml @@ -58,7 +58,7 @@ # Using this instead of the user module because the user module # fails if the directory exists. - name: set home directory for jenkins user - shell: usermod -d {{jenkins_home}} {{jenkins_user}} + shell: usermod -d {{ jenkins_home }} {{ jenkins_user }} - name: make plugins directory file: diff --git a/playbooks/roles/jenkins_worker/tasks/packer.yml b/playbooks/roles/jenkins_worker/tasks/packer.yml index 8f2298a16b2..ce700422e0f 100644 --- a/playbooks/roles/jenkins_worker/tasks/packer.yml +++ b/playbooks/roles/jenkins_worker/tasks/packer.yml @@ -1,6 +1,6 @@ --- - name: Download packer - get_url: url={{ packer_url }} dest=/var/tmp/packer.zip + shell: "curl -L {{ packer_url }} -o /var/tmp/packer.zip" - name: Unzip packer unarchive: src=/var/tmp/packer.zip dest=/usr/local/bin copy=no diff --git a/playbooks/roles/kibana/templates/config.js.j2 b/playbooks/roles/kibana/templates/config.js.j2 index 7968b3dee71..31885f69e99 100644 --- a/playbooks/roles/kibana/templates/config.js.j2 +++ b/playbooks/roles/kibana/templates/config.js.j2 @@ -21,11 +21,11 @@ function (Settings) { //elasticsearch: "http://"+window.location.hostname+":9200", {% if NGINX_ENABLE_SSL %} - elasticsearch: "https://{{ KIBANA_SERVER_NAME }}/e", + elasticsearch: "https://{{ KIBANA_SERVER_NAME }}:{{ KIBANA_SSL_NGINX_PORT }}/e", {% else %} - elasticsearch: "http://{{ KIBANA_SERVER_NAME }}/e", + elasticsearch: "http://{{ KIBANA_SERVER_NAME }}:{{ KIBANA_NGINX_PORT }}/e", {% endif %} diff --git a/playbooks/roles/launch_ec2/tasks/main.yml b/playbooks/roles/launch_ec2/tasks/main.yml index 54801cca74e..bdd49f7b3bb 100644 --- a/playbooks/roles/launch_ec2/tasks/main.yml +++ b/playbooks/roles/launch_ec2/tasks/main.yml @@ -21,7 +21,7 @@ module: ec2 state: 'absent' region: "{{ region }}" - instance_ids: ${tag_lookup.instance_ids} + instance_ids: "{{tag_lookup.instance_ids}}" when: terminate_instance == true and tag_lookup.instance_ids|length == 1 - name: deregister instance from an an elb if it was in one @@ -45,7 +45,7 @@ assign_public_ip: yes wait: true region: "{{ region }}" - instance_tags: "{{instance_tags}}" + instance_tags: "{{ instance_tags }}" volumes: - device_name: /dev/sda1 volume_size: "{{ root_ebs_size }}" diff --git a/playbooks/roles/legacy_ora/tasks/main.yml b/playbooks/roles/legacy_ora/tasks/main.yml index e46c8145bd4..9bb523f52e0 100644 --- a/playbooks/roles/legacy_ora/tasks/main.yml +++ b/playbooks/roles/legacy_ora/tasks/main.yml @@ -18,15 +18,15 @@ - name: create ora application config copy: - src={{secure_dir}}/files/{{COMMON_ENVIRONMENT}}/legacy_ora/ora.env.json - dest={{ora_app_dir}}/env.json + src={{ secure_dir }}/files/{{ COMMON_ENVIRONMENT }}/legacy_ora/ora.env.json + dest={{ ora_app_dir }}/env.json sudo_user: "{{ ora_user }}" register: env_state - name: create ora auth file copy: - src={{secure_dir}}/files/{{COMMON_ENVIRONMENT}}/legacy_ora/ora.auth.json - dest={{ora_app_dir}}/auth.json + src={{ secure_dir }}/files/{{ COMMON_ENVIRONMENT }}/legacy_ora/ora.auth.json + dest={{ ora_app_dir }}/auth.json sudo_user: "{{ ora_user }}" register: auth_state diff --git a/playbooks/roles/logstash/tasks/main.yml b/playbooks/roles/logstash/tasks/main.yml index 8f2d3bce99b..24c931e1fcd 100644 --- a/playbooks/roles/logstash/tasks/main.yml +++ b/playbooks/roles/logstash/tasks/main.yml @@ -32,7 +32,7 @@ get_url: url={{ logstash_url }} dest={{ logstash_app_dir }}/share/{{ logstash_file }} - name: ensure symlink with no version exists at {{ logstash_app_dir }}/share/logstash.jar - file: src={{ logstash_app_dir }}/share/${logstash_file} dest={{ logstash_app_dir }}/share/logstash.jar state=link + file: src={{ logstash_app_dir }}/share/{{ logstash_file }} dest={{ logstash_app_dir }}/share/logstash.jar state=link - name: start logstash action: service name=logstash state=started enabled=yes diff --git a/playbooks/roles/mariadb/defaults/main.yml b/playbooks/roles/mariadb/defaults/main.yml new file mode 100644 index 00000000000..08f144229bc --- /dev/null +++ b/playbooks/roles/mariadb/defaults/main.yml @@ -0,0 +1,118 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +## +# Defaults for role mariadb +# +MARIADB_APT_KEY_ID: '0xcbcb082a1bb943db' +# Note: version is determined by repo +MARIADB_REPO: "deb http://mirrors.syringanetworks.net/mariadb/repo/10.0/ubuntu precise main" + +MARIADB_CREATE_DBS: yes +MARIADB_CLUSTERED: no +MARIADB_CLUSTER_USER_ADMIN: "mariadb_clu_root" +MARIADB_CLUSTER_PASSWORD_ADMIN: "password" +MARIADB_HOST_PRIV: '%' + +MARIADB_HAPROXY_USER: 'haproxy' +MARIADB_HAPROXY_HOSTS: + - '192.168.33.100' + - '192.168.33.110' + - '192.168.33.120' + +MARIADB_LISTEN_ALL: false + +MARIADB_DATABASES: + - "{{ EDXAPP_MYSQL_DB_NAME|default('edxapp') }}" + - "{{ XQUEUE_MYSQL_DB_NAME|default('xqueue') }}" + - "{{ ORA_MYSQL_DB_NAME|default('ora') }}" + +MARIADB_ANALYTICS_DATABASES: + - "{{ ANALYTICS_API_CONFIG['DATABASES']['default']['NAME']|default('analytics-api') }}" + - "{{ ANALYTICS_API_CONFIG['DATABASES']['reports']['NAME']|default('reports') }}" + +MARIADB_USERS: + - name: "{{ EDXAPP_MYSQL_USER|default('edxapp001') }}" + pass: "{{ EDXAPP_MYSQL_PASSWORD|default('password') }}" + priv: "{{ EDXAPP_MYSQL_DB_NAME|default('edxapp') }}.*:ALL" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ XQUEUE_MYSQL_USER|default('xqueue001') }}" + pass: "{{ XQUEUE_MYSQL_PASSWORD|default('password') }}" + priv: "{{ XQUEUE_MYSQL_DB_NAME|default('xqueue') }}.*:ALL" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ ORA_MYSQL_USER|default('ora001') }}" + pass: "{{ ORA_MYSQL_PASSWORD|default('password') }}" + priv: "{{ ORA_MYSQL_DB_NAME|default('ora') }}.*:ALL" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ COMMON_MYSQL_MIGRATE_USER|default('migrate') }}" + pass: "{{ COMMON_MYSQL_MIGRATE_PASSWORD|default('password') }}" + priv: "{{ EDXAPP_MYSQL_DB_NAME|default('edxapp') }}.*:ALL" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ COMMON_MYSQL_MIGRATE_USER|default('migrate') }}" + pass: "{{ COMMON_MYSQL_MIGRATE_PASSWORD|default('password') }}" + priv: "{{ XQUEUE_MYSQL_DB_NAME|default('xqueue') }}.*:ALL" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ COMMON_MYSQL_MIGRATE_USER|default('migrate') }}" + pass: "{{ COMMON_MYSQL_MIGRATE_PASSWORD|default('password') }}" + priv: "{{ ORA_MYSQL_DB_NAME|default('ora') }}.*:ALL" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ COMMON_MYSQL_READ_ONLY_USER|default('read_only') }}" + pass: "{{ COMMON_MYSQL_READ_ONLY_PASS|default('password') }}" + priv: "*.*:SELECT" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ COMMON_MYSQL_ADMIN_USER|default('admin') }}" + pass: "{{ COMMON_MYSQL_ADMIN_PASS|default('password') }}" + priv: "*.*:CREATE USER" + host: "{{ MARIADB_HOST_PRIV }}" + +MARIADB_ANALYTICS_USERS: + - name: "{{ ANALYTICS_API_CONFIG['DATABASES']['default']['USER']|default('api001') }}" + pass: "{{ ANALYTICS_API_CONFIG['DATABASES']['default']['PASSWORD']|default('password') }}" + priv: "{{ ANALYTICS_API_CONFIG['DATABASES']['default']['NAME'] }}.*:ALL/reports.*:SELECT" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ ANALYTICS_API_CONFIG['DATABASES']['reports']['USER']|default('reports001') }}" + pass: "{{ ANALYTICS_API_CONFIG['DATABASES']['reports']['PASSWORD']|default('password') }}" + priv: "{{ ANALYTICS_API_CONFIG['DATABASES']['reports']['NAME'] }}.*:SELECT" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ COMMON_MYSQL_MIGRATE_USER|default('migrate') }}" + pass: "{{ COMMON_MYSQL_MIGRATE_PASSWORD|default('password') }}" + priv: "{{ ANALYTICS_API_CONFIG['DATABASES']['default']['NAME']|default('analytics-api') }}.*:ALL" + host: "{{ MARIADB_HOST_PRIV }}" + + - name: "{{ COMMON_MYSQL_MIGRATE_USER|default('migrate') }}" + pass: "{{ COMMON_MYSQL_MIGRATE_PASSWORD|default('password') }}" + priv: "{{ ANALYTICS_API_CONFIG['DATABASES']['reports']['NAME']|default('reports') }}.*:ALL" + host: "{{ MARIADB_HOST_PRIV }}" + +# +# OS packages +# +mariadb_debian_pkgs: + - python-software-properties + - python-mysqldb + +mariadb_redhat_pkgs: [] + +mariadb_apt_repository: + +mariadb_solo_packages: + - mariadb-server + +mariadb_cluster_packages: + - mariadb-galera-server + - galera diff --git a/playbooks/roles/mariadb/meta/main.yml b/playbooks/roles/mariadb/meta/main.yml new file mode 100644 index 00000000000..24e2fcd6d19 --- /dev/null +++ b/playbooks/roles/mariadb/meta/main.yml @@ -0,0 +1,22 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +## +# Role includes for role mariadb +# +# Example: +# +# dependencies: +# - { +# role: my_role +# my_role_var0: "foo" +# my_role_var1: "bar" +# } +dependencies: + - common diff --git a/playbooks/roles/mariadb/tasks/cluster.yml b/playbooks/roles/mariadb/tasks/cluster.yml new file mode 100644 index 00000000000..185d8b823b7 --- /dev/null +++ b/playbooks/roles/mariadb/tasks/cluster.yml @@ -0,0 +1,58 @@ +- name: copy galera cluster config + template: > + src="etc/mysql/conf.d/galera.cnf.j2" + dest="/etc/mysql/conf.d/galera.cnf" + owner="root" + group="root" + mode=0600 + +- name: check if we have already bootstrapped the cluster + stat: path=/etc/mysql/ansible_cluster_started + register: mariadb_bootstrap + +- name: stop mysql for cluster bootstrap + service: name=mysql state=stopped + when: not mariadb_bootstrap.stat.exists + +- name: setup bootstrap on primary + lineinfile: > + dest="/etc/mysql/conf.d/galera.cnf" + regexp="^wsrep_cluster_address=gcomm://{{ hostvars.keys()|sort|join(',') }}$" + line="wsrep_cluster_address=gcomm://" + when: ansible_hostname == hostvars[hostvars.keys()[0]].ansible_hostname and not mariadb_bootstrap.stat.exists + +- name: fetch debian.cnf file so start-stop will work properly + fetch: > + src=/etc/mysql/debian.cnf + dest=/tmp/debian.cnf + fail_on_missing=yes + flat=yes + when: ansible_hostname == hostvars[hostvars.keys()[0]].ansible_hostname and not mariadb_bootstrap.stat.exists + register: mariadb_new_debian_cnf + +- name: copy fetched file to other cluster members + copy: src=/tmp/debian.cnf dest=/etc/mysql/debian.cnf + when: mariadb_new_debian_cnf is defined + +- name: start everything + service: name=mysql state=started + when: not mariadb_bootstrap.stat.exists + +- name: reset galera cluster config since we are bootstrapped + template: > + src="etc/mysql/conf.d/galera.cnf.j2" + dest="/etc/mysql/conf.d/galera.cnf" + owner="root" + group="root" + mode=0600 + when: not mariadb_bootstrap.stat.exists + +- name: touch bootstrap file to confirm we are fully up + file: path="/etc/mysql/ansible_cluster_started" state=touch + +# This is needed for mysql-check in haproxy or other mysql monitor +# scripts to prevent haproxy checks exceeding `max_connect_errors`. +- name: create haproxy monitor user + command: > + mysql -e "INSERT INTO mysql.user (Host,User) values ('{{ item }}','{{ MARIADB_HAPROXY_USER }}'); FLUSH PRIVILEGES;" + with_items: MARIADB_HAPROXY_HOSTS diff --git a/playbooks/roles/mariadb/tasks/main.yml b/playbooks/roles/mariadb/tasks/main.yml new file mode 100644 index 00000000000..64f7f0bcb99 --- /dev/null +++ b/playbooks/roles/mariadb/tasks/main.yml @@ -0,0 +1,93 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +# +# +# Tasks for role mariadb +# +# Overview: +# +# +# Dependencies: +# +# +# Example play: +# +# + +- name: Install pre-req debian packages + apt: name={{ item }} state=present + with_items: mariadb_debian_pkgs + +- name: Add mongo key + apt_key: url="{{ COMMON_UBUNTU_APT_KEYSERVER }}{{ MARIADB_APT_KEY_ID }}" + +- name: add the mariadb repo to the sources list + apt_repository: > + repo='{{ MARIADB_REPO }}' + state=present + +- name: install mariadb solo packages + apt: name={{ item }} update_cache=yes + with_items: mariadb_solo_packages + when: not MARIADB_CLUSTERED|bool + +- name: install mariadb cluster packages + apt: name={{ item }} update_cache=yes + with_items: mariadb_cluster_packages + when: MARIADB_CLUSTERED|bool + +- name: remove bind-address + lineinfile: > + dest=/etc/mysql/my.cnf + regexp="^bind-address\s+=\s+127\.0\.0\.1$" + state=absent + when: MARIADB_LISTEN_ALL|bool or MARIADB_CLUSTERED|bool + +- include: cluster.yml + when: MARIADB_CLUSTERED|bool + +- name: start everything + service: name=mysql state=started + +- name: create all databases + mysql_db: > + db={{ item }} + state=present + encoding=utf8 + with_items: MARIADB_DATABASES + when: MARIADB_CREATE_DBS|bool + +- name: create all analytics dbs + mysql_db: > + db={{ item }} + state=present + encoding=utf8 + with_items: MARIADB_ANALYTICS_DATABASES + when: MARIADB_CREATE_DBS|bool and ANALYTICS_API_CONFIG is defined + +- name: create all users/privs + mysql_user: > + name="{{ item.name }}" + password="{{ item.pass }}" + priv="{{ item.priv }}" + host="{{ item.host }}" + append_privs=yes + with_items: MARIADB_USERS + when: MARIADB_CREATE_DBS|bool + +- name: create all analytics users/privs + mysql_user: > + name="{{ item.name }}" + password="{{ item.pass }}" + priv="{{ item.priv }}" + host="{{ item.host }}" + append_privs=yes + with_items: MARIADB_ANALYTICS_USERS + when: MARIADB_CREATE_DBS|bool and ANALYTICS_API_CONFIG is defined diff --git a/playbooks/roles/mariadb/templates/etc/mysql/conf.d/galera.cnf.j2 b/playbooks/roles/mariadb/templates/etc/mysql/conf.d/galera.cnf.j2 new file mode 100644 index 00000000000..9a7c8b09e10 --- /dev/null +++ b/playbooks/roles/mariadb/templates/etc/mysql/conf.d/galera.cnf.j2 @@ -0,0 +1,17 @@ +{%- set hosts= [] -%} +{%- for host in hostvars.keys()|sort -%} + {% do hosts.append(host) %} +{%- endfor %} +[mysqld] +binlog_format=ROW +innodb_autoinc_lock_mode=2 +innodb_doublewrite=1 +query_cache_size=0 + +wsrep_provider=/usr/lib/galera/libgalera_smm.so +wsrep_cluster_address=gcomm://{{ hosts|join(',') }}?pc.wait_prim=no +wsrep_sst_auth={{ MARIADB_CLUSTER_USER_ADMIN }}:{{ MARIADB_CLUSTER_PASSWORD_ADMIN }} + +{% if vagrant_cluster|bool %} +wsrep_node_address={{ ansible_ssh_host }} +{% endif %} diff --git a/playbooks/roles/minos/tasks/main.yml b/playbooks/roles/minos/tasks/main.yml index 0a346023b84..1a83debe622 100644 --- a/playbooks/roles/minos/tasks/main.yml +++ b/playbooks/roles/minos/tasks/main.yml @@ -61,6 +61,8 @@ - "ProccessQuienscenceVoterCelery" - "ProccessQuienscenceVoterGunicorn" - "TrackingLogVoter" + - "ZippedTrackingLogVoter" + - "RolledTrackingLogVoter" # Optional auth for git - name: create ssh script for git (not authenticated) diff --git a/playbooks/roles/minos/templates/edx/etc/minos/conf.d/RolledTrackingLogVoter.yml.j2 b/playbooks/roles/minos/templates/edx/etc/minos/conf.d/RolledTrackingLogVoter.yml.j2 new file mode 100644 index 00000000000..e3f7e169bed --- /dev/null +++ b/playbooks/roles/minos/templates/edx/etc/minos/conf.d/RolledTrackingLogVoter.yml.j2 @@ -0,0 +1,3 @@ +RolledTrackingLogVoter: + config: + tracking_directory: '{{ COMMON_LOG_DIR }}/tracking' \ No newline at end of file diff --git a/playbooks/roles/minos/templates/edx/etc/minos/conf.d/TrackingLogVoter.yml.j2 b/playbooks/roles/minos/templates/edx/etc/minos/conf.d/TrackingLogVoter.yml.j2 index 98aca96c82b..ada33dd324d 100644 --- a/playbooks/roles/minos/templates/edx/etc/minos/conf.d/TrackingLogVoter.yml.j2 +++ b/playbooks/roles/minos/templates/edx/etc/minos/conf.d/TrackingLogVoter.yml.j2 @@ -2,5 +2,5 @@ TrackingLogVoter: config: aws_profile: !!null local_directory: '{{ COMMON_LOG_DIR }}/tracking' - s3_bucket: 'edx-{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}' + s3_bucket: '{{ COMMON_AWS_SYNC_BUCKET }}' bucket_path_prefix: 'logs/tracking' diff --git a/playbooks/roles/minos/templates/edx/etc/minos/conf.d/ZippedTrackingLogVoter.yml.j2 b/playbooks/roles/minos/templates/edx/etc/minos/conf.d/ZippedTrackingLogVoter.yml.j2 new file mode 100644 index 00000000000..aa774a5f5fe --- /dev/null +++ b/playbooks/roles/minos/templates/edx/etc/minos/conf.d/ZippedTrackingLogVoter.yml.j2 @@ -0,0 +1,3 @@ +ZippedTrackingLogVoter: + config: + tracking_directory: '{{ COMMON_LOG_DIR }}/tracking' \ No newline at end of file diff --git a/playbooks/roles/mongo/defaults/main.yml b/playbooks/roles/mongo/defaults/main.yml index 0264f1b6212..8a7b4550f48 100644 --- a/playbooks/roles/mongo/defaults/main.yml +++ b/playbooks/roles/mongo/defaults/main.yml @@ -1,5 +1,5 @@ mongo_logappend: true -mongo_version: 2.4.7 +mongo_version: 2.6.4 mongo_port: "27017" mongo_extra_conf: '' mongo_key_file: '/etc/mongodb_key' @@ -31,8 +31,8 @@ mongo_logpath: "{{ mongo_log_dir }}/mongodb.log" mongo_dbpath: "{{ mongo_data_dir }}/mongodb" # Have to use this conditional instead of ignore errors -# because the mongo_user module fails and doesn't ginore errors. -mongo_create_users: !!null +# because the mongo_user module fails and doesn't ignore errors. +mongo_create_users: true # If the system is running out of an Amazon Web Services # cloudformation stack, this group name can used to pull out @@ -42,3 +42,21 @@ mongo_aws_stack_name: "tag_aws_cloudformation_stack-name_" # In environments that do not require durability (devstack / Jenkins) # you can disable the journal to reduce disk usage mongo_enable_journal: True + +# We can do regular backups of MongoDB to S3. +MONGO_S3_BACKUP: false +# backup cron time: +MONGO_S3_BACKUP_HOUR: "*/12" +MONGO_S3_BACKUP_DAY: "*" +# override with a secondary node that will perform backups +MONGO_S3_BACKUP_NODE: "undefined" +# back up data into a specific S3 bucket +MONGO_S3_BACKUP_BUCKET: "undefined" +# temporary directory mongodump will use to store data +MONGO_S3_BACKUP_TEMPDIR: "{{ mongo_data_dir }}" +MONGO_S3_NOTIFY_EMAIL: "dummy@example.com" +mongo_s3_logfile: "{{ COMMON_LOG_DIR }}/mongo/s3-mongo-backup.log" +MONGO_S3_S3CMD_CONFIG: "{{ COMMON_DATA_DIR }}/mongo-s3-backup.s3cfg" +MONGO_S3_BACKUP_AWS_ACCESS_KEY: !!null +MONGO_S3_BACKUP_AWS_SECRET_KEY: !!null + diff --git a/playbooks/roles/mongo/handlers/main.yml b/playbooks/roles/mongo/handlers/main.yml index 108f006d984..83a9ecdaf94 100644 --- a/playbooks/roles/mongo/handlers/main.yml +++ b/playbooks/roles/mongo/handlers/main.yml @@ -1,4 +1,4 @@ --- - name: restart mongo - service: name=mongodb state=restarted + service: name=mongod state=restarted diff --git a/playbooks/roles/mongo/tasks/main.yml b/playbooks/roles/mongo/tasks/main.yml index 827e7cf86ba..b0751a2857e 100644 --- a/playbooks/roles/mongo/tasks/main.yml +++ b/playbooks/roles/mongo/tasks/main.yml @@ -1,5 +1,14 @@ --- +- name: check to see that MongoDB 2.4 isn't installed + stat: path=/etc/init.d/mongodb + register: mongodb_needs_upgrade + +- name: verify 2.4 not installed + fail: msg="MongoDB 2.4 is currently installed. If on a stand alone host (devstack), apt-get remove mongodb-10gen and re-run ansible. if on a cluster, read http://docs.mongodb.org/manual/release-notes/2.6-upgrade/#upgrade-considerations and upgrade to 2.6." + when: mongodb_needs_upgrade.stat.exists + + - name: install python pymongo for mongo_user ansible module pip: > name=pymongo state=present @@ -8,7 +17,7 @@ - name: add the mongodb signing key apt_key: > id=7F0CEB10 - url={{MONGODB_APT_KEY}} + url={{ MONGODB_APT_KEY }} state=present - name: add the mongodb repo to the sources list @@ -19,7 +28,7 @@ - name: install mongo server and recommends apt: > - pkg=mongodb-10gen={{ mongo_version }} + pkg=mongodb-org={{ mongo_version }} state=present install_recommends=yes force=yes update_cache=yes @@ -33,8 +42,8 @@ - "{{ mongo_dbpath }}" - "{{ mongo_log_dir }}" -- name: stop mongo service - service: name=mongodb state=stopped +- name: stop mongod service + service: name=mongod state=stopped - name: move mongodb to {{ mongo_data_dir }} command: mv /var/lib/mongodb {{ mongo_data_dir}}/. creates={{ mongo_data_dir }}/mongodb @@ -50,11 +59,11 @@ when: MONGO_CLUSTERED - name: copy configuration template - template: src=mongodb.conf.j2 dest=/etc/mongodb.conf backup=yes + template: src=mongodb.conf.j2 dest=/etc/mongod.conf backup=yes notify: restart mongo - name: start mongo service - service: name=mongodb state=started + service: name=mongod state=started - name: wait for mongo server to start wait_for: port=27017 delay=2 @@ -77,3 +86,39 @@ state=present with_items: MONGO_USERS when: mongo_create_users + +- name: install s3cmd + pip: > + name="s3cmd" + state=present + extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" + when: MONGO_S3_BACKUP + +- name: configure s3cmd + template: > + dest="{{ MONGO_S3_S3CMD_CONFIG }}" + src=mongo-s3-backup-s3cfg.j2 + owner=root + group=root + mode=0600 + when: MONGO_S3_BACKUP + +- name: install backup-mongo-to-s3 script + template: > + src=backup-mongo-to-s3.j2 + dest=/edx/bin/backup-mongo-to-s3.sh + owner=root + group=root + mode=0700 + when: MONGO_S3_BACKUP + +- name: schedule backup-mongo-to-3s crontab + cron: + name="backup-mongo-to-s3" + job="/edx/bin/backup-mongo-to-s3.sh" + backup=yes + cron_file=backup-mongo-to-s3 + user=root + hour="{{ MONGO_S3_BACKUP_HOUR }}" + minute="0" + day="{{ MONGO_S3_BACKUP_DAY }}" diff --git a/playbooks/roles/mongo/templates/backup-mongo-to-s3.j2 b/playbooks/roles/mongo/templates/backup-mongo-to-s3.j2 new file mode 100644 index 00000000000..c44d19aa8cc --- /dev/null +++ b/playbooks/roles/mongo/templates/backup-mongo-to-s3.j2 @@ -0,0 +1,68 @@ +{% set lb = '{' %} +{% set rb = '}' %} +#!/bin/bash +# + +exec > >(tee "{{ mongo_s3_logfile }}") +exec 2>&1 + +shopt -s extglob + +usage() { + + cat< + dest={{ newrelic_logwatch_repo_dir }} + repo={{ newrelic_logwatch_repo }} version={{ newrelic_logwatch_version }} + accept_hostkey=yes + +- name: bundle install + shell: > + chdir={{ newrelic_logwatch_dir }} + creates=/var/lib/gems/1.8/gems/newrelic_plugin-1.0.2/ + bundle install + notify: restart newrelic-logwatch-agent + +- name: create agent configuration + template: > + src=opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/newrelic_plugin.yml.j2 + dest={{ newrelic_logwatch_dir }}/config/newrelic_plugin.yml.copy + notify: restart newrelic-logwatch-agent + +- template: + owner: root + src: etc/init/newrelic-logwatch-agent.conf.j2 + dest: /etc/init/newrelic-logwatch-agent.conf + notify: restart newrelic-logwatch-agent diff --git a/playbooks/roles/newrelic/templates/etc/init/newrelic-logwatch-agent.conf.j2 b/playbooks/roles/newrelic/templates/etc/init/newrelic-logwatch-agent.conf.j2 new file mode 100644 index 00000000000..4c342fb29dc --- /dev/null +++ b/playbooks/roles/newrelic/templates/etc/init/newrelic-logwatch-agent.conf.j2 @@ -0,0 +1,11 @@ +description "newrelic logwatch plugin" +start on runlevel [2345] +stop on runlevel [016] +respawn +chdir {{ newrelic_logwatch_dir }} +pre-start script + ami_id=$(ec2metadata --ami-id) + hostname=$(hostname) + sed "s/HOSTNAME/${ami_id}-${hostname}/" {{ newrelic_logwatch_dir }}/config/newrelic_plugin.yml.copy > {{ newrelic_logwatch_dir }}/config/newrelic_plugin.yml +end script +exec ruby newrelic_logwatcher_agent.rb diff --git a/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-503.j2 b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-503.j2 new file mode 100644 index 00000000000..92b6822e85c --- /dev/null +++ b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-503.j2 @@ -0,0 +1,15 @@ +{% if SANDBOX_USERNAME is defined %} +{% set server_name = SANDBOX_USERNAME + '-nginx-503' %} +{% else %} +{% set server_name = COMMON_ENVIRONMENT|default('unknown-env') + '-' + COMMON_DEPLOYMENT|default('unknown-deployment') + '-nginx-503' + '-HOSTNAME' %} +{% endif %} + + {{ server_name }}: + # Full path to the the log file + log_path: {{ nginx_log_dir|default('/edx/var/log/nginx') }}/error.log + # Returns the number of matches for this term. Use Linux Regex formatting. + term: "limiting requests" + # Provide any options to pass to grep when running. + # For example, to count non-matching lines, enter 'v'. + # Use the abbreviated format ('v' and not 'invert-match'). + grep_options: diff --git a/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-certs-errors.j2 b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-certs-errors.j2 new file mode 100644 index 00000000000..6fdb9ac73e1 --- /dev/null +++ b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-certs-errors.j2 @@ -0,0 +1,15 @@ +{% if SANDBOX_USERNAME is defined %} +{% set server_name = SANDBOX_USERNAME + '-certs-errors' %} +{% else %} +{% set server_name = COMMON_ENVIRONMENT|default('unknown-env') + '-' + COMMON_DEPLOYMENT|default('unknown-deployment') + '-certs-errors' + '-HOSTNAME'%} +{% endif %} + + {{ server_name }}: + # Full path to the the log file + log_path: {{ COMMON_LOG_DIR|default('/edx/var/log') }}/certs/edx.log + # Returns the number of matches for this term. Use Linux Regex formatting. + term: " ERROR " + # Provide any options to pass to grep when running. + # For example, to count non-matching lines, enter 'v'. + # Use the abbreviated format ('v' and not 'invert-match'). + grep_options: diff --git a/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-cms-errors.j2 b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-cms-errors.j2 new file mode 100644 index 00000000000..54271393804 --- /dev/null +++ b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-cms-errors.j2 @@ -0,0 +1,15 @@ +{% if SANDBOX_USERNAME is defined %} +{% set server_name = SANDBOX_USERNAME + '-cms-errors' %} +{% else %} +{% set server_name = COMMON_ENVIRONMENT|default('unknown-env') + '-' + COMMON_DEPLOYMENT|default('unknown-deployment') + '-cms-errors' + '-HOSTNAME' %} +{% endif %} + + {{ server_name }}: + # Full path to the the log file + log_path: {{ COMMON_LOG_DIR|default('/edx/var/log') }}/cms/edx.log + # Returns the number of matches for this term. Use Linux Regex formatting. + term: " ERROR " + # Provide any options to pass to grep when running. + # For example, to count non-matching lines, enter 'v'. + # Use the abbreviated format ('v' and not 'invert-match'). + grep_options: diff --git a/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-lms-errors.j2 b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-lms-errors.j2 new file mode 100644 index 00000000000..5c01535a70f --- /dev/null +++ b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-lms-errors.j2 @@ -0,0 +1,15 @@ +{% if SANDBOX_USERNAME is defined %} +{% set server_name = SANDBOX_USERNAME + '-lms-errors' %} +{% else %} +{% set server_name = COMMON_ENVIRONMENT|default('unknown-env') + '-' + COMMON_DEPLOYMENT|default('unknown-deployment') + '-lms-errors' + '-HOSTNAME' %} +{% endif %} + + {{ server_name }}: + # Full path to the the log file + log_path: {{ COMMON_LOG_DIR|default('/edx/var/log') }}/lms/edx.log + # Returns the number of matches for this term. Use Linux Regex formatting. + term: " ERROR " + # Provide any options to pass to grep when running. + # For example, to count non-matching lines, enter 'v'. + # Use the abbreviated format ('v' and not 'invert-match'). + grep_options: diff --git a/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-xqueue-errors.j2 b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-xqueue-errors.j2 new file mode 100644 index 00000000000..8e5165fd928 --- /dev/null +++ b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/logwatch-xqueue-errors.j2 @@ -0,0 +1,15 @@ +{% if SANDBOX_USERNAME is defined %} +{% set server_name = SANDBOX_USERNAME + '-xqueue-errors' %} +{% else %} +{% set server_name = COMMON_ENVIRONMENT|default('unknown-env') + '-' + COMMON_DEPLOYMENT|default('unknown-deployment') + '-xqueue-errors' + '-HOSTNAME' %} +{% endif %} + + {{ server_name }}: + # Full path to the the log file + log_path: {{ COMMON_LOG_DIR|default('/edx/var/log') }}/xqueue/edx.log + # Returns the number of matches for this term. Use Linux Regex formatting. + term: " ERROR " + # Provide any options to pass to grep when running. + # For example, to count non-matching lines, enter 'v'. + # Use the abbreviated format ('v' and not 'invert-match'). + grep_options: diff --git a/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/newrelic_plugin.yml.j2 b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/newrelic_plugin.yml.j2 new file mode 100644 index 00000000000..4f456403dd6 --- /dev/null +++ b/playbooks/roles/newrelic/templates/opt/newrelic_platform_plugins/newrelic_logwatcher_agent/config/newrelic_plugin.yml.j2 @@ -0,0 +1,33 @@ +# +# +# This is a sample newrelic_plugin.yml file. Please move this file +# to the following location if it is not already there: +# +# ./config/newrelic_plugin.yml +# +# Where the current directory is the directory where your main program resides and is your current +# directory when you run the main program. +# +# Please make sure to update the license_key information with the license key for your New Relic +# account. +# +# +newrelic: + # + # Update with your New Relic account license key: + # + license_key: '{{ NEWRELIC_LICENSE_KEY }}' + # + # Set to '1' for verbose output, remove for normal output. + # All output goes to stdout/stderr. + # + # verbose: 1 +# +# Agent Configuration: +# +agents: +{% for agent in NEWRELIC_LOGWATCH %} + +{% include agent %} + +{% endfor %} diff --git a/playbooks/roles/nginx/defaults/main.yml b/playbooks/roles/nginx/defaults/main.yml index 512d4a89a2d..b70ff7290f8 100644 --- a/playbooks/roles/nginx/defaults/main.yml +++ b/playbooks/roles/nginx/defaults/main.yml @@ -23,6 +23,7 @@ NGINX_ENABLE_SSL: False NGINX_SSL_CERTIFICATE: 'ssl-cert-snakeoil.pem' NGINX_SSL_KEY: 'ssl-cert-snakeoil.key' +NGINX_LOG_FORMAT_NAME: 'p_combined' # When set to False, nginx will pass X-Forwarded-For, X-Forwarded-Port, # and X-Forwarded-Proto headers through to the backend unmodified. # This is desired when nginx is deployed behind another load balancer @@ -32,8 +33,24 @@ NGINX_SSL_KEY: 'ssl-cert-snakeoil.key' # headers to reflect the properties of the incoming request. NGINX_SET_X_FORWARDED_HEADERS: False +NGINX_SERVER_ERROR_IMG: 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Pendleton_Sinking_Ship.jpg/640px-Pendleton_Sinking_Ship.jpg' +NGINX_SERVER_HTML_FILES: + - file: rate-limit.html + title: 'Rate limit exceeded' + msg: 'If think you have encountered this message in error please let us know at {{ EDXAPP_TECH_SUPPORT_EMAIL|default("technical@example.com") }}' + img: "{{ NGINX_SERVER_ERROR_IMG }}" + heading: 'Uh oh, we are having some server issues..' + - file: server-error.html + title: 'Server error' + msg: 'We have been notified of the error, if it persists please let us know at {{ EDXAPP_TECH_SUPPORT_EMAIL|default("technical@example.com") }}' + img: "{{ NGINX_SERVER_ERROR_IMG }}" + heading: 'Uh oh, we are having some server issues..' + + + nginx_app_dir: "{{ COMMON_APP_DIR }}/nginx" nginx_data_dir: "{{ COMMON_DATA_DIR }}/nginx" +nginx_server_static_dir: "{{ nginx_data_dir }}/server-static" nginx_conf_dir: "{{ nginx_app_dir }}/conf.d" nginx_log_dir: "{{ COMMON_LOG_DIR }}/nginx" nginx_sites_available_dir: "{{ nginx_app_dir }}/sites-available" @@ -63,6 +80,8 @@ nginx_cms_gunicorn_hosts: - 127.0.0.1 nginx_analytics_api_gunicorn_hosts: - 127.0.0.1 +nginx_insights_gunicorn_hosts: + - 127.0.0.1 nginx_cfg: # - link - turn on diff --git a/playbooks/roles/nginx/tasks/main.yml b/playbooks/roles/nginx/tasks/main.yml index 61f6e68be3a..5f38fdaab04 100644 --- a/playbooks/roles/nginx/tasks/main.yml +++ b/playbooks/roles/nginx/tasks/main.yml @@ -24,6 +24,7 @@ with_items: - "{{ nginx_data_dir }}" - "{{ nginx_log_dir }}" + - "{{ nginx_server_static_dir }}" notify: restart nginx - name: Install nginx packages @@ -113,6 +114,16 @@ notify: reload nginx with_dict: nginx_redirects + # These are static pages that can be used + # for nginx rate limiting, 500 errors, etc. + +- name: Create NGINX server templates + template: > + src=edx/var/nginx/server-static/server-template.j2 + dest={{ nginx_server_static_dir }}/{{ item.file }} + owner=root group={{ common_web_user }} mode=0640 + with_items: NGINX_SERVER_HTML_FILES + - name: Write out htpasswd file htpasswd: > name={{ COMMON_HTPASSWD_USER }} diff --git a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/analytics-api.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/analytics-api.j2 index 7d93d125dc7..9e29a9abb7b 100644 --- a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/analytics-api.j2 +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/analytics-api.j2 @@ -13,12 +13,6 @@ server { } location / { - {% include "basic-auth.j2" %} - try_files $uri @proxy_to_app; - } - - # No basic auth security on the heartbeat url, so that ELB can use it - location /api/v0/status { try_files $uri @proxy_to_app; } diff --git a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/basic-auth.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/basic-auth.j2 index ccddaef6c97..2f7da8eca2c 100644 --- a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/basic-auth.j2 +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/basic-auth.j2 @@ -4,6 +4,12 @@ allow 127.0.0.1; allow 192.168.0.0/16; allow 172.16.0.0/12; + + allow 10.3.110.0/24; + allow 10.3.120.0/24; + allow 10.8.110.0/24; + allow 10.8.120.0/24; + deny all; auth_basic "Restricted"; diff --git a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/cms.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/cms.j2 index fde35e84342..b7df26b1896 100644 --- a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/cms.j2 +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/cms.j2 @@ -13,10 +13,13 @@ upstream cms-backend { server { # CMS configuration file for nginx, templated by ansible + # 500 error pages + error_page 500 502 504 /server/server-error.html; + {% if NGINX_ENABLE_SSL %} - listen {{EDXAPP_CMS_NGINX_PORT}} {{default_site}}; - listen {{EDXAPP_CMS_SSL_NGINX_PORT}} ssl; + listen {{ EDXAPP_CMS_NGINX_PORT }} {{ default_site }}; + listen {{ EDXAPP_CMS_SSL_NGINX_PORT }} ssl; ssl_certificate /etc/ssl/certs/{{ NGINX_SSL_CERTIFICATE|basename }}; ssl_certificate_key /etc/ssl/private/{{ NGINX_SSL_KEY|basename }}; @@ -24,12 +27,12 @@ server { add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; {% else %} - listen {{EDXAPP_CMS_NGINX_PORT}} {{default_site}}; + listen {{ EDXAPP_CMS_NGINX_PORT }} {{ default_site }}; {% endif %} server_name {{ CMS_HOSTNAME }}; - access_log {{ nginx_log_dir }}/access.log; + access_log {{ nginx_log_dir }}/access.log {{ NGINX_LOG_FORMAT_NAME }}; error_log {{ nginx_log_dir }}/error.log error; # CS184 requires uploads of up to 4MB for submitting screenshots. diff --git a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/forum.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/forum.j2 index dbd3338acbb..353a837304a 100644 --- a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/forum.j2 +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/forum.j2 @@ -32,7 +32,7 @@ upstream forum_app_server { server { server_name forum.*; - listen {{ FORUM_NGINX_PORT }} {{default_site}}; + listen {{ FORUM_NGINX_PORT }} {{ default_site }}; client_max_body_size 1M; keepalive_timeout 5; diff --git a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/insights.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/insights.j2 new file mode 100644 index 00000000000..ed5df512641 --- /dev/null +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/insights.j2 @@ -0,0 +1,35 @@ +upstream insights_app_server { + {% for host in nginx_insights_gunicorn_hosts %} + server {{ host }}:{{ insights_gunicorn_port }} fail_timeout=0; + {% endfor %} +} + +server { + listen {{ INSIGHTS_NGINX_PORT }} default_server; + + location ~ ^/static/(?P.*) { + root {{ COMMON_DATA_DIR }}/{{ insights_service_name }}; + try_files /staticfiles/$file =404; + } + + location / { + try_files $uri @proxy_to_app; + } + + # No basic auth security on the heartbeat url, so that ELB can use it + location /status { + try_files $uri @proxy_to_app; + } + + {% include "robots.j2" %} + +location @proxy_to_app { + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + proxy_set_header X-Forwarded-Port $http_x_forwarded_port; + proxy_set_header X-Forwarded-For $http_x_forwarded_for; + proxy_set_header Host $http_host; + + proxy_redirect off; + proxy_pass http://insights_app_server; + } +} diff --git a/playbooks/roles/nginx/templates/kibana.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/kibana.j2 similarity index 89% rename from playbooks/roles/nginx/templates/kibana.j2 rename to playbooks/roles/nginx/templates/edx/app/nginx/sites-available/kibana.j2 index f07a1d7d9c9..e9a3152bd23 100755 --- a/playbooks/roles/nginx/templates/kibana.j2 +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/kibana.j2 @@ -13,14 +13,14 @@ server { {% if NGINX_ENABLE_SSL %} - listen {{KIBANA_NGINX_PORT}} {{default_site}}; - listen {{KIBANA_SSL_NGINX_PORT}} {{default_site}} ssl; + listen {{ KIBANA_NGINX_PORT }} {{ default_site }}; + listen {{ KIBANA_SSL_NGINX_PORT }} {{ default_site }} ssl; ssl_certificate /etc/ssl/certs/{{ NGINX_SSL_CERTIFICATE|basename }}; ssl_certificate_key /etc/ssl/private/{{ NGINX_SSL_KEY|basename }}; {% else %} - listen {{KIBANA_NGINX_PORT}} {{default_site}}; + listen {{ KIBANA_NGINX_PORT }} {{ default_site }}; {% endif %} server_name {{ KIBANA_SERVER_NAME }}; diff --git a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/lms-preview.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/lms-preview.j2 index 4b4631d7264..800680a31a3 100644 --- a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/lms-preview.j2 +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/lms-preview.j2 @@ -7,7 +7,7 @@ upstream lms-preview-backend { server { # LMS-preview configuration file for nginx, templated by ansible - listen {{EDXAPP_LMS_PREVIEW_NGINX_PORT}}; + listen {{ EDXAPP_LMS_PREVIEW_NGINX_PORT }}; server_name preview.*; diff --git a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/lms.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/lms.j2 index 9d84bf32bcc..fc2f86a7ece 100644 --- a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/lms.j2 +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/lms.j2 @@ -10,13 +10,21 @@ upstream lms-backend { {% endfor %} } +{%- if EDXAPP_ENABLE_RATE_LIMITING -%} +# Make Zone +limit_req_zone $cookie_{{ EDXAPP_SESSION_COOKIE_NAME }} zone=cookies:10m rate={{ EDXAPP_COURSES_REQUEST_RATE }}; +{%- endif -%} + server { # LMS configuration file for nginx, templated by ansible + # 500 error pages + error_page 500 502 504 /server/server-error.html; + {% if NGINX_ENABLE_SSL %} - listen {{EDXAPP_LMS_NGINX_PORT}} {{default_site}}; - listen {{EDXAPP_LMS_SSL_NGINX_PORT}} {{default_site}} ssl; + listen {{ EDXAPP_LMS_NGINX_PORT }} {{ default_site }}; + listen {{ EDXAPP_LMS_SSL_NGINX_PORT }} {{ default_site }} ssl; ssl_certificate /etc/ssl/certs/{{ NGINX_SSL_CERTIFICATE|basename }}; ssl_certificate_key /etc/ssl/private/{{ NGINX_SSL_KEY|basename }}; @@ -24,10 +32,10 @@ server { add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; {% else %} - listen {{EDXAPP_LMS_NGINX_PORT}} {{default_site}}; + listen {{ EDXAPP_LMS_NGINX_PORT }} {{ default_site }}; {% endif %} - access_log {{ nginx_log_dir }}/access.log; + access_log {{ nginx_log_dir }}/access.log {{ NGINX_LOG_FORMAT_NAME }}; error_log {{ nginx_log_dir }}/error.log error; # CS184 requires uploads of up to 4MB for submitting screenshots. @@ -57,17 +65,41 @@ server { {% include "basic-auth.j2" %} try_files $uri @proxy_to_lms_app; } + # No basic auth for /segmentio/event + location /segmentio/event { + try_files $uri @proxy_to_lms_app; + } + + location /notifier_api { + try_files $uri @proxy_to_lms_app; + } # No basic auth security on the github_service_hook url, so that github can use it for cms location /github_service_hook { try_files $uri @proxy_to_lms_app; } + # No basic auth security on oath2 endpoint + location /oauth2 { + try_files $uri @proxy_to_lms_app; + } + # No basic auth security on the heartbeat url, so that ELB can use it location /heartbeat { try_files $uri @proxy_to_lms_app; } + location /courses { + {%- if EDXAPP_ENABLE_RATE_LIMITING -%} + # Set Limit + limit_req zone=cookies burst={{ EDXAPP_COURSE_REQUEST_BURST_RATE }}; + error_page 503 = /server/rate-limit.html; + {%- endif -%} + + {%- include "basic-auth.j2" %} + try_files $uri @proxy_to_lms_app; + } + {% include "robots.j2" %} {% include "static-files.j2" %} diff --git a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/static-files.j2 b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/static-files.j2 index b6e547a0862..3cf4b9e89f3 100644 --- a/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/static-files.j2 +++ b/playbooks/roles/nginx/templates/edx/app/nginx/sites-available/static-files.j2 @@ -1,3 +1,9 @@ + # static pages for server status + location ~ ^/server/(?P.*) { + root /edx/var/nginx/server-static; + try_files /$file =404; + } + location ~ ^/static/(?P.*) { root {{ edxapp_data_dir }}; try_files /staticfiles/$file /course_static/$file =404; diff --git a/playbooks/roles/nginx/templates/edx/var/nginx/server-static/server-template.j2 b/playbooks/roles/nginx/templates/edx/var/nginx/server-static/server-template.j2 new file mode 100644 index 00000000000..3593161e21d --- /dev/null +++ b/playbooks/roles/nginx/templates/edx/var/nginx/server-static/server-template.j2 @@ -0,0 +1,42 @@ + + + + + {{ item.title }} + + + + + +
+

{{ item.heading }}

+ +

{{ item.title }}

+

{{ item.msg }} +

+ + diff --git a/playbooks/roles/nginx/templates/etc/nginx/nginx.conf.j2 b/playbooks/roles/nginx/templates/etc/nginx/nginx.conf.j2 index 1633d66d1ea..257184492e4 100644 --- a/playbooks/roles/nginx/templates/etc/nginx/nginx.conf.j2 +++ b/playbooks/roles/nginx/templates/etc/nginx/nginx.conf.j2 @@ -33,7 +33,7 @@ http { # Logging Settings ## - log_format p_combined '$http_x_forwarded_for - $remote_addr - $remote_user [$time_local] ' + log_format {{ NGINX_LOG_FORMAT_NAME }} '$http_x_forwarded_for - $remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent $request_time ' '"$http_referer" "$http_user_agent"'; diff --git a/playbooks/roles/nltk/tasks/main.yml b/playbooks/roles/nltk/tasks/main.yml index 2840ef63bb1..bd859d14f82 100644 --- a/playbooks/roles/nltk/tasks/main.yml +++ b/playbooks/roles/nltk/tasks/main.yml @@ -20,7 +20,7 @@ - name: unarchive nltk data shell: > - unzip {{NLTK_DATA_DIR}}/{{ item.url|basename }} chdir="{{ NLTK_DATA_DIR }}/{{ item.path|dirname }}" + unzip {{ NLTK_DATA_DIR }}/{{ item.url|basename }} chdir="{{ NLTK_DATA_DIR }}/{{ item.path|dirname }}" with_items: NLTK_DATA when: nltk_download|changed tags: diff --git a/playbooks/roles/notifier/defaults/main.yml b/playbooks/roles/notifier/defaults/main.yml index f9bc755d050..badba36ad12 100644 --- a/playbooks/roles/notifier/defaults/main.yml +++ b/playbooks/roles/notifier/defaults/main.yml @@ -8,11 +8,19 @@ NOTIFIER_DB_DIR: "{{ NOTIFIER_HOME }}/db" NOTIFIER_SOURCE_REPO: "https://github.com/edx/notifier.git" NOTIFIER_CODE_DIR: "{{ NOTIFIER_HOME }}/src" NOTIFIER_VERSION: "master" -NOTIFIER_GIT_IDENTITY_PATH: "{{ secure_dir }}/files/git-identity" NOTIFIER_REQUIREMENTS_FILE: "{{ NOTIFIER_CODE_DIR }}/requirements.txt" NOTIFIER_LOG_LEVEL: "INFO" NOTIFIER_RSYSLOG_ENABLED: "yes" NOTIFIER_DIGEST_TASK_INTERVAL: "1440" +NOTIFIER_FORUM_DIGEST_TASK_BATCH_SIZE: "5" +NOTIFIER_FORUM_DIGEST_TASK_RATE_LIMIT: "60/m" + +NOTIFIER_THEME_NAME: "" +NOTIFIER_THEME_REPO: "" +NOTIFIER_THEME_VERSION: "master" +notifier_git_ssh: "/tmp/notifier_git_ssh.sh" +NOTIFIER_GIT_IDENTITY: !!null +notifier_git_identity: "{{ NOTIFIER_HOME }}/notifier-git-identity" NOTIFIER_DIGEST_EMAIL_SENDER: "notifications@example.com" NOTIFIER_DIGEST_EMAIL_SUBJECT: "Daily Discussion Digest" @@ -20,6 +28,8 @@ NOTIFIER_DIGEST_EMAIL_TITLE: "Discussion Digest" NOTIFIER_DIGEST_EMAIL_DESCRIPTION: "A digest of unread content from course discussions you are following." NOTIFIER_EMAIL_SENDER_POSTAL_ADDRESS: "" +NOTIFIER_ENV_EXTRA: {} + NOTIFIER_LANGUAGE: "" NOTIFIER_ENV: "Development" @@ -89,7 +99,7 @@ notifier_env_vars: CS_API_KEY: "{{ NOTIFIER_COMMENT_SERVICE_API_KEY }}" US_URL_BASE: "{{ NOTIFIER_USER_SERVICE_BASE }}" US_API_KEY: "{{ NOTIFIER_USER_SERVICE_API_KEY }}" - DATADOG_API_KEY: "{{ DATADOG_API_KEY }}" + DATADOG_API_KEY: "{{ DATADOG_API_KEY|default('') }}" LOG_LEVEL: "{{ NOTIFIER_LOG_LEVEL }}" RSYSLOG_ENABLED: "{{ NOTIFIER_RSYSLOG_ENABLED }}" BROKER_URL: "{{ NOTIFIER_CELERY_BROKER_URL }}" @@ -98,3 +108,5 @@ notifier_env_vars: US_HTTP_AUTH_PASS: "{{ NOTIFIER_USER_SERVICE_HTTP_AUTH_PASS }}" FORUM_DIGEST_TASK_INTERVAL: "{{ NOTIFIER_DIGEST_TASK_INTERVAL }}" LOGO_IMAGE_URL: "{{ NOTIFIER_LOGO_IMAGE_URL }}" + FORUM_DIGEST_TASK_BATCH_SIZE: "{{ NOTIFIER_FORUM_DIGEST_TASK_BATCH_SIZE }}" + FORUM_DIGEST_TASK_RATE_LIMIT: "{{ NOTIFIER_FORUM_DIGEST_TASK_RATE_LIMIT }}" diff --git a/playbooks/roles/notifier/tasks/deploy.yml b/playbooks/roles/notifier/tasks/deploy.yml index 3b352f93e05..0580936224a 100644 --- a/playbooks/roles/notifier/tasks/deploy.yml +++ b/playbooks/roles/notifier/tasks/deploy.yml @@ -11,6 +11,45 @@ - restart notifier-scheduler - restart notifier-celery-workers +# Optional auth for git +- name: create ssh script for git (not authenticated) + template: > + src=git_ssh_noauth.sh.j2 dest={{ notifier_git_ssh }} + owner={{ NOTIFIER_USER }} mode=750 + when: not NOTIFIER_USE_GIT_IDENTITY + +- name: create ssh script for git (authenticated) + template: > + src=git_ssh_auth.sh.j2 dest={{ notifier_git_ssh }} + owner={{ NOTIFIER_USER }} mode=750 + when: NOTIFIER_USE_GIT_IDENTITY + +- name: install read-only ssh key + copy: > + content="{{ NOTIFIER_GIT_IDENTITY }}" dest={{ notifier_git_identity }} + force=yes owner={{ NOTIFIER_USER }} mode=0600 + when: NOTIFIER_USE_GIT_IDENTITY + +- name: checkout theme + git: > + dest={{ NOTIFIER_CODE_DIR }}/{{ NOTIFIER_THEME_NAME }} + repo={{ NOTIFIER_THEME_REPO }} + version={{ NOTIFIER_THEME_VERSION }} + accept_hostkey=yes + when: NOTIFIER_THEME_NAME != '' + sudo_user: "{{ NOTIFIER_USER }}" + environment: + GIT_SSH: "{{ notifier_git_ssh }}" + +- name: write notifier local settings + template: > + src=settings_local.py.j2 + dest={{ NOTIFIER_CODE_DIR }}/notifier/settings_local.py + mode=0555 + when: NOTIFIER_THEME_NAME != '' + notify: + - restart notifier-celery-workers + - name: source repo group perms file: path={{ NOTIFIER_SOURCE_REPO }} mode=2775 state=directory diff --git a/playbooks/roles/notifier/tasks/main.yml b/playbooks/roles/notifier/tasks/main.yml index 3ca24e4e356..793524138db 100644 --- a/playbooks/roles/notifier/tasks/main.yml +++ b/playbooks/roles/notifier/tasks/main.yml @@ -52,6 +52,7 @@ template: src=notifier_env.j2 dest={{ NOTIFIER_HOME }}/notifier_env owner="{{ NOTIFIER_USER }}" group="{{ NOTIFIER_USER }}" + mode=655 - name: drop a bash_profile copy: > @@ -85,6 +86,33 @@ file: path="{{ NOTIFIER_HOME }}/bin" mode=2775 state=directory owner={{ NOTIFIER_USER }} group={{ NOTIFIER_USER }} +- name: create notifier/.ssh directory + file: + path="{{ NOTIFIER_HOME }}/.ssh" mode=2700 state=directory owner={{ NOTIFIER_USER }} group={{ NOTIFIER_USER }} + +- name: create service log dir + file: > + path="{{ item }}" + state=directory + owner="syslog" + group="syslog" + with_items: + - "{{ COMMON_LOG_DIR }}/notifier" + +- name: write supervisord wrapper for celery workers + template: > + src=notifier-celery-workers-supervisor.sh.j2 + dest="{{ NOTIFIER_HOME }}/notifier-celery-workers-supervisor.sh" + mode=0775 + sudo_user: "{{ NOTIFIER_USER }}" + +- name: write supervisord wrapper for scheduler + template: > + src=notifier-scheduler-supervisor.sh.j2 + dest="{{ NOTIFIER_HOME }}/notifier-scheduler-supervisor.sh" + mode=0775 + sudo_user: "{{ NOTIFIER_USER }}" + - name: write supervisord config for celery workers template: > src=edx/app/supervisor/conf.d/notifier-celery-workers.conf.j2 diff --git a/playbooks/roles/notifier/templates/edx/app/supervisor/conf.d/notifier-celery-workers.conf.j2 b/playbooks/roles/notifier/templates/edx/app/supervisor/conf.d/notifier-celery-workers.conf.j2 index bfa88ba4b10..a18aaeffced 100644 --- a/playbooks/roles/notifier/templates/edx/app/supervisor/conf.d/notifier-celery-workers.conf.j2 +++ b/playbooks/roles/notifier/templates/edx/app/supervisor/conf.d/notifier-celery-workers.conf.j2 @@ -3,10 +3,11 @@ ; [program:notifier-celery-workers] -command={{ NOTIFIER_VENV_DIR }}/bin/python manage.py celery worker -l {{ NOTIFIER_LOG_LEVEL }} +command={{ NOTIFIER_HOME }}/notifier-celery-workers-supervisor.sh process_name=%(program_name)s numprocs=1 +stopasgroup=true directory={{ NOTIFIER_CODE_DIR }} umask=022 autostart=true @@ -25,7 +26,3 @@ stderr_logfile={{ NOTIFIER_SUPERVISOR_LOG_DEST }}/notifier-celery-workers-stderr stderr_logfile_maxbytes=1MB stderr_logfile_backups=10 stderr_capture_maxbytes=1MB -environment=PID='/var/tmp/notifier-celery-workers.pid',LANG=en_US.UTF-8, -{%- for name,value in notifier_env_vars.items() -%} -{%- if value -%}{{name}}="{{value}}"{%- if not loop.last -%},{%- endif -%}{%- endif -%} -{%- endfor -%} diff --git a/playbooks/roles/notifier/templates/edx/app/supervisor/conf.d/notifier-scheduler.conf.j2 b/playbooks/roles/notifier/templates/edx/app/supervisor/conf.d/notifier-scheduler.conf.j2 index 3b986b0951d..5dd8331165d 100644 --- a/playbooks/roles/notifier/templates/edx/app/supervisor/conf.d/notifier-scheduler.conf.j2 +++ b/playbooks/roles/notifier/templates/edx/app/supervisor/conf.d/notifier-scheduler.conf.j2 @@ -3,10 +3,11 @@ ; [program:notifier-scheduler] -command={{ NOTIFIER_VENV_DIR }}/bin/python manage.py scheduler +command={{ NOTIFIER_HOME }}/notifier-scheduler-supervisor.sh process_name=%(program_name)s numprocs=1 +stopasgroup=true directory={{ NOTIFIER_CODE_DIR }} umask=022 autostart=true @@ -25,7 +26,3 @@ stderr_logfile={{ NOTIFIER_SUPERVISOR_LOG_DEST }}/notifier-scheduler-stderr.log stderr_logfile_maxbytes=1MB stderr_logfile_backups=10 stderr_capture_maxbytes=1MB -environment=PID='/var/tmp/notifier-scheduler.pid',LANG=en_US.UTF-8, -{%- for name,value in notifier_env_vars.items() -%} -{%- if value -%}{{name}}="{{value}}"{%- if not loop.last -%},{%- endif -%}{%- endif -%} -{%- endfor -%} diff --git a/playbooks/roles/notifier/templates/git_ssh_auth.sh.j2 b/playbooks/roles/notifier/templates/git_ssh_auth.sh.j2 new file mode 100644 index 00000000000..355c0e72fb8 --- /dev/null +++ b/playbooks/roles/notifier/templates/git_ssh_auth.sh.j2 @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/ssh -o StrictHostKeyChecking=no -i {{ notifier_git_identity }} "$@" diff --git a/playbooks/roles/notifier/templates/git_ssh_noauth.sh.j2 b/playbooks/roles/notifier/templates/git_ssh_noauth.sh.j2 new file mode 100644 index 00000000000..e30af2deeb1 --- /dev/null +++ b/playbooks/roles/notifier/templates/git_ssh_noauth.sh.j2 @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/ssh -o StrictHostKeyChecking=no "$@" diff --git a/playbooks/roles/notifier/templates/notifier-celery-workers-supervisor.sh.j2 b/playbooks/roles/notifier/templates/notifier-celery-workers-supervisor.sh.j2 new file mode 100644 index 00000000000..e4af0bdee69 --- /dev/null +++ b/playbooks/roles/notifier/templates/notifier-celery-workers-supervisor.sh.j2 @@ -0,0 +1,10 @@ +#!/bin/bash + +source {{ NOTIFIER_HOME }}/notifier_env +cd {{ NOTIFIER_CODE_DIR }} + +export PID='/var/tmp/notifier-scheduler.pid' +export LANG=en_US.UTF-8 + +{{ NOTIFIER_VENV_DIR }}/bin/python manage.py celery worker -l {{ NOTIFIER_LOG_LEVEL }} + diff --git a/playbooks/roles/notifier/templates/notifier-scheduler-supervisor.sh.j2 b/playbooks/roles/notifier/templates/notifier-scheduler-supervisor.sh.j2 new file mode 100644 index 00000000000..4c8f54cb61f --- /dev/null +++ b/playbooks/roles/notifier/templates/notifier-scheduler-supervisor.sh.j2 @@ -0,0 +1,10 @@ +#!/bin/bash + +source {{ NOTIFIER_HOME }}/notifier_env +cd {{ NOTIFIER_CODE_DIR }} + +export PID='/var/tmp/notifier-celery-workers.pid' +export LANG=en_US.UTF-8 + +{{ NOTIFIER_VENV_DIR }}/bin/python manage.py scheduler + diff --git a/playbooks/roles/notifier/templates/notifier_env.j2 b/playbooks/roles/notifier/templates/notifier_env.j2 index 283cf98cea4..e8ecd677a6d 100644 --- a/playbooks/roles/notifier/templates/notifier_env.j2 +++ b/playbooks/roles/notifier/templates/notifier_env.j2 @@ -1,7 +1,11 @@ # {{ ansible_managed }} +{% do notifier_env_vars.update(NOTIFIER_ENV_EXTRA) %} {% for name,value in notifier_env_vars.items() %} {% if value %} export {{ name }}="{{ value }}" {% endif %} {% endfor %} +{% if NOTIFIER_THEME_NAME != "" %} +export DJANGO_SETTINGS_MODULE=notifier.settings_local +{% endif %} diff --git a/playbooks/roles/notifier/templates/settings_local.py.j2 b/playbooks/roles/notifier/templates/settings_local.py.j2 new file mode 100644 index 00000000000..b1f36d73039 --- /dev/null +++ b/playbooks/roles/notifier/templates/settings_local.py.j2 @@ -0,0 +1,6 @@ +from .settings import * + +FORUM_DIGEST_EMAIL_SUBJECT = '{{ NOTIFIER_DIGEST_EMAIL_SUBJECT }}' + +CUSTOM_THEME_DIR = '{{ NOTIFIER_CODE_DIR }}/{{ NOTIFIER_THEME_NAME }}/' +TEMPLATE_DIRS = (CUSTOM_THEME_DIR + 'templates',) diff --git a/playbooks/roles/ora/defaults/main.yml b/playbooks/roles/ora/defaults/main.yml index 1320585d4f7..667f5ec9387 100644 --- a/playbooks/roles/ora/defaults/main.yml +++ b/playbooks/roles/ora/defaults/main.yml @@ -1,6 +1,7 @@ # vars for the ORA role --- ORA_NGINX_PORT: 18060 +ORA_GUNICORN_EXTRA: "" ora_app_dir: "{{ COMMON_APP_DIR }}/ora" ora_code_dir: "{{ ora_app_dir }}/ora" @@ -86,61 +87,61 @@ ora_gunicorn_host: 127.0.0.1 # appropriate for running all edX # services on a single server. ora_env_config: - LOGGING_ENV: $ORA_LOGGING_ENV + LOGGING_ENV: "{{ ORA_LOGGING_ENV }}" LOG_DIR: "{{ COMMON_DATA_DIR }}/logs/xqueue" COURSE_DATA_PATH: "{{ ora_data_course_dir }}" - REQUESTS_TIMEOUT: $ORA_REQUESTS_TIMEOUT - QUEUES_TO_PULL_FROM: $ORA_QUEUES_TO_PULL_FROM - TIME_BETWEEN_XQUEUE_PULLS: $ORA_TIME_BETWEEN_XQUEUE_PULLS - TIME_BETWEEN_EXPIRED_CHECKS: $ORA_TIME_BETWEEN_EXPIRED_CHECKS - GRADER_SETTINGS_DIRECTORY: $ORA_GRADER_SETTINGS_DIRECTORY - MAX_NUMBER_OF_TIMES_TO_RETRY_GRADING: $ORA_MAX_NUMBER_OF_TIMES_TO_RETRY_GRADING - MIN_TO_USE_ML: $ORA_MIN_TO_USE_ML - ML_PATH: $ORA_ML_PATH - ML_MODEL_PATH: $ORA_ML_MODEL_PATH - TIME_BETWEEN_ML_CREATOR_CHECKS: $ORA_TIME_BETWEEN_ML_CREATOR_CHECKS - TIME_BETWEEN_ML_GRADER_CHECKS: $ORA_TIME_BETWEEN_ML_GRADER_CHECKS - MIN_TO_USE_PEER: $ORA_MIN_TO_USE_PEER - PEER_GRADER_COUNT: $ORA_PEER_GRADER_COUNT - PEER_GRADER_MINIMUM_TO_CALIBRATE: $ORA_PEER_GRADER_MINIMUM_TO_CALIBRATE - PEER_GRADER_MAXIMUM_TO_CALIBRATE: $ORA_PEER_GRADER_MAXIMUM_TO_CALIBRATE - PEER_GRADER_MIN_NORMALIZED_CALIBRATION_ERROR: $ORA_PEER_GRADER_MIN_NORMALIZED_CALIBRATION_ERROR - EXPIRE_SUBMISSIONS_AFTER: $ORA_EXPIRE_SUBMISSIONS_AFTER - RESET_SUBMISSIONS_AFTER: $ORA_RESET_SUBMISSIONS_AFTER - LOCAL_LOGLEVEL: $ORA_LOCAL_LOGLEVEL - DEBUG: $ORA_DEBUG + REQUESTS_TIMEOUT: "{{ ORA_REQUESTS_TIMEOUT }}" + QUEUES_TO_PULL_FROM: "{{ ORA_QUEUES_TO_PULL_FROM }}" + TIME_BETWEEN_XQUEUE_PULLS: "{{ ORA_TIME_BETWEEN_XQUEUE_PULLS }}" + TIME_BETWEEN_EXPIRED_CHECKS: "{{ ORA_TIME_BETWEEN_EXPIRED_CHECKS }}" + GRADER_SETTINGS_DIRECTORY: "{{ ORA_GRADER_SETTINGS_DIRECTORY }}" + MAX_NUMBER_OF_TIMES_TO_RETRY_GRADING: "{{ ORA_MAX_NUMBER_OF_TIMES_TO_RETRY_GRADING }}" + MIN_TO_USE_ML: "{{ ORA_MIN_TO_USE_ML }}" + ML_PATH: "{{ ORA_ML_PATH }}" + ML_MODEL_PATH: "{{ ORA_ML_MODEL_PATH }}" + TIME_BETWEEN_ML_CREATOR_CHECKS: "{{ ORA_TIME_BETWEEN_ML_CREATOR_CHECKS }}" + TIME_BETWEEN_ML_GRADER_CHECKS: "{{ ORA_TIME_BETWEEN_ML_GRADER_CHECKS }}" + MIN_TO_USE_PEER: "{{ ORA_MIN_TO_USE_PEER }}" + PEER_GRADER_COUNT: "{{ ORA_PEER_GRADER_COUNT }}" + PEER_GRADER_MINIMUM_TO_CALIBRATE: "{{ ORA_PEER_GRADER_MINIMUM_TO_CALIBRATE }}" + PEER_GRADER_MAXIMUM_TO_CALIBRATE: "{{ ORA_PEER_GRADER_MAXIMUM_TO_CALIBRATE }}" + PEER_GRADER_MIN_NORMALIZED_CALIBRATION_ERROR: "{{ ORA_PEER_GRADER_MIN_NORMALIZED_CALIBRATION_ERROR }}" + EXPIRE_SUBMISSIONS_AFTER: "{{ ORA_EXPIRE_SUBMISSIONS_AFTER }}" + RESET_SUBMISSIONS_AFTER: "{{ ORA_RESET_SUBMISSIONS_AFTER }}" + LOCAL_LOGLEVEL: "{{ ORA_LOCAL_LOGLEVEL }}" + DEBUG: "{{ ORA_DEBUG }}" SYSLOG_SERVER: ORA_SYSLOG_SERVER USE_S3_TO_STORE_MODELS: ORA_USE_S3_TO_STORE_MODELS - S3_BUCKETNAME: $ORA_S3_BUCKETNAME + S3_BUCKETNAME: "{{ ORA_S3_BUCKETNAME }}" ora_auth_config: - USERS: $ORA_USERS + USERS: "{{ ORA_USERS }}" XQUEUE_INTERFACE: django_auth: - username: $ORA_XQUEUE_DJANGO_USER - password: $ORA_XQUEUE_DJANGO_PASSWORD - basic_auth: [ $ORA_XQUEUE_BASIC_AUTH_USER, $ORA_XQUEUE_BASIC_AUTH_PASSWORD ] - url: $ORA_XQUEUE_URL + username: "{{ ORA_XQUEUE_DJANGO_USER }}" + password: "{{ ORA_XQUEUE_DJANGO_PASSWORD }}" + basic_auth: [ "{{ ORA_XQUEUE_BASIC_AUTH_USER }}", "{{ORA_XQUEUE_BASIC_AUTH_PASSWORD}}" ] + url: "{{ ORA_XQUEUE_URL }}" GRADING_CONTROLLER_INTERFACE: django_auth: - password: $ORA_DJANGO_PASSWORD - username: $ORA_DJANGO_USER - url: $ORA_URL + password: "{{ ORA_DJANGO_PASSWORD }}" + username: "{{ ORA_DJANGO_USER }}" + url: "{{ ORA_URL }}" DATABASES: default: ENGINE: 'django.db.backends.mysql' - NAME: $ORA_MYSQL_DB_NAME - USER: $ORA_MYSQL_USER - PASSWORD: $ORA_MYSQL_PASSWORD - HOST: $ORA_MYSQL_HOST - PORT: $ORA_MYSQL_PORT - AWS_ACCESS_KEY_ID: $ORA_AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY: $ORA_AWS_SECRET_ACCESS_KEY + NAME: "{{ ORA_MYSQL_DB_NAME }}" + USER: "{{ ORA_MYSQL_USER }}" + PASSWORD: "{{ ORA_MYSQL_PASSWORD }}" + HOST: "{{ ORA_MYSQL_HOST }}" + PORT: "{{ ORA_MYSQL_PORT }}" + AWS_ACCESS_KEY_ID: "{{ ORA_AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "{{ ORA_AWS_SECRET_ACCESS_KEY }}" ora_environment: SERVICE_VARIANT: ora - LANG: $ORA_LANG - PATH: $ora_deploy_path + LANG: "{{ ORA_LANG }}" + PATH: "{{ ora_deploy_path }}" ora_debian_pkgs: - python-software-properties diff --git a/playbooks/roles/ora/tasks/deploy.yml b/playbooks/roles/ora/tasks/deploy.yml index d5b60f71061..011144f528e 100644 --- a/playbooks/roles/ora/tasks/deploy.yml +++ b/playbooks/roles/ora/tasks/deploy.yml @@ -22,11 +22,11 @@ - include: ease.yml - name: create ora application config - template: src=ora.env.json.j2 dest={{ora_app_dir}}/ora.env.json + template: src=ora.env.json.j2 dest={{ ora_app_dir }}/ora.env.json sudo_user: "{{ ora_user }}" - name: create ora auth file - template: src=ora.auth.json.j2 dest={{ora_app_dir}}/ora.auth.json + template: src=ora.auth.json.j2 dest={{ ora_app_dir }}/ora.auth.json sudo_user: "{{ ora_user }}" - name: setup the ora env @@ -80,7 +80,7 @@ - restart ora_celery - name: syncdb and migrate - shell: SERVICE_VARIANT=ora {{ora_venv_dir}}/bin/django-admin.py syncdb --migrate --noinput --settings=edx_ora.aws --pythonpath={{ora_code_dir}} + shell: SERVICE_VARIANT=ora {{ ora_venv_dir }}/bin/django-admin.py syncdb --migrate --noinput --settings=edx_ora.aws --pythonpath={{ ora_code_dir }} when: migrate_db is defined and migrate_db|lower == "yes" sudo_user: "{{ ora_user }}" notify: @@ -88,7 +88,7 @@ - restart ora_celery - name: create users - shell: SERVICE_VARIANT=ora {{ora_venv_dir}}/bin/django-admin.py update_users --settings=edx_ora.aws --pythonpath={{ora_code_dir}} + shell: SERVICE_VARIANT=ora {{ ora_venv_dir }}/bin/django-admin.py update_users --settings=edx_ora.aws --pythonpath={{ ora_code_dir }} sudo_user: "{{ ora_user }}" notify: - restart ora diff --git a/playbooks/roles/ora/tasks/ease.yml b/playbooks/roles/ora/tasks/ease.yml index fe4786a540a..10af195d0dc 100644 --- a/playbooks/roles/ora/tasks/ease.yml +++ b/playbooks/roles/ora/tasks/ease.yml @@ -1,7 +1,7 @@ # Do A Checkout - name: git checkout ease repo into its base dir git: > - dest={{ora_ease_code_dir}} repo={{ora_ease_source_repo}} version={{ora_ease_version}} + dest={{ ora_ease_code_dir }} repo={{ ora_ease_source_repo }} version={{ora_ease_version}} accept_hostkey=yes sudo_user: "{{ ora_user }}" notify: @@ -9,7 +9,7 @@ - restart ora_celery - name: install ease system packages - apt: pkg={{item}} state=present + apt: pkg={{ item }} state=present with_items: ora_ease_debian_pkgs notify: - restart ora @@ -19,7 +19,7 @@ # Install the python pre requirements into {{ ora_ease_venv_dir }} - name: install ease python pre-requirements pip: > - requirements="{{ora_ease_pre_requirements_file}}" virtualenv="{{ora_ease_venv_dir}}" state=present + requirements="{{ ora_ease_pre_requirements_file }}" virtualenv="{{ ora_ease_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ ora_user }}" notify: @@ -29,7 +29,7 @@ # Install the python post requirements into {{ ora_ease_venv_dir }} - name: install ease python post-requirements pip: > - requirements="{{ora_ease_post_requirements_file}}" virtualenv="{{ora_ease_venv_dir}}" state=present + requirements="{{ ora_ease_post_requirements_file }}" virtualenv="{{ ora_ease_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ ora_user }}" notify: diff --git a/playbooks/roles/ora/tasks/main.yml b/playbooks/roles/ora/tasks/main.yml index 7c3bfa85934..e0a0b70cf42 100644 --- a/playbooks/roles/ora/tasks/main.yml +++ b/playbooks/roles/ora/tasks/main.yml @@ -35,14 +35,14 @@ - "{{ ora_app_dir }}/ml_models" - name: install debian packages that ora needs - apt: pkg={{item}} state=present + apt: pkg={{ item }} state=present notify: - restart ora - restart ora_celery with_items: ora_debian_pkgs - name: install debian packages for ease that ora needs - apt: pkg={{item}} state=present + apt: pkg={{ item }} state=present notify: - restart ora - restart ora_celery diff --git a/playbooks/roles/ora/templates/ora.conf.j2 b/playbooks/roles/ora/templates/ora.conf.j2 index d655f057f19..c2ea9062187 100644 --- a/playbooks/roles/ora/templates/ora.conf.j2 +++ b/playbooks/roles/ora/templates/ora.conf.j2 @@ -1,6 +1,6 @@ [program:ora] -command={{ ora_venv_bin }}/gunicorn --preload -b {{ ora_gunicorn_host }}:{{ ora_gunicorn_port }} -w {{ ora_gunicorn_workers }} --timeout=90 --pythonpath={{ ora_code_dir}} edx_ora.wsgi +command={{ ora_venv_bin }}/gunicorn --preload -b {{ ora_gunicorn_host }}:{{ ora_gunicorn_port }} -w {{ ora_gunicorn_workers }} --timeout=90 --pythonpath={{ ora_code_dir}} {{ ORA_GUNICORN_EXTRA }} edx_ora.wsgi user={{ common_web_user }} directory={{ ora_code_dir }} diff --git a/playbooks/roles/oraclejdk/tasks/main.yml b/playbooks/roles/oraclejdk/tasks/main.yml index f41fdb22833..ab9d27d778a 100644 --- a/playbooks/roles/oraclejdk/tasks/main.yml +++ b/playbooks/roles/oraclejdk/tasks/main.yml @@ -15,9 +15,9 @@ - name: download Oracle Java shell: > curl -b gpw_e24=http%3A%2F%2Fwww.oracle.com -b oraclelicense=accept-securebackup-cookie -O -L {{ oraclejdk_url }} - executable=/bin/bash - chdir=/var/tmp - creates=/var/tmp/{{ oraclejdk_file }} + executable=/bin/bash + chdir=/var/tmp + creates=/var/tmp/{{ oraclejdk_file }} - name: create jvm dir file: > diff --git a/playbooks/roles/oraclejdk/templates/java.sh.j2 b/playbooks/roles/oraclejdk/templates/java.sh.j2 index 0562b22beb7..5b67a9d45de 100644 --- a/playbooks/roles/oraclejdk/templates/java.sh.j2 +++ b/playbooks/roles/oraclejdk/templates/java.sh.j2 @@ -1,2 +1,2 @@ -export JAVA_HOME="{{oraclejdk_link}}" +export JAVA_HOME="{{ oraclejdk_link }}" export PATH=$JAVA_HOME/bin:$PATH diff --git a/playbooks/roles/rabbitmq/defaults/main.yml b/playbooks/roles/rabbitmq/defaults/main.yml index 986ab13eb92..4760c044b66 100644 --- a/playbooks/roles/rabbitmq/defaults/main.yml +++ b/playbooks/roles/rabbitmq/defaults/main.yml @@ -43,9 +43,9 @@ rabbitmq_debian_pkgs: rabbitmq_config_dir: "/etc/rabbitmq" rabbitmq_cookie_dir: "/var/lib/rabbitmq" -rabbitmq_cookie_location: "{{rabbitmq_cookie_dir}}/.erlang.cookie" +rabbitmq_cookie_location: "{{ rabbitmq_cookie_dir }}/.erlang.cookie" -rabbitmq_mnesia_folder: "{{rabbitmq_cookie_dir}}/mnesia" +rabbitmq_mnesia_folder: "{{ rabbitmq_cookie_dir }}/mnesia" rabbitmq_port: 5672 rabbitmq_management_port: 15672 @@ -53,8 +53,8 @@ rabbitmq_ip: "{{ ansible_default_ipv4.address }}" # Structure for auth config file. rabbitmq_auth_config: - erlang_cookie: $RABBIT_ERLANG_COOKIE - admins: $RABBIT_USERS + erlang_cookie: "{{ RABBIT_ERLANG_COOKIE }}" + admins: "{{ RABBIT_USERS }}" rabbitmq_clustered_hosts: [] diff --git a/playbooks/roles/rabbitmq/tasks/main.yml b/playbooks/roles/rabbitmq/tasks/main.yml index f10b0b7d747..6b1f32b8410 100644 --- a/playbooks/roles/rabbitmq/tasks/main.yml +++ b/playbooks/roles/rabbitmq/tasks/main.yml @@ -4,13 +4,13 @@ # http://rabbitmq.1065348.n5.nabble.com/Rabbitmq-boot-failure-with-quot-tables-not-present-quot-td24494.html - name: trust rabbit repository - apt_key: url={{rabbitmq_apt_key}} state=present + apt_key: url={{ rabbitmq_apt_key }} state=present - name: install python-software-properties if debian - apt: pkg={{",".join(rabbitmq_debian_pkgs)}} state=present + apt: pkg={{ ",".join(rabbitmq_debian_pkgs) }} state=present - name: add rabbit repository - apt_repository_1.8: repo="{{rabbitmq_repository}}" state=present update_cache=yes validate_certs=no + apt_repository_1.8: repo="{{ rabbitmq_repository }}" state=present update_cache=yes validate_certs=no - name: fetch the rabbitmq server deb get_url: > @@ -63,30 +63,30 @@ # Defaulting to /var/lib/rabbitmq - name: create cookie directory file: > - path={{rabbitmq_cookie_dir}} + path={{ rabbitmq_cookie_dir }} owner=rabbitmq group=rabbitmq mode=0755 state=directory - name: add rabbitmq erlang cookie template: > - src=erlang.cookie.j2 dest={{rabbitmq_cookie_location}} + src=erlang.cookie.j2 dest={{ rabbitmq_cookie_location }} owner=rabbitmq group=rabbitmq mode=0400 register: erlang_cookie # Defaulting to /etc/rabbitmq - name: create rabbitmq config directory file: > - path={{rabbitmq_config_dir}} + path={{ rabbitmq_config_dir }} owner=root group=root mode=0755 state=directory - name: add rabbitmq environment configuration template: > - src=rabbitmq-env.conf.j2 dest={{rabbitmq_config_dir}}/rabbitmq-env.conf + src=rabbitmq-env.conf.j2 dest={{ rabbitmq_config_dir }}/rabbitmq-env.conf owner=root group=root mode=0644 - name: add rabbitmq cluster configuration template: > src=etc/rabbitmq/rabbitmq.config.j2 - dest={{rabbitmq_config_dir}}/rabbitmq.config + dest={{ rabbitmq_config_dir }}/rabbitmq.config owner=root group=root mode=0644 register: cluster_configuration @@ -98,7 +98,7 @@ # This folder should be deleted before clustering is setup because it retains data # that can conflict with the clustering information. - name: remove mnesia configuration - file: path={{rabbitmq_mnesia_folder}} state=absent + file: path={{ rabbitmq_mnesia_folder }} state=absent when: erlang_cookie.changed or cluster_configuration.changed or rabbitmq_refresh - name: start rabbit nodes @@ -124,7 +124,7 @@ configure_priv='.*' tags="administrator" state=present vhost={{ item[1] }} with_nested: - - ${rabbitmq_auth_config.admins} + - "{{rabbitmq_auth_config.admins}}" - RABBITMQ_VHOSTS when: "'admins' in rabbitmq_auth_config" tags: diff --git a/playbooks/roles/rabbitmq/templates/etc/rabbitmq/rabbitmq.config.j2 b/playbooks/roles/rabbitmq/templates/etc/rabbitmq/rabbitmq.config.j2 index ff7eef4e294..36f0513d121 100644 --- a/playbooks/roles/rabbitmq/templates/etc/rabbitmq/rabbitmq.config.j2 +++ b/playbooks/roles/rabbitmq/templates/etc/rabbitmq/rabbitmq.config.j2 @@ -1,5 +1,7 @@ % {{ ansible_managed }} +[{rabbit, [ + {log_levels, [{connection, info}]}, {% if RABBITMQ_CLUSTERED -%} {%- set hosts= [] -%} @@ -7,14 +9,12 @@ {% do hosts.append("rabbit@ip-" + host.replace('.','-')) %} {%- endfor %} -[{rabbit, - [{cluster_nodes, {['{{ hosts|join("\',\'") }}'], disc}}]}]. + {cluster_nodes, {['{{ hosts|join("\',\'") }}'], disc}} {%- else -%} {# If rabbitmq_clustered_hosts is set, use that instead assuming an aws stack. Note: That these names should include the node name prefix. eg. 'rabbit@hostname' #} -[{rabbit, - [{cluster_nodes, {['{{ rabbitmq_clustered_hosts|join("\',\'") }}'], disc}}]}]. - -{%- endif -%} + {cluster_nodes, {['{{ rabbitmq_clustered_hosts|join("\',\'") }}'], disc}} +{%- endif %} +]}]. diff --git a/playbooks/roles/security/defaults/main.yml b/playbooks/roles/security/defaults/main.yml new file mode 100644 index 00000000000..e91bff4d6e0 --- /dev/null +++ b/playbooks/roles/security/defaults/main.yml @@ -0,0 +1,34 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +## +# Defaults for role security +# + +# +# vars are namespace with the module name. +# +security_role_name: security +# set to true to enable unattended upgrades nightly +SECURITY_UNATTENDED_UPGRADES: false +# set to true to upgrade all packages nightly. false will only upgrade from security repo. +SECURITY_UPDATE_ALL_PACKAGES: false +# set to true to run aptitute safe-upgrade whenever ansible is run +SECURITY_UPGRADE_ON_ANSIBLE: false + + +# +# OS packages +# + +security_debian_pkgs: + - aptitude + - unattended-upgrades + +security_redhat_pkgs: [] diff --git a/playbooks/roles/security/tasks/main.yml b/playbooks/roles/security/tasks/main.yml new file mode 100644 index 00000000000..a51c57d6835 --- /dev/null +++ b/playbooks/roles/security/tasks/main.yml @@ -0,0 +1,26 @@ +--- +# +# edX Configuration +# +# github: https://github.com/edx/configuration +# wiki: https://github.com/edx/configuration/wiki +# code style: https://github.com/edx/configuration/wiki/Ansible-Coding-Conventions +# license: https://github.com/edx/configuration/blob/master/LICENSE.TXT +# +# +# +# Tasks for role security +# +# Overview: +# +# +# Dependencies: +# +# +# Example play: +# +# + +- include: security-ubuntu.yml + when: + - ansible_distribution == 'Ubuntu' diff --git a/playbooks/roles/security/tasks/security-ubuntu.yml b/playbooks/roles/security/tasks/security-ubuntu.yml new file mode 100644 index 00000000000..45f1368dbe3 --- /dev/null +++ b/playbooks/roles/security/tasks/security-ubuntu.yml @@ -0,0 +1,50 @@ +#### Enable periodic security updates + +- name: install security packages + apt: name={{ item }} state=latest + with_items: security_debian_pkgs + + +- name: update all system packages + apt: upgrade=safe + when: SECURITY_UPGRADE_ON_ANSIBLE + +- name: configure periodic unattended-upgrades + template: > + src=etc/apt/apt.conf.d/10periodic + dest=/etc/apt/apt.conf.d/10periodic + owner=root group=root mode=0644 + when: SECURITY_UNATTENDED_UPGRADES + +- name: disable unattended-upgrades + file: path=/etc/apt/apt.conf.d/10periodic state=absent + when: not SECURITY_UNATTENDED_UPGRADES + +- name: only unattended-upgrade from security repo + template: > + src=etc/apt/apt.conf.d/20unattended-upgrade + dest=/etc/apt/apt.conf.d/20unattended-upgrade + owner=root group=root mode=0644 + when: SECURITY_UNATTENDED_UPGRADES and not SECURITY_UPDATE_ALL_PACKAGES + +- name: disable security only updates on unattended-upgrades + file: path=/etc/apt/apt.conf.d/20unattended-upgrade state=absent + when: SECURITY_UPDATE_ALL_PACKAGES or not SECURITY_UNATTENDED_UPGRADES + + +#### Bash security vulnerability + +- name: Check if we are vulnerable + shell: executable=/bin/bash chdir=/tmp foo='() { echo vulnerable; }' bash -c foo + register: test_vuln + ignore_errors: yes + +- name: Apply bash security update if we are vulnerable + apt: name=bash state=latest update_cache=true + when: "'vulnerable' in test_vuln.stdout" + +- name: Check again and fail if we are still vulnerable + shell: executable=/bin/bash foo='() { echo vulnerable; }' bash -c foo + when: "'vulnerable' in test_vuln.stdout" + register: test_vuln + failed_when: "'vulnerable' in test_vuln.stdout" diff --git a/playbooks/roles/security/templates/etc/apt/apt.conf.d/10periodic b/playbooks/roles/security/templates/etc/apt/apt.conf.d/10periodic new file mode 100644 index 00000000000..20c4b2949dd --- /dev/null +++ b/playbooks/roles/security/templates/etc/apt/apt.conf.d/10periodic @@ -0,0 +1,5 @@ +APT::Periodic::Enable "1"; +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Download-Upgradeable-Packages "1"; +APT::Periodic::AutocleanInterval "7"; +APT::Periodic::Unattended-Upgrade "1"; diff --git a/playbooks/roles/security/templates/etc/apt/apt.conf.d/20unattended-upgrade b/playbooks/roles/security/templates/etc/apt/apt.conf.d/20unattended-upgrade new file mode 100644 index 00000000000..6dc92f3b131 --- /dev/null +++ b/playbooks/roles/security/templates/etc/apt/apt.conf.d/20unattended-upgrade @@ -0,0 +1,4 @@ + +Unattended-Upgrade::Allowed-Origins { + "${distro_id} ${distro_codename}-security"; +}; diff --git a/playbooks/roles/shibboleth/tasks/main.yml b/playbooks/roles/shibboleth/tasks/main.yml index da42b5f5321..2dd19e904f4 100644 --- a/playbooks/roles/shibboleth/tasks/main.yml +++ b/playbooks/roles/shibboleth/tasks/main.yml @@ -2,7 +2,7 @@ --- - name: Installs shib and dependencies from apt - apt: pkg={{item}} install_recommends=no state=present update_cache=yes + apt: pkg={{ item }} install_recommends=no state=present update_cache=yes with_items: - shibboleth-sp2-schemas - libshibsp-dev @@ -24,14 +24,14 @@ when: shib_download_metadata - name: writes out key and pem file - template: src=sp.{{item}}.j2 dest=/etc/shibboleth/sp.{{item}} group=_shibd owner=_shibd mode=0600 + template: src=sp.{{ item }}.j2 dest=/etc/shibboleth/sp.{{ item }} group=_shibd owner=_shibd mode=0600 with_items: - key - pem notify: restart shibd - name: writes out configuration files - template: src={{ shib_template_dir }}/{{item}}.j2 dest=/etc/shibboleth/{{item}} group=_shibd owner=_shibd mode=0644 + template: src={{ shib_template_dir }}/{{ item }}.j2 dest=/etc/shibboleth/{{ item }} group=_shibd owner=_shibd mode=0644 with_items: - attribute-map.xml - shibboleth2.xml diff --git a/playbooks/roles/splunkforwarder/defaults/main.yml b/playbooks/roles/splunkforwarder/defaults/main.yml index 5e146e895b0..494a961aa1e 100644 --- a/playbooks/roles/splunkforwarder/defaults/main.yml +++ b/playbooks/roles/splunkforwarder/defaults/main.yml @@ -28,23 +28,23 @@ SPLUNKFORWARDER_SERVERS: SPLUNKFORWARDER_LOG_ITEMS: - source: '{{ COMMON_LOG_DIR }}/lms' recursive: true - index: '{{COMMON_ENVIRONMENT}}-{{COMMON_DEPLOYMENT}}' + index: '{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}' sourcetype: 'edx' - source: '{{ COMMON_LOG_DIR }}/cms' recursive: true - index: '{{COMMON_ENVIRONMENT}}-{{COMMON_DEPLOYMENT}}' + index: '{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}' sourcetype: 'edx' - source: '{{ COMMON_LOG_DIR }}' recursive: true - index: '{{COMMON_ENVIRONMENT}}-{{COMMON_DEPLOYMENT}}' + index: '{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}' sourcetype: 'syslog' - source: '/var/log' recursive: true - index: '{{COMMON_ENVIRONMENT}}-{{COMMON_DEPLOYMENT}}' + index: '{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}' sourcetype: 'syslog' - source: '{{ COMMON_LOG_DIR }}/nginx' recursive: true - index: '{{COMMON_ENVIRONMENT}}-{{COMMON_DEPLOYMENT}}' + index: '{{ COMMON_ENVIRONMENT }}-{{ COMMON_DEPLOYMENT }}' sourcetype: 'nginx' # diff --git a/playbooks/roles/splunkforwarder/tasks/main.yml b/playbooks/roles/splunkforwarder/tasks/main.yml index 8c2adf53dea..43fe30a7b2c 100644 --- a/playbooks/roles/splunkforwarder/tasks/main.yml +++ b/playbooks/roles/splunkforwarder/tasks/main.yml @@ -31,12 +31,12 @@ - name: download the splunk deb get_url: > - dest="/tmp/{{SPLUNKFORWARDER_DEB}}" - url="{{SPLUNKFORWARDER_PACKAGE_URL}}" + dest="/tmp/{{ SPLUNKFORWARDER_DEB }}" + url="{{ SPLUNKFORWARDER_PACKAGE_URL }}" register: download_deb - name: install splunk forwarder - shell: gdebi -nq /tmp/{{SPLUNKFORWARDER_DEB}} + shell: gdebi -nq /tmp/{{ SPLUNKFORWARDER_DEB }} when: download_deb.changed # Create splunk user @@ -49,27 +49,27 @@ # to run some of the below commands. - name: start splunk manually shell: > - {{splunkforwarder_output_dir}}/bin/splunk start --accept-license --answer-yes --no-prompt - creates={{splunkforwarder_output_dir}}/var/lib/splunk + {{ splunkforwarder_output_dir }}/bin/splunk start --accept-license --answer-yes --no-prompt + creates={{ splunkforwarder_output_dir }}/var/lib/splunk when: download_deb.changed register: started_manually - name: stop splunk manually shell: > - {{splunkforwarder_output_dir}}/bin/splunk stop --accept-license --answer-yes --no-prompt + {{ splunkforwarder_output_dir }}/bin/splunk stop --accept-license --answer-yes --no-prompt when: download_deb.changed and started_manually.changed - name: create boot script shell: > - {{splunkforwarder_output_dir}}/bin/splunk enable boot-start -user splunk --accept-license --answer-yes --no-prompt - creates=/etc/init.d/splunk + {{ splunkforwarder_output_dir }}/bin/splunk enable boot-start -user splunk --accept-license --answer-yes --no-prompt + creates=/etc/init.d/splunk register: create_boot_script when: download_deb.changed notify: restart splunkforwarder # Update credentials - name: update admin pasword - shell: "{{splunkforwarder_output_dir}}/bin/splunk edit user admin -password {{SPLUNKFORWARDER_PASSWORD}} -auth admin:changeme --accept-license --answer-yes --no-prompt" + shell: "{{ splunkforwarder_output_dir }}/bin/splunk edit user admin -password {{ SPLUNKFORWARDER_PASSWORD }} -auth admin:changeme --accept-license --answer-yes --no-prompt" when: download_deb.changed notify: restart splunkforwarder @@ -80,7 +80,7 @@ # Ensure permissions on splunk content - name: ensure splunk forder permissions - file: path={{splunkforwarder_output_dir}} state=directory recurse=yes owner=splunk group=splunk + file: path={{ splunkforwarder_output_dir }} state=directory recurse=yes owner=splunk group=splunk when: download_deb.changed notify: restart splunkforwarder diff --git a/playbooks/roles/supervisor/files/pre_supervisor_checks.py b/playbooks/roles/supervisor/files/pre_supervisor_checks.py index d9a39a3c6e9..da459ef18ac 100644 --- a/playbooks/roles/supervisor/files/pre_supervisor_checks.py +++ b/playbooks/roles/supervisor/files/pre_supervisor_checks.py @@ -96,6 +96,16 @@ def edp_for_instance(instance_id): instance_id = get_instance_metadata()['instance-id'] prefix = instance_id + + ec2 = boto.connect_ec2() + reservations = ec2.get_all_instances(instance_ids=[instance_id]) + instance = reservations[0].instances[0] + if instance.instance_profile['arn'].endswith('/abbey'): + print("Running an abbey build. Not starting any services.") + # Needs to exit with 1 instead of 0 to prevent + # services from starting. + exit(1) + try: environment, deployment, play = edp_for_instance(instance_id) prefix = "{environment}-{deployment}-{play}-{instance_id}".format( @@ -103,6 +113,10 @@ def edp_for_instance(instance_id): deployment=deployment, play=play, instance_id=instance_id) + except: + print("Failed to get EDP for {}".format(instance_id)) + + try: for service in services_for_instance(instance_id): if service in MIGRATION_COMMANDS: # Do extra migration related stuff. @@ -144,6 +158,7 @@ def edp_for_instance(instance_id): print(msg) if notify: notify(msg) + traceback.print_exc() else: msg = "{}: {}".format(prefix, " | ".join(report)) print(msg) diff --git a/playbooks/roles/supervisor/tasks/main.yml b/playbooks/roles/supervisor/tasks/main.yml index 0ddd91e133b..266e9d2807f 100644 --- a/playbooks/roles/supervisor/tasks/main.yml +++ b/playbooks/roles/supervisor/tasks/main.yml @@ -97,13 +97,13 @@ - name: install supervisor in its venv pip: > - name=supervisor virtualenv="{{supervisor_venv_dir}}" state=present + name=supervisor virtualenv="{{ supervisor_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ supervisor_user }}" - name: install supervisor in its venv pip: > - name={{ item }} virtualenv="{{supervisor_venv_dir}}" state=present + name={{ item }} virtualenv="{{ supervisor_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ supervisor_user }}" with_items: supervisor_pip_pkgs @@ -156,7 +156,7 @@ - name: start supervisor service: > - name={{supervisor_service}} + name={{ supervisor_service }} state=started register: start_supervisor diff --git a/playbooks/roles/supervisor/templates/etc/init/pre_supervisor.conf.j2 b/playbooks/roles/supervisor/templates/etc/init/pre_supervisor.conf.j2 index c713086a6f6..1febb606407 100644 --- a/playbooks/roles/supervisor/templates/etc/init/pre_supervisor.conf.j2 +++ b/playbooks/roles/supervisor/templates/etc/init/pre_supervisor.conf.j2 @@ -5,4 +5,4 @@ task setuid {{ supervisor_user }} -exec {{ supervisor_venv_dir }}/bin/python {{ supervisor_app_dir }}/pre_supervisor_checks.py --available={{supervisor_available_dir}} --enabled={{supervisor_cfg_dir}} {% if SUPERVISOR_HIPCHAT_API_KEY is defined %}--hipchat-api-key {{ SUPERVISOR_HIPCHAT_API_KEY }} --hipchat-room {{ SUPERVISOR_HIPCHAT_ROOM }} {% endif %} {% if edxapp_code_dir is defined %}--edxapp-python {{ COMMON_BIN_DIR }}/python.edxapp --edxapp-code-dir {{ edxapp_code_dir }}{% endif %} {% if xqueue_code_dir is defined %}--xqueue-code-dir {{ xqueue_code_dir }} --xqueue-python {{ COMMON_BIN_DIR }}/python.xqueue {% endif %} +exec {{ supervisor_venv_dir }}/bin/python {{ supervisor_app_dir }}/pre_supervisor_checks.py --available={{ supervisor_available_dir }} --enabled={{ supervisor_cfg_dir }} {% if SUPERVISOR_HIPCHAT_API_KEY is defined %}--hipchat-api-key {{ SUPERVISOR_HIPCHAT_API_KEY }} --hipchat-room {{ SUPERVISOR_HIPCHAT_ROOM }} {% endif %} {% if edxapp_code_dir is defined %}--edxapp-python {{ COMMON_BIN_DIR }}/python.edxapp --edxapp-code-dir {{ edxapp_code_dir }}{% endif %} {% if xqueue_code_dir is defined %}--xqueue-code-dir {{ xqueue_code_dir }} --xqueue-python {{ COMMON_BIN_DIR }}/python.xqueue {% endif %} diff --git a/playbooks/roles/test_build_server/files/test-development-environment.sh b/playbooks/roles/test_build_server/files/test-development-environment.sh index 5af566cb3df..b3eabafb76e 100644 --- a/playbooks/roles/test_build_server/files/test-development-environment.sh +++ b/playbooks/roles/test_build_server/files/test-development-environment.sh @@ -35,11 +35,14 @@ paver test_system -t cms/djangoapps/course_creators/tests/test_views.py paver test_js_run -s xmodule # Run some of the bok-choy tests -paver test_bokchoy -t test_lms.py:RegistrationTest +paver test_bokchoy -t lms/test_lms.py:RegistrationTest +paver test_bokchoy -t discussion/test_discussion.py:DiscussionTabSingleThreadTest --fasttest +paver test_bokchoy -t studio/test_studio_with_ora_component.py:ORAComponentTest --fasttest +paver test_bokchoy -t lms/test_lms_matlab_problem.py:MatlabProblemTest --fasttest # Run some of the lettuce acceptance tests -# paver test_acceptance -s lms --extra_args="lms/djangoapps/courseware/features/problems.feature" -# paver test_acceptance -s cms --extra_args="cms/djangoapps/contentstore/features/html-editor.feature" +paver test_acceptance -s lms --extra_args="lms/djangoapps/courseware/features/problems.feature -s 1" +paver test_acceptance -s cms --extra_args="cms/djangoapps/contentstore/features/html-editor.feature -s 1" # Generate quality reports paver run_quality diff --git a/playbooks/roles/testcourses/tasks/deploy.yml b/playbooks/roles/testcourses/tasks/deploy.yml index 54d1d6bbbcb..70b2afa6412 100644 --- a/playbooks/roles/testcourses/tasks/deploy.yml +++ b/playbooks/roles/testcourses/tasks/deploy.yml @@ -15,7 +15,7 @@ - name: Untar the test courses command: > tar zxf {{ item.path|basename }} - chdir=/var/tmp/{{ item.path|basename }} + chdir=/var/tmp/{{ item.path|basename }} with_items: TESTCOURSES_EXPORTS sudo_user: "{{ common_web_user }}" diff --git a/playbooks/roles/user/tasks/main.yml b/playbooks/roles/user/tasks/main.yml index 24c7f3ea161..5082e36b57d 100644 --- a/playbooks/roles/user/tasks/main.yml +++ b/playbooks/roles/user/tasks/main.yml @@ -111,9 +111,9 @@ # authorized_keys2 used here so that personal # keys can be copied to authorized_keys -# force is set to yes here, otherwise the keys -# won't update if they haven't changed on the github -# side +# 2014/10/14 - using curl instead of get_url because +# get_url was failing due to certificate verification errors + - name: copy github key[s] to .ssh/authorized_keys2 shell: > curl https://github.com/{{ item.name }}.keys -o /home/{{ item.name }}/.ssh/authorized_keys2 diff --git a/playbooks/roles/xqueue/defaults/main.yml b/playbooks/roles/xqueue/defaults/main.yml index 7900ae2be8e..66f88e7e417 100644 --- a/playbooks/roles/xqueue/defaults/main.yml +++ b/playbooks/roles/xqueue/defaults/main.yml @@ -2,6 +2,8 @@ # when the role is included --- XQUEUE_NGINX_PORT: 18040 +XQUEUE_GUNICORN_WORKERS_EXTRA: "" +XQUEUE_GUNICORN_EXTRA: "" XQUEUE_QUEUES: # push queue 'edX-Open_DemoX': 'http://localhost:18050' @@ -9,6 +11,7 @@ XQUEUE_QUEUES: 'test-pull': !!null 'certificates': !!null 'open-ended': !!null + 'open-ended-message': !!null XQUEUE_LOGGING_ENV: sandbox XQUEUE_SYSLOG_SERVER: 'localhost' XQUEUE_S3_BUCKET : 'sandbox-bucket' @@ -53,33 +56,33 @@ xqueue_gunicorn_port: 8040 xqueue_gunicorn_host: 127.0.0.1 xqueue_env_config: - XQUEUES: $XQUEUE_QUEUES + XQUEUES: "{{ XQUEUE_QUEUES }}" XQUEUE_WORKERS_PER_QUEUE: $XQUEUE_WORKERS_PER_QUEUE - LOGGING_ENV : $XQUEUE_LOGGING_ENV - SYSLOG_SERVER: $XQUEUE_SYSLOG_SERVER - LOG_DIR : "{{ COMMON_DATA_DIR }}/logs/xqueue" - RABBIT_HOST : $XQUEUE_RABBITMQ_HOSTNAME - S3_BUCKET : $XQUEUE_S3_BUCKET - S3_PATH_PREFIX: $XQUEUE_S3_PATH_PREFIX - LOCAL_LOGLEVEL: $XQUEUE_LOCAL_LOGLEVEL + LOGGING_ENV: "{{ XQUEUE_LOGGING_ENV }}" + SYSLOG_SERVER: "{{ XQUEUE_SYSLOG_SERVER }}" + LOG_DIR: "{{ COMMON_DATA_DIR }}/logs/xqueue" + RABBIT_HOST: "{{ XQUEUE_RABBITMQ_HOSTNAME }}" + S3_BUCKET: "{{ XQUEUE_S3_BUCKET }}" + S3_PATH_PREFIX: "{{ XQUEUE_S3_PATH_PREFIX }}" + LOCAL_LOGLEVEL: "{{ XQUEUE_LOCAL_LOGLEVEL }}" xqueue_auth_config: - AWS_ACCESS_KEY_ID: $XQUEUE_AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY: $XQUEUE_AWS_SECRET_ACCESS_KEY - REQUESTS_BASIC_AUTH: [$XQUEUE_BASIC_AUTH_USER, $XQUEUE_BASIC_AUTH_PASSWORD] - USERS: $XQUEUE_DJANGO_USERS + AWS_ACCESS_KEY_ID: "{{ XQUEUE_AWS_ACCESS_KEY_ID }}" + AWS_SECRET_ACCESS_KEY: "{{ XQUEUE_AWS_SECRET_ACCESS_KEY }}" + REQUESTS_BASIC_AUTH: ["{{ XQUEUE_BASIC_AUTH_USER }}", "{{XQUEUE_BASIC_AUTH_PASSWORD}}"] + USERS: "{{ XQUEUE_DJANGO_USERS }}" DATABASES: default: ENGINE: "django.db.backends.mysql" - NAME: $XQUEUE_MYSQL_DB_NAME - USER: $XQUEUE_MYSQL_USER - PASSWORD: $XQUEUE_MYSQL_PASSWORD - HOST: $XQUEUE_MYSQL_HOST - PORT: $XQUEUE_MYSQL_PORT - RABBITMQ_USER: $XQUEUE_RABBITMQ_USER - RABBITMQ_PASS: $XQUEUE_RABBITMQ_PASS + NAME: "{{ XQUEUE_MYSQL_DB_NAME }}" + USER: "{{ XQUEUE_MYSQL_USER }}" + PASSWORD: "{{ XQUEUE_MYSQL_PASSWORD }}" + HOST: "{{ XQUEUE_MYSQL_HOST }}" + PORT: "{{ XQUEUE_MYSQL_PORT }}" + RABBITMQ_USER: "{{ XQUEUE_RABBITMQ_USER }}" + RABBITMQ_PASS: "{{ XQUEUE_RABBITMQ_PASS }}" -xqueue_source_repo: https://github.com/edx/xqueue.git +xqueue_source_repo: "https://github.com/edx/xqueue.git" xqueue_version: 'HEAD' xqueue_pre_requirements_file: "{{ xqueue_code_dir }}/pre-requirements.txt" xqueue_post_requirements_file: "{{ xqueue_code_dir }}/requirements.txt" diff --git a/playbooks/roles/xqueue/tasks/deploy.yml b/playbooks/roles/xqueue/tasks/deploy.yml index 9123440a1ce..45e8b27c017 100644 --- a/playbooks/roles/xqueue/tasks/deploy.yml +++ b/playbooks/roles/xqueue/tasks/deploy.yml @@ -41,7 +41,7 @@ - name : install python pre-requirements pip: > requirements="{{ xqueue_pre_requirements_file }}" virtualenv="{{ xqueue_venv_dir }}" state=present - extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" + extra_args="-i {{ COMMON_PYPI_MIRROR_URL }} --exists-action w" sudo_user: "{{ xqueue_user }}" notify: - restart xqueue @@ -50,7 +50,7 @@ - name : install python post-requirements pip: > requirements="{{ xqueue_post_requirements_file }}" virtualenv="{{ xqueue_venv_dir }}" state=present - extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" + extra_args="-i {{ COMMON_PYPI_MIRROR_URL }} --exists-action w" sudo_user: "{{ xqueue_user }}" notify: - restart xqueue diff --git a/playbooks/roles/xqueue/tasks/main.yml b/playbooks/roles/xqueue/tasks/main.yml index 0666c40b7ac..bb9fbd1b3fb 100644 --- a/playbooks/roles/xqueue/tasks/main.yml +++ b/playbooks/roles/xqueue/tasks/main.yml @@ -2,7 +2,7 @@ # - group_vars/all # - common/tasks/main.yml --- -# Check out xqueue repo to {{xqueue_code_dir}} +# Check out xqueue repo to {{ xqueue_code_dir }} # # diff --git a/playbooks/roles/xqueue/templates/xqueue.conf.j2 b/playbooks/roles/xqueue/templates/xqueue.conf.j2 index 050706698cc..b98e8d33fc7 100644 --- a/playbooks/roles/xqueue/templates/xqueue.conf.j2 +++ b/playbooks/roles/xqueue/templates/xqueue.conf.j2 @@ -7,12 +7,12 @@ {% endif %} {% if XQUEUE_WORKERS -%} -command={{ executable }} --preload -b {{ xqueue_gunicorn_host }}:{{ xqueue_gunicorn_port }} -w {{ XQUEUE_WORKERS }} --timeout=300 --pythonpath={{ xqueue_code_dir }} xqueue.wsgi +command={{ executable }} --preload -b {{ xqueue_gunicorn_host }}:{{ xqueue_gunicorn_port }} -w {{ XQUEUE_WORKERS }} --timeout=300 --pythonpath={{ xqueue_code_dir }} {{ XQUEUE_GUNICORN_WORKERS_EXTRA }} xqueue.wsgi {% else -%} {% if ansible_processor|length > 0 %} -command={{ executable }} --preload -b {{ xqueue_gunicorn_host }}:{{ xqueue_gunicorn_port }} -w {{ ansible_processor|length * 2 }} --timeout=300 --pythonpath={{ xqueue_code_dir }} xqueue.wsgi +command={{ executable }} --preload -b {{ xqueue_gunicorn_host }}:{{ xqueue_gunicorn_port }} -w {{ ansible_processor|length * 2 }} --timeout=300 --pythonpath={{ xqueue_code_dir }} {{ XQUEUE_GUNICORN_EXTRA }} xqueue.wsgi {% else -%} -command={{ executable }} --preload -b {{ xqueue_gunicorn_host }}:{{ xqueue_gunicorn_port }} -w 2 --timeout=300 --pythonpath={{ xqueue_code_dir }} xqueue.wsgi +command={{ executable }} --preload -b {{ xqueue_gunicorn_host }}:{{ xqueue_gunicorn_port }} -w 2 --timeout=300 --pythonpath={{ xqueue_code_dir }} {{ XQUEUE_GUNICORN_EXTRA }} xqueue.wsgi {% endif -%} {% endif -%} diff --git a/playbooks/roles/xqueue/templates/xqueue_consumer.conf.j2 b/playbooks/roles/xqueue/templates/xqueue_consumer.conf.j2 index d93f25e5d08..aabd95cbcab 100644 --- a/playbooks/roles/xqueue/templates/xqueue_consumer.conf.j2 +++ b/playbooks/roles/xqueue/templates/xqueue_consumer.conf.j2 @@ -1,6 +1,6 @@ [program:xqueue_consumer] -command={{xqueue_venv_bin}}/django-admin.py run_consumer --pythonpath={{xqueue_code_dir}} --settings=xqueue.aws_settings $WORKERS_PER_QUEUE +command={{ xqueue_venv_bin }}/django-admin.py run_consumer --pythonpath={{ xqueue_code_dir }} --settings=xqueue.aws_settings $WORKERS_PER_QUEUE user={{ common_web_user }} directory={{ xqueue_code_dir }} diff --git a/playbooks/roles/xserver/defaults/main.yml b/playbooks/roles/xserver/defaults/main.yml index fcf41f2bd5d..caad78562a2 100644 --- a/playbooks/roles/xserver/defaults/main.yml +++ b/playbooks/roles/xserver/defaults/main.yml @@ -3,6 +3,7 @@ XSERVER_NGINX_PORT: 18050 +XSERVER_GUNICORN_EXTRA: "" XSERVER_RUN_URL: '' XSERVER_LOGGING_ENV: 'sandbox' XSERVER_SYSLOG_SERVER: '' @@ -31,11 +32,11 @@ xserver_grader_root: "{{ XSERVER_GRADER_DIR }}/graders" xserver_git_identity: "{{ xserver_app_dir }}/xserver-identity" xserver_env_config: - RUN_URL: $XSERVER_RUN_URL - GRADER_ROOT: $xserver_grader_root - LOGGING_ENV: $XSERVER_LOGGING_ENV + RUN_URL: "{{ XSERVER_RUN_URL }}" + GRADER_ROOT: "{{ xserver_grader_root }}" + LOGGING_ENV: "{{ XSERVER_LOGGING_ENV }}" LOG_DIR: "{{ xserver_log_dir }}" - SYSLOG_SERVER: $XSERVER_SYSLOG_SERVER + SYSLOG_SERVER: "{{ XSERVER_SYSLOG_SERVER }}" SANDBOX_PYTHON: '{{ xserver_venv_sandbox_dir }}/bin/python' xserver_source_repo: "git://github.com/edx/xserver.git" diff --git a/playbooks/roles/xserver/tasks/deploy.yml b/playbooks/roles/xserver/tasks/deploy.yml index 9f839949a52..30f4908938c 100644 --- a/playbooks/roles/xserver/tasks/deploy.yml +++ b/playbooks/roles/xserver/tasks/deploy.yml @@ -13,7 +13,7 @@ - name: checkout code git: > - dest={{xserver_code_dir}} repo={{xserver_source_repo}} version={{xserver_version}} + dest={{ xserver_code_dir }} repo={{ xserver_source_repo }} version={{xserver_version}} accept_hostkey=yes sudo_user: "{{ xserver_user }}" register: xserver_checkout @@ -21,14 +21,14 @@ - name: install requirements pip: > - requirements="{{xserver_requirements_file}}" virtualenv="{{ xserver_venv_dir }}" state=present + requirements="{{ xserver_requirements_file }}" virtualenv="{{ xserver_venv_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ xserver_user }}" notify: restart xserver - name: install sandbox requirements pip: > - requirements="{{xserver_requirements_file}}" virtualenv="{{xserver_venv_sandbox_dir}}" state=present + requirements="{{ xserver_requirements_file }}" virtualenv="{{ xserver_venv_sandbox_dir }}" state=present extra_args="-i {{ COMMON_PYPI_MIRROR_URL }}" sudo_user: "{{ xserver_user }}" notify: restart xserver diff --git a/playbooks/roles/xserver/templates/99-sandbox.j2 b/playbooks/roles/xserver/templates/99-sandbox.j2 index 4b069c49ce2..929d15daa27 100644 --- a/playbooks/roles/xserver/templates/99-sandbox.j2 +++ b/playbooks/roles/xserver/templates/99-sandbox.j2 @@ -1 +1 @@ -www-data ALL=({{ xserver_sandbox_user }}) NOPASSWD:{{xserver_venv_sandbox_dir}}/bin/python +www-data ALL=({{ xserver_sandbox_user }}) NOPASSWD:{{ xserver_venv_sandbox_dir }}/bin/python diff --git a/playbooks/roles/xserver/templates/xserver.conf.j2 b/playbooks/roles/xserver/templates/xserver.conf.j2 index 91426988cd7..9fea3018e89 100644 --- a/playbooks/roles/xserver/templates/xserver.conf.j2 +++ b/playbooks/roles/xserver/templates/xserver.conf.j2 @@ -1,6 +1,6 @@ [program:xserver] -command={{ xserver_venv_bin }}/gunicorn --preload -b {{ xserver_gunicorn_host }}:{{ xserver_gunicorn_port }} -w {{ xserver_gunicorn_workers }} --timeout=30 --pythonpath={{ xserver_code_dir }} pyxserver_wsgi:application +command={{ xserver_venv_bin }}/gunicorn --preload -b {{ xserver_gunicorn_host }}:{{ xserver_gunicorn_port }} -w {{ xserver_gunicorn_workers }} --timeout=30 --pythonpath={{ xserver_code_dir }} {{ XSERVER_GUNICORN_EXTRA }} pyxserver_wsgi:application user={{ common_web_user }} directory={{ xserver_code_dir }} diff --git a/playbooks/security.yml b/playbooks/security.yml new file mode 100644 index 00000000000..2e402fb61fd --- /dev/null +++ b/playbooks/security.yml @@ -0,0 +1,5 @@ +- name: Apply security role + hosts: all + sudo: yes + roles: + - security diff --git a/playbooks/vagrant-cluster.yml b/playbooks/vagrant-cluster.yml new file mode 100644 index 00000000000..81ab3df2e78 --- /dev/null +++ b/playbooks/vagrant-cluster.yml @@ -0,0 +1,68 @@ +- name: Configure group cluster + hosts: all + sudo: True + gather_facts: True + vars: + vagrant_cluster: yes + mongo_cluster_members: + - "cluster1" + - "cluster2" + - "cluster3" + MONGO_CLUSTERED: yes + MONGO_CLUSTER_KEY: 'password' + mongo_create_users: no + ELASTICSEARCH_CLUSTERED: yes + MARIADB_CLUSTERED: yes + MARIADB_CREATE_DBS: no + vars_files: + - "group_vars/all" + roles: + - user + - mongo + - oraclejdk + - elasticsearch + - mariadb + - edx_ansible + +# Rabbit needs to be built serially +- name: Configure group cluster serial roles + hosts: all + sudo: True + serial: 1 + gather_facts: True + vars: + rabbitmq_clustered_hosts: + - "rabbit@cluster1" + - "rabbit@cluster2" + - "rabbit@cluster3" + rabbitmq_ip: "" + vars_files: + - "group_vars/all" + roles: + - rabbitmq + +# Mongo user doesn't handle slave's gracefully when +# creating users and there are race conditions +# in MariaDB occasionally so this play will work +# but will also show as failed +- name: Configure group with tasks that will always fail + hosts: all + sudo: True + gather_facts: True + vars: + mongo_cluster_members: + - "cluster1" + - "cluster2" + - "cluster3" + MONGO_CLUSTERED: yes + MONGO_CLUSTER_KEY: 'password' + mongo_create_users: yes + RABBITMQ_CLUSTERED: yes + MARIADB_CLUSTERED: yes + MARIADB_CREATE_DBS: yes + vars_files: + - "group_vars/all" + - "roles/analytics-api/defaults/main.yml" + roles: + - mongo + - mariadb diff --git a/playbooks/vagrant-devstack.yml b/playbooks/vagrant-devstack.yml index b9d636fac87..d683eb31ab9 100644 --- a/playbooks/vagrant-devstack.yml +++ b/playbooks/vagrant-devstack.yml @@ -7,7 +7,6 @@ openid_workaround: true devstack: true disable_edx_services: true - edx_platform_version: 'master' mongo_enable_journal: false EDXAPP_NO_PREREQ_INSTALL: 0 COMMON_MOTD_TEMPLATE: 'devstack_motd.tail.j2' diff --git a/playbooks/vagrant-fullstack.yml b/playbooks/vagrant-fullstack.yml index 86314005a59..1d84b434f09 100644 --- a/playbooks/vagrant-fullstack.yml +++ b/playbooks/vagrant-fullstack.yml @@ -5,11 +5,16 @@ vars: migrate_db: 'yes' openid_workaround: true - edx_platform_version: 'master' EDXAPP_LMS_NGINX_PORT: '80' EDX_ANSIBLE_DUMP_VARS: true CERTS_DOWNLOAD_URL: 'http://192.168.33.10:18090' CERTS_VERIFY_URL: 'http://192.168.33.10:18090' + # used for releases + edx_platform_version: '{{ OPENEDX_RELEASE | default("master") }}' + ora2_version: '{{ OPENEDX_RELEASE | default("master") }}' + certs_version: '{{ OPENEDX_RELEASE | default("master") }}' + forum_version: '{{ OPENEDX_RELEASE | default("master") }}' + xqueue_version: '{{ OPENEDX_RELEASE | default("master") }}' vars_files: - "group_vars/all" roles: @@ -19,7 +24,6 @@ nginx_sites: - cms - lms - - ora - forum - xqueue - certs @@ -36,7 +40,6 @@ - elasticsearch - forum - { role: "xqueue", update_users: True } - - ora - certs - role: analytics-api when: ANALYTICS_API_GIT_IDENTITY diff --git a/requirements.txt b/requirements.txt index e5ee18fbade..64936fa89e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,14 @@ ansible==1.5.5 PyYAML==3.11 -Jinja2==2.7.2 +Jinja2==2.7.3 MarkupSafe==0.23 argparse==1.2.1 boto==2.29.1 ecdsa==0.11 -paramiko==1.14.0 +paramiko==1.15.1 pycrypto==2.6.1 wsgiref==0.1.2 docopt==0.6.1 python-simple-hipchat==0.2 prettytable==0.7.2 +awscli==1.4.2 diff --git a/util/jenkins/ansible-provision.sh b/util/jenkins/ansible-provision.sh index cb8013b4a1a..88386ddd5c2 100644 --- a/util/jenkins/ansible-provision.sh +++ b/util/jenkins/ansible-provision.sh @@ -174,6 +174,7 @@ EDXAPP_NEWRELIC_LMS_APPNAME: sandbox-${dns_name}-edxapp-lms EDXAPP_NEWRELIC_CMS_APPNAME: sandbox-${dns_name}-edxapp-cms XQUEUE_NEWRELIC_APPNAME: sandbox-${dns_name}-xqueue FORUM_NEW_RELIC_APP_NAME: sandbox-${dns_name}-forums +SANDBOX_USERNAME: $github_username EOF fi diff --git a/util/jenkins/certificate-whitelist.sh b/util/jenkins/certificate-whitelist.sh new file mode 100644 index 00000000000..7fc2d972e08 --- /dev/null +++ b/util/jenkins/certificate-whitelist.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +cd configuration +pip install -r requirements.txt +env + +ansible="ansible first_in_tag_Name_${environment}-${deployment}-worker -i playbooks/ec2.py -u ubuntu -s -U www-data -a" +manage="/edx/bin/python.edxapp /edx/bin/manage.edxapp lms --settings aws cert_whitelist" + +echo "$username" > /tmp/username.txt + +if [ "$addremove" = "add" ]; then + for x in $(cat /tmp/username.txt); do + echo "Adding $x" + $ansible "$manage --add $x -c $course_id" + done +elif [ "$addremove" = "remove" ]; then + for x in $(cat /tmp/username.txt); do + echo "Removing $x" + $ansible "$manage --del $x -c $course_id" + done +fi + +rm /tmp/username.txt diff --git a/util/jenkins/change-enrollment-course.sh b/util/jenkins/change-enrollment-course.sh new file mode 100644 index 00000000000..b6b9c5260c4 --- /dev/null +++ b/util/jenkins/change-enrollment-course.sh @@ -0,0 +1,12 @@ +cd configuration +pip install -r requirements.txt +env + +ansible="ansible first_in_tag_Name_${environment}-${deployment}-worker -i playbooks/ec2.py -u ubuntu -s -U www-data -a" +manage="/edx/bin/python.edxapp /edx/bin/manage.edxapp lms change_enrollment --settings aws" + +if [ "$noop" = true ]; then + $ansible "$manage --noop --course $course --to $to --from $from" +else + $ansible "$manage --course $course --to $to --from $from" +fi diff --git a/util/jenkins/change-enrollment.sh b/util/jenkins/change-enrollment.sh new file mode 100644 index 00000000000..ee607c06281 --- /dev/null +++ b/util/jenkins/change-enrollment.sh @@ -0,0 +1,12 @@ +cd configuration +pip install -r requirements.txt +env + +ansible="ansible first_in_tag_Name_${environment}-${deployment}-worker -i playbooks/ec2.py -u ubuntu -s -U www-data -a" +manage="/edx/bin/python.edxapp /edx/bin/manage.edxapp lms change_enrollment --settings aws" + +if [ "$noop" = true ]; then + $ansible "$manage --noop --course $course --user $name --to $to --from $from" +else + $ansible "$manage --course $course --user $name --to $to --from $from" +fi diff --git a/util/jenkins/check-migrations.sh b/util/jenkins/check-migrations.sh index 2b7c66ff15e..d1531061db1 100644 --- a/util/jenkins/check-migrations.sh +++ b/util/jenkins/check-migrations.sh @@ -1,11 +1,7 @@ #!/usr/bin/env bash set -x -if [[ - -z $WORKSPACE || - -z $environment || - -z $deployment - ]]; then +if [[ -z $WORKSPACE ]]; then echo "Environment incorrect for this wrapper script" env exit 1 @@ -13,7 +9,7 @@ fi env -cd $WORKSPACE/edx-platform +cd "$WORKSPACE/edx-platform" # install requirements # These requirements will be installed into the shinginpanda @@ -27,23 +23,43 @@ pip install --exists-action w -r requirements/edx/repo.txt pip install --exists-action w -r requirements/edx/github.txt pip install --exists-action w -r requirements/edx/local.txt -cd $WORKSPACE/configuration/playbooks/edx-east +if [[ $openid_workaround == "true" ]]; then + sed -i -e 's/claimed_id = models.TextField(max_length=2047, unique=True/claimed_id = models.TextField(max_length=2047/' "$VIRTUAL_ENV/lib/python2.7/site-packages/django_openid_auth/models.py" +fi + +cd "$WORKSPACE/configuration/playbooks/edx-east" if [[ -f ${WORKSPACE}/configuration-secure/ansible/vars/${deployment}.yml ]]; then extra_var_args+=" -e@${WORKSPACE}/configuration-secure/ansible/vars/${deployment}.yml" fi -if [[ $db_dry_run=="false" ]]; then - # Set this to an empty string if db_dry_run is +if [[ -z $syncdb ]]; then + syncdb="false" +fi + +if [[ $db_dry_run == "false" ]]; then + # Set this to an empty string if db_dry_run is # not set. By default the db_dry_run var is # set to --db-dry-run + extra_var_args+=" -e db_dry_run=''" +else + # always skip syncdb unless dry run is unchecked + syncdb="false" +fi + +if [[ -f ${WORKSPACE}/configuration-secure/ansible/vars/${environment}-${deployment}.yml ]]; then + extra_var_args+=" -e@${WORKSPACE}/configuration-secure/ansible/vars/${environment}-${deployment}.yml" fi -extra_var_args+=" -e@${WORKSPACE}/configuration-secure/ansible/vars/${environment}-${deployment}.yml" +for extra_var in $extra_vars; do + extra_var_args+=" -e@${WORKSPACE}/configuration-secure/ansible/vars/$extra_var" +done + extra_var_args+=" -e edxapp_app_dir=${WORKSPACE}" extra_var_args+=" -e edxapp_code_dir=${WORKSPACE}/edx-platform" extra_var_args+=" -e edxapp_user=jenkins" +extra_var_args+=" -e syncdb=$syncdb" # Generate the json configuration files ansible-playbook -c local $extra_var_args --tags edxapp_cfg -i localhost, -s -U jenkins edxapp.yml diff --git a/util/jenkins/django-admin.sh b/util/jenkins/django-admin.sh new file mode 100644 index 00000000000..f5528e914eb --- /dev/null +++ b/util/jenkins/django-admin.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +cd configuration +pip install -r requirements.txt +env + +ansible="ansible first_in_tag_Name_${environment}-${deployment}-worker -i playbooks/ec2.py -u ubuntu -s -U www-data -a" +manage="/edx/bin/python.edxapp ./manage.py chdir=/edx/app/edxapp/edx-platform" + +if [ "$service_variant" != "UNSET" ]; then + manage="$manage $service_variant --settings aws" +fi + +if [ "$help" = "true" ]; then + manage="$manage help" +fi + +$ansible "$manage $command $options --settings aws" diff --git a/util/jenkins/get-rc-branches.sh b/util/jenkins/get-rc-branches.sh new file mode 100755 index 00000000000..8a0de7fc8ca --- /dev/null +++ b/util/jenkins/get-rc-branches.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +usage() { + + prog=$(basename "$0") + cat< /dev/null 2>&1 +else + $noop cd "/var/tmp/$repo_basename" + $noop git fetch > /dev/null > /dev/null 2>&1 +fi + +$noop cd "/var/tmp/$repo_basename" +if [[ -z $noop ]]; then + for branch in $(git branch -a | sort -r | tr -d ' ' | grep -E "$filter" ); do + echo "origin/${branch}" + done + for tag in $(git tag -l | sort -r | tr -d ' ' | grep -E "$filter"); do + echo "$tag" + done +else + echo "Would have checked for branches or tags using filter $filter" +fi diff --git a/util/jenkins/issue-certificate.sh b/util/jenkins/issue-certificate.sh index fd9e05f0491..1a835b67dc7 100755 --- a/util/jenkins/issue-certificate.sh +++ b/util/jenkins/issue-certificate.sh @@ -4,13 +4,17 @@ cd configuration pip install -r requirements.txt env -ip=`python playbooks/ec2.py | jq -r '."tag_Name_prod-edx-worker"[0] | strings'` +ansible="ansible first_in_tag_Name_${environment}-${deployment}-worker -i playbooks/ec2.py -u ubuntu -s -U www-data -m shell -a" +manage="/edx/bin/python.edxapp /edx/bin/manage.edxapp lms --settings aws" if [ "$report" = "true" ]; then - ssh ubuntu@$ip "cd /edx/app/edxapp/edx-platform && sudo -u www-data /edx/bin/python.edxapp ./manage.py lms gen_cert_report -c $course_id --settings aws" + $ansible "$manage gen_cert_report -c $course_id" | grep -A2 "Looking up certificate states for" | sed 's/rm:.*//' +elif [ "$regenerate" = "true" ] ; then + $ansible "$manage regenerate_user -c $course_id -u $username" else - ssh ubuntu@$ip "cd /edx/app/edxapp/edx-platform && sudo -u www-data /edx/bin/python.edxapp ./manage.py lms ungenerated_certs -c $course_id --settings aws" - if [ ! -z "$force_certificate_state" ]; then - ssh ubuntu@$ip "cd /edx/app/edxapp/edx-platform && sudo -u www-data /edx/bin/python.edxapp ./manage.py lms ungenerated_certs -c $course_id -f $force_certificate_state --settings aws" - fi + if [ -n "$force_certificate_state" ]; then + $ansible "$manage ungenerated_certs -c $course_id -f $force_certificate_state && $manage gen_cert_report -c $course_id" | grep -A2 "Looking up certificate states for" | sed 's/rm:.*//' + else + $ansible "$manage ungenerated_certs -c $course_id && $manage gen_cert_report -c $course_id" | grep -A2 "Looking up certificate states for" | sed 's/rm:.*//' + fi fi diff --git a/util/jenkins/restart-xqueue.sh b/util/jenkins/restart-xqueue.sh new file mode 100644 index 00000000000..e23c4652d85 --- /dev/null +++ b/util/jenkins/restart-xqueue.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd configuration +pip install -r requirements.txt +env + +command="/edx/bin/supervisorctl restart xqueue" + +ansible tag_Name_${environment}-${deployment}-commoncluster -i playbooks/ec2.py -u ubuntu -s -a "$command" diff --git a/util/jenkins/restart-xqueue_consumer.sh b/util/jenkins/restart-xqueue_consumer.sh new file mode 100644 index 00000000000..c7cb68b3714 --- /dev/null +++ b/util/jenkins/restart-xqueue_consumer.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd configuration +pip install -r requirements.txt +env + +command="/edx/bin/supervisorctl restart xqueue_consumer" + +ansible tag_Name_${environment}-${deployment}-commoncluster -i playbooks/ec2.py -u ubuntu -s -a "$command" diff --git a/util/jenkins/restart-xqwatcher.sh b/util/jenkins/restart-xqwatcher.sh new file mode 100644 index 00000000000..11269af830a --- /dev/null +++ b/util/jenkins/restart-xqwatcher.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd configuration +pip install -r requirements.txt +env + +command="/edx/app/xqwatcher/venvs/supervisor/bin/supervisorctl -c /edx/app/xqwatcher/supervisor/supervisord.conf restart xqwatcher" + +ansible tag_Name_${environment}-${deployment}-xqwatcher -i playbooks/ec2.py -u ubuntu -s -a "$command" diff --git a/util/packer/jenkins_worker.json b/util/packer/jenkins_worker.json index ea2b47bff16..e14c6396b51 100644 --- a/util/packer/jenkins_worker.json +++ b/util/packer/jenkins_worker.json @@ -21,11 +21,11 @@ "inline": ["rm -rf {{user `playbook_remote_dir`}}"] }, { "type": "file", - "source": "../../../configuration/playbooks", + "source": "../../playbooks", "destination": "{{user `playbook_remote_dir`}}" }, { "type": "file", - "source": "../../../configuration/requirements.txt", + "source": "../../requirements.txt", "destination": "{{user `playbook_remote_dir`}}/requirements.txt" }, { "type": "shell", @@ -40,6 +40,7 @@ "type": "shell", "inline": ["cd {{user `playbook_remote_dir`}}", ". packer-venv/bin/activate", + "pip install -q -U ansible==1.7.1", "ansible-playbook run_role.yml -i inventory.ini -c local -e role=test_build_server -vvvv"] }] } diff --git a/util/vpc-tools/abbey.py b/util/vpc-tools/abbey.py index 4a4e05a67a9..38b5ead2c08 100644 --- a/util/vpc-tools/abbey.py +++ b/util/vpc-tools/abbey.py @@ -18,7 +18,7 @@ from pprint import pprint -AMI_TIMEOUT = 1800 # time to wait for AMIs to complete(30 minutes) +AMI_TIMEOUT = 2700 # time to wait for AMIs to complete(45 minutes) EC2_RUN_TIMEOUT = 180 # time to wait for ec2 state transition EC2_STATUS_TIMEOUT = 300 # time to wait for ec2 system status checks NUM_TASKS = 5 # number of tasks for time summary report @@ -187,6 +187,19 @@ def create_instance_args(): 'tag:aws:cloudformation:stack-name': stack_name, 'tag:play': args.play} ) + + if len(subnet) < 1: + # + # try scheme for non-cloudformation builds + # + + subnet = vpc.get_all_subnets( + filters={ + 'tag:cluster': args.play, + 'tag:environment': args.environment, + 'tag:deployment': args.deployment} + ) + if len(subnet) < 1: sys.stderr.write("ERROR: Expected at least one subnet, got {}\n".format( len(subnet))) diff --git a/util/vpc-tools/asg_lifcycle_watcher.py b/util/vpc-tools/asg_lifcycle_watcher.py index ba805930040..820e8aad20c 100644 --- a/util/vpc-tools/asg_lifcycle_watcher.py +++ b/util/vpc-tools/asg_lifcycle_watcher.py @@ -22,6 +22,11 @@ import subprocess from boto.sqs.message import RawMessage import logging +import os +from distutils import spawn + +class MissingHostError(Exception): + pass class LifecycleHandler: @@ -30,18 +35,27 @@ class LifecycleHandler: NUM_MESSAGES = 10 WAIT_TIME_SECONDS = 10 - def __init__(self, profile, queue, hook, bin_directory, dry_run): + def __init__(self, profile, queue, hook, dry_run, bin_directory=None): logging.basicConfig(level=logging.INFO) - self.profile = profile self.queue = queue self.hook = hook - self.bin_directory = bin_directory + self.profile = profile + if bin_directory: + os.environ["PATH"] = bin_directory + os.pathsep + os.environ["PATH"] + self.aws_bin = spawn.find_executable('aws') + self.python_bin = spawn.find_executable('python') + + self.base_cli_command ="{python_bin} {aws_bin} --profile {profile} ".format( + python_bin=self.python_bin, + aws_bin=self.aws_bin, + profile=self.profile) + self.dry_run = dry_run - self.ec2 = boto.connect_ec2(profile_name=self.profile) + self.ec2_con = boto.connect_ec2() + self.sqs_con = boto.connect_sqs() def process_lifecycle_messages(self): - sqs_con = boto.connect_sqs() - queue = sqs_con.get_queue(self.queue) + queue = self.sqs_con.get_queue(self.queue) # Needed to get unencoded message for ease of processing queue.set_message_class(RawMessage) @@ -60,78 +74,84 @@ def process_lifecycle_messages(self): asg = as_message['AutoScalingGroupName'] token = as_message['LifecycleActionToken'] - if self.verify_ok_to_retire(as_message['EC2InstanceId']): + try: + + if self.verify_ok_to_retire(as_message['EC2InstanceId']): - logging.info("Host is marked as OK to retire, retiring {instance}".format( - instance=instance_id)) + logging.info("Host is marked as OK to retire, retiring {instance}".format( + instance=instance_id)) - self.continue_lifecycle(asg,token,self.hook) + self.continue_lifecycle(asg, token, self.hook) + + self.delete_sqs_message(queue, sqs_message, as_message, self.dry_run) - if not self.dry_run: - logging.info("Deleting message with body {message}".format(message=as_message)) - sqs_con.delete_message(queue,sqs_message) else: - logging.info("Would have deleted message with body {message}".format(message=as_message)) + logging.info("Recording lifecycle heartbeat for instance {instance}".format( + instance=instance_id)) - else: - logging.info("Recording lifecycle heartbeat for instance {instance}".format( - instance=instance_id)) + self.record_lifecycle_action_heartbeat(asg, token, self.hook) + except MissingHostError as mhe: + logging.exception(mhe) + # There is nothing we can do to recover from this, so we + # still delete the message + self.delete_sqs_message(queue, sqs_message, as_message, self.dry_run) - self.record_lifecycle_action_heartbeat(asg, token,self.hook) - # These notifications are send when configuring a new lifecycle hook, they can be + # These notifications are sent when configuring a new lifecycle hook, they can be # deleted safely elif as_message['Event'] == LifecycleHandler.TEST_NOTIFICATION: - if not self.dry_run: - logging.info("Deleting message with body {message}".format(message=as_message)) - sqs_con.delete_message(queue,sqs_message) - else: - logging.info("Would have deleted message with body {message}".format(message=as_message)) + self.delete_sqs_message(queue, sqs_message, as_message, self.dry_run) else: raise NotImplemented("Encountered message, {message_id}, of unexpected type.".format( message_id=as_message['MessageId'])) + def delete_sqs_message(self, queue, sqs_message, as_message, dry_run): + if not dry_run: + logging.info("Deleting message with body {message}".format(message=as_message)) + self.sqs_con.delete_message(queue, sqs_message) + else: + logging.info("Would have deleted message with body {message}".format(message=as_message)) def record_lifecycle_action_heartbeat(self, asg, token, hook): - command = "{path}/python " \ - "{path}/aws " \ - "autoscaling record-lifecycle-action-heartbeat " \ + command = self.base_cli_command + "autoscaling record-lifecycle-action-heartbeat " \ "--lifecycle-hook-name {hook} " \ "--auto-scaling-group-name {asg} " \ "--lifecycle-action-token {token}".format( - path=self.bin_directory,hook=hook,asg=asg,token=token) + hook=hook,asg=asg,token=token) self.run_subprocess_command(command, self.dry_run) def continue_lifecycle(self, asg, token, hook): - command = "{path}/python " \ - "{path}/aws autoscaling complete-lifecycle-action --lifecycle-hook-name {hook} " \ + command = self.base_cli_command + "autoscaling complete-lifecycle-action --lifecycle-hook-name {hook} " \ "--auto-scaling-group-name {asg} --lifecycle-action-token {token} --lifecycle-action-result " \ "CONTINUE".format( - path=self.bin_directory, hook=hook, asg=asg, token=token) + hook=hook, asg=asg, token=token) self.run_subprocess_command(command, self.dry_run) def run_subprocess_command(self, command, dry_run): - logging.info("Running command {command}.".format(command=command)) + message = "Running command {command}.".format(command=command) if not dry_run: + logging.info(message) try: output = subprocess.check_output(command.split(' ')) logging.info("Output was {output}".format(output=output)) except Exception as e: logging.exception(e) raise e + else: + logging.info("Dry run: {message}".format(message=message)) def get_ec2_instance_by_id(self, instance_id): """ Simple boto call to get the instance based on the instance-id """ - instances = self.ec2.get_only_instances([instance_id]) + instances = self.ec2_con.get_only_instances([instance_id]) if len(instances) == 1: - return self.ec2.get_only_instances([instance_id])[0] + return self.ec2_con.get_only_instances([instance_id])[0] else: return None @@ -152,28 +172,28 @@ def verify_ok_to_retire(self, instance_id): else: # No instance for id in SQS message this can happen if something else # has terminated the instances outside of this workflow - logging.warn("Instance with id {id} is referenced in an SQS message, but does not exist.") - return True + message = "Instance with id {id} is referenced in an SQS message, but does not exist.".\ + format(id=instance_id) + raise MissingHostError(message) if __name__=="__main__": parser = argparse.ArgumentParser() parser.add_argument('-p', '--profile', help='The boto profile to use ' 'per line.',default=None) - parser.add_argument('-b', '--bin', required=True, + parser.add_argument('-b', '--bin-directory', required=False, default=None, help='The bin directory of the virtual env ' - 'from which tor run the AWS cli') + 'from which to run the AWS cli (optional)') parser.add_argument('-q', '--queue', required=True, help="The SQS queue containing the lifecyle messages") parser.add_argument('--hook', required=True, help="The lifecyle hook to act upon.") - parser.add_argument('-d', "--dry-run", dest="dry_run", action="store_true", help='Print the commands, but do not do anything') parser.set_defaults(dry_run=False) args = parser.parse_args() - lh = LifecycleHandler(args.profile, args.queue, args.hook, args.bin, args.dry_run) + lh = LifecycleHandler(args.profile, args.queue, args.hook, args.dry_run, args.bin_directory) lh.process_lifecycle_messages() diff --git a/util/vpc-tools/db-clone.py b/util/vpc-tools/db-clone.py index bb51092fa94..441456ada5d 100644 --- a/util/vpc-tools/db-clone.py +++ b/util/vpc-tools/db-clone.py @@ -131,6 +131,8 @@ def wait_on_db_status(db_name, region='us-east-1', wait_on='available', aws_id=N print("Waiting 15 seconds before checking to see if db is available") time.sleep(15) wait_on_db_status(restore_dbid) + print("Waiting another 15 seconds") + time.sleep(15) if args.clean_wwc: # Run the mysql clean sql file sanitize_cmd = """mysql -u root -p{root_pass} -h{db_host} wwc < {sanitize_wwc_sql_file} """.format( @@ -157,7 +159,8 @@ def wait_on_db_status(db_name, region='us-east-1', wait_on='available', aws_id=N db_cmd = """cd {play_path} && ansible-playbook -c local -i 127.0.0.1, create_dbs.yml """ \ """{extra_args} -e "edxapp_db_root_user=root xqueue_db_root_user=root" """ \ """ -e "db_root_pass={root_pass}" """ \ - """EDXAPP_MYSQL_HOST={db_host}" """.format( + """ -e "EDXAPP_MYSQL_HOST={db_host}" """ \ + """ -e "XQUEUE_MYSQL_HOST={db_host}" """.format( root_pass=args.password, extra_args=extra_args, db_host=db_host, diff --git a/vagrant/base/cluster/Vagrantfile b/vagrant/base/cluster/Vagrantfile new file mode 100644 index 00000000000..e3b44a6f7a4 --- /dev/null +++ b/vagrant/base/cluster/Vagrantfile @@ -0,0 +1,85 @@ +# -*- mode: ruby -*- + +# vi: set ft=ruby : + +VAGRANTFILE_API_VERSION = "2" + +Vagrant.require_version ">= 1.5.0" + +$script = <