diff --git a/ansible/host_vars/tuxedo/vars b/ansible/host_vars/tuxedo/vars new file mode 100644 index 0000000..7eea8a6 --- /dev/null +++ b/ansible/host_vars/tuxedo/vars @@ -0,0 +1,7 @@ +--- + +pip_package: python2-pip +pip_install_packages: + - name: boto3 + - name: botocore + - name: futures diff --git a/ansible/playbook.yml b/ansible/playbook.yml new file mode 100644 index 0000000..89fea77 --- /dev/null +++ b/ansible/playbook.yml @@ -0,0 +1,9 @@ +--- + +- hosts: tuxedo + become: true + roles: + - aws-nvme-device-files + - epel + - pip + - tuxedo diff --git a/ansible/requirements.yml b/ansible/requirements.yml new file mode 100644 index 0000000..4a757bc --- /dev/null +++ b/ansible/requirements.yml @@ -0,0 +1,16 @@ +--- + +roles: + - src: https://github.com/companieshouse/ansible-role-aws-nvme-device-files + name: aws-nvme-device-files + version: 1.0.0 + - src: https://github.com/geerlingguy/ansible-role-repo-epel + name: epel + version: 3.0.0 + - src: https://github.com/geerlingguy/ansible-role-pip + name: pip + version: 2.0.0 + +collections: + - name: community.general + - name: ansible.posix diff --git a/ansible/roles/tuxedo/defaults/main.yml b/ansible/roles/tuxedo/defaults/main.yml new file mode 100644 index 0000000..48f3e8e --- /dev/null +++ b/ansible/roles/tuxedo/defaults/main.yml @@ -0,0 +1,32 @@ +--- + +tuxedo_service_group: tuxedo +tuxedo_service_group_id: 1010 +tuxedo_service_user_id_minimum: 1010 +tuxedo_service_user_id_increment: 10 + +tuxedo_service_users: + - name: ef + uid: "{{ tuxedo_service_user_id_minimum + (0 * tuxedo_service_user_id_increment) | int }}" + - name: prod + uid: "{{ tuxedo_service_user_id_minimum + (1 * tuxedo_service_user_id_increment) | int }}" + - name: scud + uid: "{{ tuxedo_service_user_id_minimum + (2 * tuxedo_service_user_id_increment) | int }}" + +# If locale is ever changed from 'C', a symlink with the same name should be created +# in $TUXDIR/locale pointing at $TUXDIR/locale/C for message strings to resolve correctly +system_locale: C + +tuxedo_version: "8.1" +tuxedo_install_directory: "/opt/tuxedo/{{ tuxedo_version }}" + +informix_sdk_version: "410UC12" +informix_sdk_install_directory: "/opt/informix-client-sdk/{{ informix_sdk_version }}" + +informix_service_user: informix +informix_service_group: informix +informix_version: "14.10" +informix_install_directory: "/opt/informix/{{ informix_version }}" + +deployment_dir: deployment +envfile_name: envfile diff --git a/ansible/roles/tuxedo/tasks/main.yml b/ansible/roles/tuxedo/tasks/main.yml new file mode 100644 index 0000000..ca6a3c2 --- /dev/null +++ b/ansible/roles/tuxedo/tasks/main.yml @@ -0,0 +1,248 @@ +--- + +- name: Set timezone to Europe/London + community.general.timezone: + name: Europe/London + +- name: Format swap volume + command: "mkswap {{ swap_volume_device_node }}" + when: swap_volume_enabled | bool + +- name: Add swap volume to filesystem table + mount: + path: swap + src: "{{ swap_volume_device_node }}" + fstype: swap + opts: defaults + state: present + when: swap_volume_enabled | bool + +- name: Install the 'Development tools' package group + yum: + name: "@Development tools" + state: present + +- name: Install i686 build-time software dependencies + yum: + name: + - cyrus-sasl-devel.i686 + - expat-devel.i686 + - glibc-devel.i686 + - glibc-static.i686 + - libcurl-devel.i686 + - ncurses-devel.i686 + - net-snmp-devel.i686 + - openssl-devel.i686 + - readline-devel.i686 + state: latest + +- name: Install i686 run-time software dependencies + yum: + name: + - glibc.i686 + - libgcc.i686 + - libstdc++.i686 + - openssl-libs.i686 + - zlib.i686 + state: latest + +- name: Install additional i686 library dependencies + aws_s3: + bucket: "{{ resource_bucket_name }}" + object: "{{ resource_bucket_c_libraries_prefix }}/libstdc++-libc6.2-2.so.3" + dest: /usr/lib/libstdc++-libc6.2-2.so.3 + mode: get + +- name: Set permissions for i686 library dependencies + file: + path: /usr/lib/libstdc++-libc6.2-2.so.3 + owner: root + group: root + mode: '0755' + +- name: Create service group + group: + name: "{{ tuxedo_service_group }}" + gid: "{{ tuxedo_service_group_id }}" + state: present + system: no + +- name: Create service users + user: + name: "{{ item.name }}" + uid: "{{ item.uid }}" + groups: "{{ tuxedo_service_group }}" + shell: /bin/bash + system: no + loop: "{{ tuxedo_service_users }}" + +- name: Create Informix group + group: + name: "{{ informix_service_group }}" + state: present + system: yes + +- name: Create Informix user + user: + name: "{{ informix_service_user }}" + groups: "{{ informix_service_group }}" + shell: /bin/bash + system: yes + +- name: Create .bash_profile for service users + template: + src: bash_profile.j2 + dest: "/home/{{ item.name }}/.bash_profile" + owner: "{{ item.name }}" + group: "{{ item.name }}" + mode: '0644' + loop: "{{ tuxedo_service_users }}" + +- name: Create Tuxedo installation directory + file: + path: "{{ tuxedo_install_directory }}" + owner: root + group: root + mode: '0755' + state: directory + +- name: Create temporary directory + tempfile: + state: directory + register: temp_dir + +- name: Download Tuxedo archive + aws_s3: + bucket: "{{ resource_bucket_name }}" + object: "{{ resource_bucket_tuxedo_prefix }}/tuxedo-{{ tuxedo_version }}.tar.gz" + dest: "{{ temp_dir.path }}/tuxedo-{{ tuxedo_version }}.tar.gz" + mode: get + +- name: Extract Tuxedo archive + unarchive: + src: "{{ temp_dir.path }}/tuxedo-{{ tuxedo_version }}.tar.gz" + dest: "{{ tuxedo_install_directory }}" + remote_src: yes + +- name: Download Tuxedo license + aws_s3: + bucket: "{{ resource_bucket_name }}" + object: "{{ resource_bucket_tuxedo_license_prefix }}/tuxedo-{{ tuxedo_version }}" + dest: "{{ tuxedo_install_directory }}/udataobj/lic.txt" + mode: get + +- name: Set ownership of Tuxedo installation files + file: + path: "{{ tuxedo_install_directory }}" + state: directory + recurse: yes + owner: root + group: root + +- name: Create Informix Client SDK installation directory + file: + path: "{{ informix_sdk_install_directory }}" + owner: root + group: root + mode: '0755' + state: directory + +- name: Download Informix Client SDK archive + aws_s3: + bucket: "{{ resource_bucket_name }}" + object: "{{ resource_bucket_informix_sdk_prefix }}/informix-sdk-{{ informix_sdk_version }}.tar.gz" + dest: "{{ temp_dir.path }}/informix-sdk-{{ informix_sdk_version }}.tar.gz" + mode: get + +- name: Extract Download Informix Client SDK archive + unarchive: + src: "{{ temp_dir.path }}/informix-sdk-{{ informix_sdk_version }}.tar.gz" + dest: "{{ informix_sdk_install_directory }}" + remote_src: yes + +- name: Set ownership of Informix Client SDK installation files + file: + path: "{{ informix_sdk_install_directory }}" + state: directory + recurse: yes + owner: root + group: root + +- name: Remove temporary directory + file: + path: temp_dir.path + state: absent + +# The bundled InstallAnywhere installer for IBM Informix 14.10 requires more +# space than is provided by the tmpfs mount from the base distribution image. +# It also attempts to load shared object files from a tmpfs filesystem (/tmp) +# during installation which, by default, is not permitted as the filesystem is +# mounted with the 'noexec' option. +# +# The installation procedure therefore requires additional steps to be performed +# which are documented here for reference: +# +# - Create a non-tmpfs temporary directory to avoid having to resize the +# tmpfs filesystem from the base image or shrink the filesystem before +# creating the resulting machine image +# - Export an environment variable IATEMPDIR with the path to the previously +# created temporary directory before running ids_install to instruct the +# installer to use the specified temporary directory +# - Despite using IATEMPDIR, the bundled installer will not relocate all +# shared object files to the path specified (e.g. libnativeAPI.so) and the +# dynamic loader will be unable to execute such files given that the default +# 'noexec' option is enabled for the tmpfs filesystem; to workaround this +# the active filesystem is remounted with the 'exec' option, then remounted +# again after installation to reinstate the 'noexec' option + +- name: Create temporary directory for Informix installation + tempfile: + path: /root + state: directory + register: informix_temp_dir + +- name: Download Informix installer + aws_s3: + bucket: "{{ resource_bucket_name }}" + object: "{{ resource_bucket_informix_prefix }}/informix-{{ informix_version }}.tar.gz" + dest: "{{ informix_temp_dir.path }}/informix-{{ informix_version }}.tar.gz" + mode: get + +- name: Extract Informix installer + unarchive: + src: "{{ informix_temp_dir.path }}/informix-{{ informix_version }}.tar.gz" + dest: "{{ informix_temp_dir.path }}" + remote_src: yes + +- name: Create Informix installation properties file + template: + src: informix_install.properties.j2 + dest: "{{ informix_temp_dir.path }}/informix_install.properties" + +- name: Remount tmpfs filesystem at /tmp with 'exec' option + ansible.posix.mount: + path: /tmp + opts: exec + state: remounted + +- name: Install Informix + command: "sh {{ informix_temp_dir.path }}/ids_install -i silent -f {{ informix_temp_dir.path }}/informix_install.properties" + +- name: Remount tmpfs filesystem at /tmp with 'noexec' option + ansible.posix.mount: + path: /tmp + opts: noexec + state: remounted + +- name: Set ownership and permissions for Informix installation directory + file: + path: "{{ informix_install_directory }}" + owner: "{{ informix_service_user }}" + group: "{{ informix_service_group }}" + mode: '0755' + state: directory + +- name: Remove temporary Informix installation directory + file: + path: informix_temp_dir.path + state: absent diff --git a/ansible/roles/tuxedo/templates/bash_profile.j2 b/ansible/roles/tuxedo/templates/bash_profile.j2 new file mode 100644 index 0000000..19f1f79 --- /dev/null +++ b/ansible/roles/tuxedo/templates/bash_profile.j2 @@ -0,0 +1,11 @@ +# .bash_profile + +# Get the aliases and functions +if [ -f ~/.bashrc ]; then + . ~/.bashrc +fi + +# Source Tuxedo environment variables for user logins +if [ -f ~/{{ deployment_dir }}/config/{{ envfile_name }} ]; then + . ~/{{ deployment_dir }}/config/{{ envfile_name }} +fi diff --git a/ansible/roles/tuxedo/templates/informix_install.properties.j2 b/ansible/roles/tuxedo/templates/informix_install.properties.j2 new file mode 100644 index 0000000..1391827 --- /dev/null +++ b/ansible/roles/tuxedo/templates/informix_install.properties.j2 @@ -0,0 +1,8 @@ +# IBM Informix installation response file; this file can be generated by running +# ids_install -r and can be used to perform a non-interactive +# installation with the command: ids_install -i silent -f + +LICENSE_ACCEPTED=TRUE +USER_INSTALL_DIR={{ informix_install_directory }} +UNIX_INSTALLTYPE_SELECT=DEFAULT +IDS_INSTALL_TYPE=TYPICAL diff --git a/packer/build.pkr.hcl b/packer/build.pkr.hcl new file mode 100644 index 0000000..6a64681 --- /dev/null +++ b/packer/build.pkr.hcl @@ -0,0 +1,21 @@ +build { + sources = [ + "source.amazon-ebs.builder", + ] + + provisioner "ansible" { + host_alias = "${var.ansible_host_alias}" + playbook_file = "${var.playbook_file_path}" + extra_arguments = [ + "-e", "aws_region=${var.aws_region}", + "-e", "resource_bucket_c_libraries_prefix=${var.resource_bucket_c_libraries_prefix}", + "-e", "resource_bucket_name=${var.resource_bucket_name}", + "-e", "resource_bucket_tuxedo_license_prefix=${var.resource_bucket_tuxedo_license_prefix}", + "-e", "resource_bucket_tuxedo_prefix=${var.resource_bucket_tuxedo_prefix}", + "-e", "resource_bucket_informix_prefix=${var.resource_bucket_informix_prefix}", + "-e", "resource_bucket_informix_sdk_prefix=${var.resource_bucket_informix_sdk_prefix}", + "-e", "swap_volume_device_node=${var.swap_volume_device_node}", + "-e", "swap_volume_enabled=${var.swap_volume_size_gb > 0 ? true : false}" + ] + } +} diff --git a/packer/sources.pkr.hcl b/packer/sources.pkr.hcl new file mode 100644 index 0000000..82e6cfa --- /dev/null +++ b/packer/sources.pkr.hcl @@ -0,0 +1,58 @@ +source "amazon-ebs" "builder" { + ami_name = "${var.ami_name_prefix}-${var.version}" + ami_users = var.ami_account_ids + communicator = "ssh" + instance_type = var.aws_instance_type + region = var.aws_region + ssh_private_key_file = var.ssh_private_key_file + ssh_username = var.ssh_username + ssh_keypair_name = "packer-builders-${var.aws_region}" + iam_instance_profile = "packer-builders-${var.aws_region}" + + launch_block_device_mappings { + device_name = "/dev/sda1" + volume_size = var.root_volume_size_gb + volume_type = "gp2" + delete_on_termination = true + } + + dynamic "launch_block_device_mappings" { + for_each = var.swap_volume_size_gb > 0 ? [1] : [] + + content { + device_name = var.swap_volume_device_node + volume_size = var.swap_volume_size_gb + volume_type = "gp2" + delete_on_termination = true + } + } + + security_group_filter { + filters = { + "group-name": "packer-builders-${var.aws_region}" + } + } + + source_ami_filter { + filters = { + virtualization-type = "hvm" + name = "${var.aws_source_ami_filter_name}" + root-device-type = "ebs" + } + owners = ["${var.aws_source_ami_owner_id}"] + most_recent = true + } + + subnet_filter { + filters = { + "tag:Name": "${var.aws_subnet_filter_name}" + } + most_free = true + random = false + } + + tags = { + Name = "${var.ami_name_prefix}-${var.version}" + Builder = "packer-{{packer_version}}" + } +} diff --git a/packer/variables.pkr.hcl b/packer/variables.pkr.hcl new file mode 100644 index 0000000..6582eb0 --- /dev/null +++ b/packer/variables.pkr.hcl @@ -0,0 +1,121 @@ +variable "ami_account_ids" { + type = list(string) + description = "A list of account IDs that have access to launch the resulting AMI(s)" +} + +variable "ami_name_prefix" { + type = string + default = "fil-tuxedo-ami" + description = "The prefix string that will be used for the name tags of the resulting AMI and snapshot(s); the version string will be appended automatically" +} + +variable "ansible_host_alias" { + type = string + default = "tuxedo" + description = "The Ansible host alias" +} + +variable "aws_instance_type" { + type = string + default = "t3.medium" + description = "The EC2 instance type used when building the AMI" +} + +variable "aws_region" { + type = string + default = "eu-west-2" + description = "The AWS region in which the AMI will be built" +} + +variable "aws_source_ami_filter_name" { + type = string + default = "centos7-base-*" + description = "The source AMI filter string. Any filter described by the DescribeImages API documentation is valid. If multiple images match then the latest will be used" +} + +variable "aws_source_ami_owner_id" { + type = string + default = "169942020521" + description = "The source AMI owner ID; used in combination with aws_source_ami_filter_name to filter for matching source AMIs" +} + +variable "aws_subnet_filter_name" { + type = string + description = "The subnet filter string. Any filter described by the DescribeSubnets API documentation is valid. If multiple subnets match then the one with the most IPv4 addresses free will be used" +} + +variable "playbook_file_path" { + type = string + default = "../ansible/playbook.yml" + description = "The relative path to the Ansible playbook file" +} + +variable "resource_bucket_name" { + type = string + description = "The name of the S3 resources bucket" +} + +variable "resource_bucket_informix_prefix" { + type = string + default = "packages/informix" + description = "The object prefix for Informix packages within the S3 resources bucket" +} + +variable "resource_bucket_informix_sdk_prefix" { + type = string + default = "packages/informix" + description = "The object prefix for Informix Client SDK packages within the S3 resources bucket" +} + +variable "resource_bucket_c_libraries_prefix" { + type = string + default = "libraries/c/i686" + description = "The object prefix for C shared object libraries within the S3 resources bucket" +} + +variable "resource_bucket_tuxedo_license_prefix" { + type = string + default = "licenses/tuxedo" + description = "The object prefix for Tuxedo license files within the S3 resources bucket" +} + +variable "resource_bucket_tuxedo_prefix" { + type = string + default = "packages/tuxedo" + description = "The object prefix for Tuxedo packages within the S3 resources bucket" +} + +variable "root_volume_size_gb" { + type = number + default = 20 + description = "The EC2 instance root volume size in Gibibytes (GiB)" +} + +variable "ssh_private_key_file" { + type = string + default = "/home/packer/.ssh/packer-builder" + description = "The path to the common Packer builder private SSH key" +} + +variable "ssh_username" { + type = string + default = "centos" + description = "The username Packer will use when connecting with SSH" +} + +variable "swap_volume_device_node" { + type = string + default = "/dev/xvdb" + description = "The device node identifier for the swap volume" +} + +variable "swap_volume_size_gb" { + type = number + default = 0 + description = "The EC2 instance swap volume size in Gibibytes (GiB); set to 0 to disable swap volume" +} + +variable "version" { + type = string + description = "The semantic version number for the AMI; the version string will be appended automatically to the name tags added to the resulting AMI and snapshot(s)" +}