├── .gitignore ├── .idea └── vcs.xml ├── .travis.yml ├── .yamllint ├── README.md ├── defaults └── main.yml ├── handlers └── main.yml ├── meta └── main.yml ├── molecule └── default │ ├── Dockerfile.j2 │ ├── INSTALL.rst │ ├── __init__.py │ ├── create.yml │ ├── destroy.yml │ ├── molecule.yml │ ├── playbook.yml │ ├── prepare.yml │ ├── requirements.yml │ └── tests │ ├── application.yml │ ├── spring-boot-sample.conf │ ├── spring-boot-sample.jar │ └── test_install.py ├── requirements.txt ├── tasks ├── java.yml ├── main.yml └── service.yml ├── templates └── app.service.j2 └── vars ├── Debian.yml ├── RedHat.yml └── main.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | molecule/default/.cache 3 | molecule/**/.molecule 4 | molecule/**/__pycache__ 5 | molecule/**/__init__.py 6 | molecule/**/__init__.pyc 7 | molecule/**/.pytest_cache 8 | .venv -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: required 3 | language: python 4 | services: 5 | - docker 6 | before_install: 7 | - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 8 | - sudo add-apt-repository 9 | "deb [arch=amd64] 10 | https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" 11 | - sudo apt-get update 12 | - sudo apt-get -y install docker-ce 13 | install: 14 | - pip install -r requirements.txt 15 | script: 16 | - molecule test 17 | notifications: 18 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ 19 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | extends: default 2 | 3 | rules: 4 | braces: 5 | max-spaces-inside: 1 6 | level: error 7 | brackets: 8 | max-spaces-inside: 1 9 | level: error 10 | line-length: disable 11 | truthy: disable 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Spring boot 2 | [![Build Status](https://travis-ci.org/remyma/ansible-springboot.svg?branch=master)](https://travis-ci.org/remyma/ansible-springboot) 3 | 4 | Deploy spring boot applications as linux services. 5 | 6 | ## Requirements 7 | 8 | Your spring boot application should be previously packaged as a fully executable jar as explained here : 9 | 10 | https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html#deployment-script-customization-conf-file 11 | 12 | ## Role Variables 13 | 14 | | Variable | Default | Description | 15 | | ------------ | ------------- | -------------- | 16 | | springboot_java_install | true | If you want this role to install java. Use false if java is already installed | 17 | | springboot_src_file | | Mandatory or use ```springboot_src_url```. Path of the springboot jar to deploy. | 18 | | springboot_src_url | | Mandatory or use ```springboot_src_file```. Url of the springboot jar to deploy. | 19 | | springboot_application_name | | Mandatory. Spring application name. Use to name jar to be deployed, systemd service, ... | 20 | | springboot_propertyfile_template | | Optional. Path towards a template to manage your app properties (eg : application.properties, application.yml). | 21 | | springboot_configuration_template | | Optional. Path towards a template to manage your app config (see : https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html#deployment-script-customization-when-it-runs). | 22 | | springboot_deploy_folder | /opt/{{ springboot_application_name }} | Folder where application jar is deployed | 23 | | springboot_user | springboot | Linux user to run spring boot application | 24 | | springboot_group | springboot | Linux group to run spring boot application | 25 | 26 | 27 | ## Example Playbook 28 | 29 | Minimal playbook : 30 | 31 | - hosts: all 32 | vars: 33 | springboot_application_name: spring-boot-sample 34 | springboot_src: tests/spring-boot-sample.jar 35 | roles: 36 | - role: ansible-springboot 37 | 38 | If you want to deploy also configuration and/or properties for you application : 39 | 40 | - hosts: all 41 | vars: 42 | springboot_application_name: spring-boot-sample 43 | springboot_src: spring-boot-sample.jar 44 | springboot_propertyfile_template: /path/to/your/template/application.yml 45 | springboot_configuration_template: /path/to/your/template/spring-boot-sample.conf 46 | roles: 47 | - role: ansible-springboot 48 | 49 | ## License 50 | 51 | BSD 52 | 53 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Install java by default. Optional 3 | springboot_java_install: true 4 | 5 | # Mandatory variable : path to springboot jar to deploy. 6 | # Either specify a file or a url depending on if you want to copy you app or download it. 7 | springboot_src_file: None 8 | springboot_src_url: None 9 | 10 | # Mandatory variable : spring boot application name 11 | springboot_application_name: None 12 | 13 | # Optional : you can specify a template file to manage you app properties 14 | springboot_propertyfile_template: None 15 | 16 | # Optional : you can specify a template file to manage you app configuration 17 | # (see : https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html#deployment-script-customization-when-it-runs) 18 | springboot_configuration_template: None 19 | 20 | # Deploy folder 21 | springboot_deploy_folder: /opt/{{ springboot_application_name }} 22 | 23 | # linux user to start the service 24 | springboot_user: springboot 25 | 26 | # linux group to start the service 27 | springboot_group: springboot 28 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Restart application" 3 | systemd: 4 | name: "{{ springboot_application_name }}" 5 | enabled: yes 6 | daemon-reload: yes 7 | state: restarted 8 | when: use_system_d 9 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Matthieu Rémy 4 | description: Deploy springboot applications 5 | license: "license (BSD, MIT)" 6 | min_ansible_version: 2.2.0 7 | 8 | platforms: 9 | - name: EL 10 | versions: 11 | - 6 12 | - 7 13 | - name: Debian 14 | versions: 15 | - all 16 | - name: Ubuntu 17 | versions: 18 | - all 19 | 20 | galaxy_tags: 21 | - spring 22 | - springboot 23 | 24 | dependencies: [] 25 | -------------------------------------------------------------------------------- /molecule/default/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # Molecule managed 2 | 3 | FROM {{ item.image }} 4 | 5 | RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get upgrade -y && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 6 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python python-devel python2-dnf bash && dnf clean all; \ 7 | elif [ $(command -v yum) ]; then yum makecache fast && yum update -y && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ 8 | elif [ $(command -v zypper) ]; then zypper refresh && zypper update -y && zypper install -y python sudo bash python-xml && zypper clean -a; \ 9 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; fi 10 | -------------------------------------------------------------------------------- /molecule/default/INSTALL.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | Install 3 | ******* 4 | 5 | Requirements 6 | ============ 7 | 8 | * Docker Engine 9 | * docker-py 10 | 11 | Install 12 | ======= 13 | 14 | .. code-block:: bash 15 | 16 | $ sudo pip install docker-py 17 | -------------------------------------------------------------------------------- /molecule/default/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remyma/ansible-springboot/c3f6528565309cf4a338db4434ab3f9f8d4c0213/molecule/default/__init__.py -------------------------------------------------------------------------------- /molecule/default/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: False 6 | no_log: "{{ not lookup('env', 'MOLECULE_DEBUG') | bool }}" 7 | vars: 8 | molecule_file: "{{ lookup('env', 'MOLECULE_FILE') }}" 9 | molecule_ephemeral_directory: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}" 10 | molecule_scenario_directory: "{{ lookup('env', 'MOLECULE_SCENARIO_DIRECTORY') }}" 11 | molecule_yml: "{{ lookup('file', molecule_file) | molecule_from_yaml }}" 12 | tasks: 13 | - name: Create Dockerfiles from image names 14 | template: 15 | src: "{{ molecule_scenario_directory }}/Dockerfile.j2" 16 | dest: "{{ molecule_ephemeral_directory }}/Dockerfile_{{ item.image | regex_replace('[^a-zA-Z0-9_]', '_') }}" 17 | with_items: "{{ molecule_yml.platforms }}" 18 | register: platforms 19 | 20 | - name: Discover local Docker images 21 | docker_image_facts: 22 | name: "molecule_local/{{ item.item.name }}" 23 | with_items: "{{ platforms.results }}" 24 | register: docker_images 25 | 26 | - name: Build an Ansible compatible image 27 | docker_image: 28 | path: "{{ molecule_ephemeral_directory }}" 29 | name: "molecule_local/{{ item.item.image }}" 30 | dockerfile: "{{ item.item.dockerfile | default(item.invocation.module_args.dest) }}" 31 | force: "{{ item.item.force | default(True) }}" 32 | with_items: "{{ platforms.results }}" 33 | when: platforms.changed or docker_images.results | map(attribute='images') | select('equalto', []) | list | count >= 0 34 | 35 | - name: Create molecule instance(s) 36 | docker_container: 37 | name: "{{ item.name }}" 38 | hostname: "{{ item.name }}" 39 | image: "molecule_local/{{ item.image }}" 40 | state: started 41 | recreate: False 42 | log_driver: syslog 43 | command: "{{ item.command | default('sleep infinity') }}" 44 | privileged: "{{ item.privileged | default(omit) }}" 45 | volumes: "{{ item.volumes | default(omit) }}" 46 | capabilities: "{{ item.capabilities | default(omit) }}" 47 | with_items: "{{ molecule_yml.platforms }}" 48 | -------------------------------------------------------------------------------- /molecule/default/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: False 6 | no_log: "{{ not lookup('env', 'MOLECULE_DEBUG') | bool }}" 7 | vars: 8 | molecule_file: "{{ lookup('env', 'MOLECULE_FILE') }}" 9 | molecule_yml: "{{ lookup('file', molecule_file) | molecule_from_yaml }}" 10 | tasks: 11 | - name: Destroy molecule instance(s) 12 | docker_container: 13 | name: "{{ item.name }}" 14 | state: absent 15 | force_kill: "{{ item.force_kill | default(True) }}" 16 | with_items: "{{ molecule_yml.platforms }}" 17 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: docker 6 | lint: 7 | name: yamllint 8 | platforms: 9 | - name: instance 10 | image: centos:7 11 | privileged: True 12 | command: '/sbin/init' 13 | cap_add: ['SYS_ADMIN', 'SETPCAP'] 14 | provisioner: 15 | name: ansible 16 | lint: 17 | name: ansible-lint 18 | scenario: 19 | name: default 20 | verifier: 21 | name: testinfra 22 | lint: 23 | name: flake8 24 | -------------------------------------------------------------------------------- /molecule/default/playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | vars: 5 | - springboot_src_file: 'tests/spring-boot-sample.jar' 6 | - springboot_configuration_template: 'tests/spring-boot-sample.conf' 7 | - springboot_propertyfile_template: 'tests/application.yml' 8 | - springboot_application_name: 'spring-boot-sample' 9 | - log_folder: "/opt/spring-boot-sample" 10 | roles: 11 | - role: geerlingguy.java 12 | when: "ansible_os_family == 'RedHat'" 13 | java_packages: 14 | - java-1.8.0-openjdk 15 | - role: ansible-springboot 16 | -------------------------------------------------------------------------------- /molecule/default/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | gather_facts: False 5 | tasks: [] 6 | -------------------------------------------------------------------------------- /molecule/default/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: geerlingguy.java 3 | -------------------------------------------------------------------------------- /molecule/default/tests/application.yml: -------------------------------------------------------------------------------- 1 | --- 2 | spring: 3 | application: 4 | name: {{ springboot_application_name }} 5 | -------------------------------------------------------------------------------- /molecule/default/tests/spring-boot-sample.conf: -------------------------------------------------------------------------------- 1 | LOG_FOLDER={{ log_folder }} -------------------------------------------------------------------------------- /molecule/default/tests/spring-boot-sample.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remyma/ansible-springboot/c3f6528565309cf4a338db4434ab3f9f8d4c0213/molecule/default/tests/spring-boot-sample.jar -------------------------------------------------------------------------------- /molecule/default/tests/test_install.py: -------------------------------------------------------------------------------- 1 | import testinfra.utils.ansible_runner 2 | 3 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 4 | '.molecule/ansible_inventory').get_hosts('all') 5 | 6 | 7 | def test_application_running(Service): 8 | application = Service("spring-boot-sample") 9 | assert application.is_enabled 10 | 11 | 12 | def test_application_properties_exists(File): 13 | propertyFile = File("/opt/spring-boot-sample/application.yml") 14 | assert propertyFile.exists 15 | 16 | 17 | def test_application_conf_exists(File): 18 | configFile = File("/opt/spring-boot-sample/spring-boot-sample.conf") 19 | assert configFile.exists 20 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible==2.7.10 2 | molecule==2.20.1 3 | docker-py==1.10.6 4 | -------------------------------------------------------------------------------- /tasks/java.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: RedHat - Ensure Java is installed 3 | yum: 4 | name: "{{ java }}" 5 | state: "present" 6 | update_cache: yes 7 | when: ansible_os_family == 'RedHat' 8 | 9 | # - name: Debian - Refresh java repo 10 | # apt: update_cache=yes 11 | # changed_when: false 12 | # when: ansible_os_family == 'Debian' 13 | 14 | - name: Debian - Ensure Java is installed 15 | apt: name={{ java }} state="present" 16 | when: ansible_os_family == 'Debian' 17 | 18 | - name: Check java version 19 | command: java -version 2>&1 | grep OpenJDK 20 | register: open_jdk 21 | changed_when: false 22 | 23 | # https://github.com/docker-library/openjdk/issues/19 - ensures tests pass due to java 8 broken certs 24 | - name: refresh the java ca-certificates 25 | command: /var/lib/dpkg/info/ca-certificates-java.postinst configure 26 | when: ansible_distribution == 'Ubuntu' and open_jdk.rc == 0 27 | changed_when: false 28 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Check for required inventory springboot_application_name variable" 3 | fail: 4 | msg: springboot_application_name variable is required and should be defined in you inventory 5 | when: not springboot_application_name 6 | 7 | - name: "Check for required inventory springboot_src_file or springboot_src_url variable" 8 | fail: 9 | msg: springboot_src variable or springboot_src_url is required and should be defined in you inventory 10 | when: (not springboot_src_file) and (not springboot_src_url) 11 | 12 | - name: os-specific vars 13 | include_vars: "{{ ansible_os_family }}.yml" 14 | 15 | - name: "Install java" 16 | include: java.yml 17 | when: springboot_java_install 18 | 19 | - name: "Create group for springboot app" 20 | group: 21 | name: "{{ springboot_group }}" 22 | state: present 23 | 24 | - name: "Create user for springboot app" 25 | user: 26 | name: "{{ springboot_user }}" 27 | group: "{{ springboot_group }}" 28 | state: present 29 | 30 | - name: "Create deploy folder if not exists" 31 | file: 32 | path: "{{ springboot_deploy_folder }}" 33 | state: directory 34 | owner: "{{ springboot_user }}" 35 | group: "{{ springboot_group }}" 36 | 37 | - name: "Copy jar application" 38 | copy: 39 | src: "{{ springboot_src_file }}" 40 | dest: "{{ springboot_deploy_folder }}/{{ springboot_application_name }}.jar" 41 | owner: "{{ springboot_user }}" 42 | group: "{{ springboot_group }}" 43 | mode: "u+x" 44 | when: springboot_src_file is defined and springboot_src_file|length > 0 45 | notify: 46 | - "Restart application" 47 | 48 | - name: "Download springboot application" 49 | get_url: 50 | url: "{{ springboot_src_url }}" 51 | dest: "{{ springboot_deploy_folder }}/{{ springboot_application_name }}.jar" 52 | owner: "{{ springboot_user }}" 53 | group: "{{ springboot_group }}" 54 | mode: "u+x" 55 | when: springboot_src_url is defined and springboot_src_url|length > 0 56 | notify: 57 | - "Restart application" 58 | 59 | - name: "Deploy app properties" 60 | template: 61 | src: "{{ springboot_propertyfile_template }}" 62 | dest: "{{ springboot_deploy_folder }}" 63 | owner: "{{ springboot_user }}" 64 | group: "{{ springboot_group }}" 65 | when: springboot_propertyfile_template 66 | notify: 67 | - "Restart application" 68 | 69 | - name: "Deploy app config" 70 | template: 71 | src: "{{ springboot_configuration_template }}" 72 | dest: "{{ springboot_deploy_folder }}" 73 | owner: "{{ springboot_user }}" 74 | group: "{{ springboot_group }}" 75 | when: springboot_configuration_template 76 | notify: 77 | - "Restart application" 78 | 79 | - name: Install as service 80 | include: service.yml 81 | -------------------------------------------------------------------------------- /tasks/service.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Check if use systemd" 3 | set_fact: | 4 | use_system_d={{ (ansible_distribution == 'Debian' and ansible_distribution_version | version_compare('8', '>=')) 5 | or (ansible_distribution in ['RedHat','CentOS'] and ansible_distribution_version | version_compare('7', '>=')) 6 | or (ansible_distribution == 'Ubuntu' and ansible_distribution_version | version_compare('15', '>=')) }} 7 | 8 | - name: "Ensure systemd system directory is present (for Ubuntu)" 9 | file: 10 | path: "{{ sysd_user_services_folder }}" 11 | state: directory 12 | owner: root 13 | group: root 14 | 15 | - name: "Create systemd service file" 16 | template: 17 | src: app.service.j2 18 | dest: "{{ sysd_script }}" 19 | mode: 0644 20 | owner: "{{ springboot_user }}" 21 | group: "{{ springboot_group }}" 22 | when: use_system_d 23 | notify: 24 | - "Restart application" 25 | -------------------------------------------------------------------------------- /templates/app.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description={{ springboot_application_name }} 3 | After=syslog.target 4 | 5 | [Service] 6 | User={{ springboot_user }} 7 | ExecStart="{{ springboot_deploy_folder }}/{{ springboot_application_name }}.jar" 8 | SuccessExitStatus=143 9 | 10 | [Install] 11 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /vars/Debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | java: "{{ springboot_java | default('openjdk-8-jre-headless') }}" 3 | -------------------------------------------------------------------------------- /vars/RedHat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | java: "{{ springboot_java | default('java-1.8.0-openjdk.x86_64') }}" 3 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sysd_user_services_folder: /usr/lib/systemd/system 3 | sysd_script: "{{ sysd_user_services_folder }}/{{ springboot_application_name }}.service" 4 | --------------------------------------------------------------------------------