Installation of Docker Swarm using Ansible



Introduction

In this tutorial we will cover how to install Docker Swarm in 2 nodes, 1 master, and 1 worker, using Ansible. For this, we will briefly cover how Ansible itself works and all the necessary steps to make an automated installation of Docker Swarm.


What is Ansible?

Ansible is an open-source automation engine that automates software provisioning, configuration management, and application deployment.


As with most configuration management software, Ansible has two types of servers: controlling machines and nodes. First, there is a single controlling machine which is where orchestration begins. Nodes are managed by a controlling machine over SSH. The controlling machine describes the location of nodes through its inventory.


To orchestrate nodes, Ansible deploys modules to nodes over SSH. Modules are temporarily stored in the nodes and communicate with the controlling machine through a JSON protocol over the standard output. When Ansible is not managing nodes, it does not consume resources because no daemons or programs are executing for Ansible in the background.



What is Docker Swarm?

Docker Swarm is a native Docker application commonly used to manage clusters, which allows orchestrating containers between different nodes easily.



Installation

The first thing needed when installing Ansible is creating the inventory file where we will declare all the hosts where we are going to install the services.

For this tutorial we are going to make the installation in AWS using 2 EC2, each one with CentOS7, with the following configuration:


First, we are going to create the directory to store the project.


# mkdir ansibleSwarm


Now create the ansible and inventory directories.

# mkdir -p ansibleSwarm/ansible/inventory



Inside the inventory directory, we are going to create the “hosts” file where we will write the connection data of our servers.


# Hosts manager ansible_host=107.23.74.224 ansible_ssh_user=centos ansible_ssh_private_key_file=/home/rodrigo/Development/innuy/AnsibleSwarm/ansible/private/DockerSwarm.pem worker1 ansible_host=34.205.4.171 ansible_ssh_user=centos ansible_ssh_private_key_file=/home/rodrigo/Development/innuy/AnsibleSwarm/ansible/private/DockerSwarm.pem [swarm-managers] manager [swarm-workers] worker1 [swarm:children] swarm-managers swarm-workers


Here is what each of these configuration lines means:
  • manager - logical name of the server
  • ansible_host - IP or DNS name of the server
  • ansible_ssh_user - Username which ansible is going to use to connect to the server using SSH.
  • ansible_ssh_private_key_file - Path of the ssh key of the AWS server.


After those, in the next lines we are creating the logical groups [swarm-managers] and [swarm-workers], which contain the servers, and then the group [swarm] which contains the groups [swarm-managers] and [swarm-workers].


Role for the swarm group (all the hosts)


1- Common role:


Create the roles folder to store all the playbooks.



# mkdir -p ansibleSwarm/ansible/roles


Now let’s create the common and tasks directory, where we are going to have the main.yml file which has all the necessary steps for the installation.

These roles are going to install the packets needed to work in all the nodes (managers and workers).

Let’s create the main.yml file with our favorite editor inside the ansibleSwarm/ansible/roles/common/tasks with the following content.


--- - name: Clean yum become: true shell: yum clean all tags: [common] - name: upgrade all packages become: true yum: name=* state=latest tags: [common] - name: Disable selinux selinux: state=disabled become: true tags: [common] - name: install unzip become: true yum: name=unzip tags: [common] - name: Enable the EPEL repository definitions. become: true yum: pkg=epel-release tags: [common] - name: Install python setup tools become: true yum: name=python-setuptools tags: [common] - name: Install Pypi become: true easy_install: name=pip tags: [common] - name: install git become: true Yum: name: git state: present tags: [common]




2- Create the docker role


Let’s create the docker role
# mkdir -p ansibleSwarm/ansible/roles/docker/tasks


This ansible role allows the installation and configuration of the last version of docker for the community edition.

Let’s create the main.yml with our favorite editor.


--- - name: Install docker-py become: true pip: name: docker-py extra_args: --ignore-installed tags: [docker] - name: Update docker repo become: true lineinfile: dest: /etc/yum.repos.d/docker.repo create: yes line: "{{ item }}" with_items: - "[dockerrepo]" - "name=Docker Repository" - "baseurl=https://yum.dockerproject.org/repo/main/centos/7/" - "enabled=1" - "gpgcheck=1" - "gpgkey=https://yum.dockerproject.org/gpg" tags: [docker] - name: Install Docker become: true yum: pkg=docker-engine state=present tags: [docker] - name: enable sysv dockerd service become: true service: name: docker.service enabled: yes tags: [docker] - name: Start service become: true service: name: docker.service state: started tags: [docker]

Specific node manager packages

Let’s create the role docker manager.

# mkdir -p ansibleSwarm/ansible/roles/dockermanager/tasks

This role initializes the docker swarm cluster in the node manager, which in turn will provide us with the token that is necessary for the worker nodes.

Let’s create the main.yml inside this role.

--- - name: Disabling swarm become: yes command: docker swarm leave -f ignore_errors: yes tags: [dockermanager] - name: initialize swarm cluster become: yes command: docker swarm init --advertise-addr={{ swarm_iface | default('eth0') }}:2377 register: bootstrap_first_node tags: [dockermanager]


Specific node worker packages

Let’s create the dockerworker role.
mkdir -p ansibleSwarm/ansible/roles/dockermanager/tasks


This role is going to use the token of the manager to connect and create the Docker Swarm cluster.

--- - name: leaving older swarm command: docker swarm leave -f become: yes ignore_errors: yes tags: [swarm] - name: join nodes to manager command: docker swarm join --token={{ tokennode }} {{ hostmanager }} become: yes register: swarm_join_result failed_when: not "'This node is already part of a swarm' in command_result.stderr" tags: [swarm]

Now we are creating the variables directory for this role.

# mkdir -p ansibleSwarm/ansible/roles/dockerworker/vars

Inside the vars directory, we will be creating the main.yml with the following values to obtain the token of the manager. By doing this we can then use the tokennode and hostmanager variables in the tasks folder.

# vars file for dockermanager tokennode: "{{ hostvars.manager.bootstrap_first_node.stdout.split('\n')[5].split(' ')[5] }}" hostmanager: "{{ hostvars.manager.bootstrap_first_node.stdout.split('\n')[6] }}"

Launching the playbook

Once we have all the roles we only need to create a playbook with all the roles for the execution.

--- - name: Install common packages hosts: swarm roles: - common - docker - name: Configure Manager hosts: swarm-managers roles: - dockermanager - name: Configure Workers hosts: swarm-workers roles: - dockerworker

The final step is to execute all the roles we created before.

# ansible-playbook -i inventory/hosts swarm.yml

After executing the command the output should look similar to this.


root@ip-172-31-52-216:/home/rodrigo/Development/innuy/AnsibleSwarm/ansible# ansible-playbook -i inventory/hosts swarm.yml PLAY [Install common packages] ************************************************* TASK [setup] ******************************************************************* ok: [worker1] ok: [manager] TASK [common : Clean yum] ****************************************************** changed: [worker1] [WARNING]: Consider using yum module rather than running yum changed: [manager] TASK [common : upgrade all packages] ******************************************* ok: [worker1] ok: [manager] TASK [common : Disable selinux] ************************************************ changed: [manager] changed: [worker1] TASK [common : install unzip] ************************************************** ok: [worker1] ok: [manager] TASK [common : Enable the EPEL repository definitions.] ************************ ok: [worker1] ok: [manager] TASK [common : Install python setup tools] ************************************* ok: [worker1] ok: [manager] TASK [common : Install Pypi] *************************************************** ok: [worker1] ok: [manager] TASK [common : install git] **************************************************** ok: [worker1] ok: [manager] TASK [docker : Install docker-py] ********************************************** changed: [worker1] changed: [manager] TASK [docker : Update docker repo] ********************************************* ok: [worker1] => (item=[dockerrepo]) ok: [manager] => (item=[dockerrepo]) ok: [worker1] => (item=name=Docker Repository) ok: [manager] => (item=name=Docker Repository) ok: [worker1] => (item=baseurl=https://yum.dockerproject.org/repo/main/centos/7/) ok: [manager] => (item=baseurl=https://yum.dockerproject.org/repo/main/centos/7/) ok: [manager] => (item=enabled=1) ok: [worker1] => (item=enabled=1) ok: [manager] => (item=gpgcheck=1) ok: [worker1] => (item=gpgcheck=1) ok: [manager] => (item=gpgkey=https://yum.dockerproject.org/gpg) ok: [worker1] => (item=gpgkey=https://yum.dockerproject.org/gpg) TASK [docker : Install Docker] ************************************************* ok: [manager] ok: [worker1] TASK [docker : enable sysv dockerd service] ************************************ ok: [worker1] ok: [manager] TASK [docker : Start service] ************************************************** ok: [manager] ok: [worker1] PLAY [Configure Manager] ******************************************************* TASK [setup] ******************************************************************* ok: [manager] TASK [dockermanager : Disabling swarm] ***************************************** changed: [manager] TASK [dockermanager : initialize swarm cluster] ******************************** changed: [manager] PLAY [Configure Workers] ******************************************************* TASK [setup] ******************************************************************* ok: [worker1] TASK [dockerworker : leaving older swarm] ************************************** changed: [worker1] TASK [dockerworker : join nodes to manager] ************************************ changed: [worker1] PLAY RECAP ********************************************************************* manager : ok=17 changed=5 unreachable=0 failed=0 worker1 : ok=17 changed=5 unreachable=0 failed=0


Hope this tutorial has helped you in combining Ansible and Docker Swarm for your needs!

iOS UI Unit Test


You may already be familiar with the concept of Unit Testing. However, usually Unit Tests are done for the logic of the application, but not so much for the visual aspects and the user interaction with the application itself. Making sure the user can interact correctly with the app is as important as what the logic of the app itself does, if not more. So how do we test this?


There are some ways to achieve this in different languages, platforms, and technologies. However, if you are developing a native iOS application, there is a simple and effective way to do it. UI unit testing.


UI unit testing is a kind of unit tests that can interact with the app as a user would, tapping on different elements and triggering gestures to ensure the flow of the app is as expected.


So let’s get started with UI unit testing, by creating our first tests and learning how to run them.



Incorporate UI unit testing to your project



First, let’s start by adding the unit tests to our project. If you are creating a new project from scratch, this is really easy to do. Actually, you don’t even have to do anything! By default, both unit tests and UI unit tests are added to a project when it is created on Xcode. You will see during the creation process that the check boxes corresponding to this tests are already marked:






If you wish to add UI unit testing for an existing project it is not much harder. Just go to File -> New -> Target. On this screen just look for “iOS UI Testing Bundle” and add it to your project. Doing so will add a new folder to your project where all UI tests will be included. Pretty simple!





Creating UI Unit Tests



UI Tests will usually be located in their own folder, which by default will be called “projectNameUITests”. Inside this folder, there should already be a file for UI tests. The class that is defined here uses XCTestCase, and should override both the “setUp” and “tearDown” functions. The first test file should come with some comments that explain what those are, but basically “setUp” is called before each test, and “tearDown” is called after each test. You use them for setting the state of the app where your tests should run and doing the cleanup afterward.


Now we come to the main part of UI Unit Test: the tests themselves. We will create a new function for each test we want to do. Note that all test functions should start with “test” since that is how they are recognized as test functions instead of just auxiliary functions.


To start with, it is recommended that you create a var to reference the app itself on the test class, since this will be used many times to retrieve screen elements. This can be done as following:


class TestExamplesUITests: XCTestCase {
   var app: XCUIApplication!
   override func setUp() {
       super.setUp()
       continueAfterFailure = false
       app = XCUIApplication()
       app.launch()
   }
}


You may notice “continueAfterFailure” in the code above. It pretty much does what it says on the tin. It is a boolean value which determines if a test should continue after it fails (and find everything that fails inside that test), or if it should stop and continue with the other tests.


Now we need to create the tests themselves. As with Unit Tests in general, we will create a certain flow for the test to follow and assert that what should happen has happened. The main difference is that, instead of testing pure logic and assert the results, we will interact with screen elements and assert that the screen is updated accordingly.


Here is a really simple example of a UI Unit Test, in which we will tap a button that is on the first screen (identified as “nextButton”) which should navigate to a second screen, and later check that a label (identified as “nextScreenLabel”) is on screen now that the navigation has been performed.


func testNavigateToNextScreen() {
       XCTAssertTrue(app.buttons["nextButton"].exists)
       XCTAssertFalse(app.staticTexts["nextScreenLabel"].exists)
       app.buttons["nextButton"].tap()
       XCTAssertTrue(app.staticTexts["nextScreenLabel"].exists)
   }


As you see, first we check that the button that we want to press exists. No purpose in continuing our test if there is no button to press, right? After that, we check that the label that should only be on the second screen does not currently exist (test should start in the first screen of the App). Then we tap the button and check again if the label is now present. If everything worked correctly, this test should succeed when we run it.


There are other events we can trigger, like gestures on the App itself. For example, to create a swipe event from the user in the app, we can call app.swipeLeft(). It is that simple!
And now let’s see how to run the test and check the results.



Running tests and checking screenshots



As you’d expect, this is not rocket science.





You just need to go to the tab shown in the image above on Xcode and you will see all tests you have created, UI or otherwise. Now hover over any of the tests (or the entire testing bundle) and a “run” icon will appear. Click on it, and there you go! Just a word of warning, UI tests take much longer to run than other Unit Tests since it has to compile the app an emulate it on real time as a user would. But that is a minor price to pay.


After all selected tests have run, you will get an icon with a tick or an x indicating which of them failed and which of them succeeded.


However, this is not where it ends. We can actually see exactly how the app looked in many steps along the way of each test. Just right click on the test you want to examine more carefully, and click on “Jump to report”. You will see something like the following:




Now you just need to click on the “eye” icon at the right of one of the steps that have it, and you will see a screenshot of the app exactly at that moment of the process. Normally screenshots are taken after each interaction, so you should be able to see everything you need.


And there you have it! Here are the basics of UI Unit Testing on iOS on Swift. You should be able to take this basic tutorial and adapt it to your needs with some minor digging to get the exact interactions you need. We also have an example on our GitHub linked down below of the test done above and one extra test using the swipe interaction. Feel free to check it!


Reference


Android services

Android Services

What is a service?



Normally, when trying to execute an operation in Android it is, in some way, related and linked to a user interface. However, sometimes it would be better to run some long operations on the background, and just notify the user at the end. Or maybe just keep something running all the time, such as a music player. For these cases we can use a Service.


A service is a component which runs in the background without a user interface. Services will continue running even if the user changes to another application, ensuring the long operations are not interrupted by mistake.


There are many kinds of services at our disposal on android, so let’s see what are the main differences.


Services types



Started service



This is the basic type of service. It is usually used to perform a single operation, without returning a final result to the caller. It is created when “startService” is called for the first time and will be destroyed when “stopService” is executed. While “startService” can be called many times while the service is still running (and it will execute the “onStartCommand” function every time), it will not be re-created and will be destroyed when the “stopService” is ran for the first time. It does not matter how many times “startService” is called, it will end when “stopService” is executed once.


Started services can either be Sticky or not Sticky. The difference between a sticky and a not sticky service is what happens when the system kills the service. If the service is not a sticky service, it will not be recreated unless there are pending intents to be delivered. Meanwhile, a sticky service will be recreated regardless of if there are pending intents or not. In case there are none, it will be recreated with a null intent.


Not sticky services should be used when the it is possible to re-start the operation if unfinished and necessary, so the system can manage it appropriately. If it is vital that the service keeps running until stopped manually, you can use a sticky service.


There is also a started service called “redeliver intent”, which is useful when doing long operations that shouldn’t be interrupted. In this type of service, when the system kills it it will be re-created using the last intent that was delivered, and will use the pending intents subsequently. This allows for operations to be continued with the same intent instead of cancelling them altogether.


Bound service



Another type of service is the bound service. In this, instead of calling “startService”, when an activity wants to use this service it “binds” to it. The service is created when the first bind is made, and it continues running. This service can be binded to multiple times by different activities. When this service is binded to it returns an object that implements the “IBinder” interface to be used.


When the service is no longer required by an activity that is binded to it, the activity should call “unbind” to that service. After all activities which were bound to the service call “unbind” (meaning there are no more activities binded to this service), the service is destroyed.


Note: Bound Started service



It is possible to have a service that is both bound and started. This can be useful for some really specific situations but it is not commonly used and it normally leads to more confusion and problems than it is worth. Consider if this is exactly what you want before using it.


Scheduled service



A scheduled service is basically what the name suggests. It is a service that is scheduled to run at a later time. This can be done by using the JobScheduler class, and it allows you to set when to run this service, and even conditions to see if the service should be executed or not depending on status of the device when the scheduled time is reached.


Life cycle



A service follows a simple life cycle during its use. This lifecycle is used in both started services and bound services, although they have some differences in the middle. However, the general concept is the same.


First, the service is created. This is done by calling “startService” or “bindService” depending on the type of service. At this point is when it calls the “onCreate” function which can be used for initial setup on either type of service.


Immediately after creation, it starts executing the “onStartCommand” or “onBind” function (dependin on the case). Here is where the main processing of the service is executed and where it will continue until the service is stopped. In case of bound services, “onBind” will be called every time a new activity binds itself to the service, and “onUnbind” will be called when any activity unbinds itself.


When the service is stopped (either by calling “stopService” or the last “unbind”), the function “onDestroy” is called. Finall, the service is shut down and the life cycle comes to an end.


Creating a service



Now let’s see how to actually create a service. For this exampl, we will create a started service, but creating a bound service is not really that different.


To create a service you need to create a new class that extends from “Service”. This will force us to override the method “onBind”, which is for bound services. Since we will now create a simple started service, we can return null here and move on.


Now we can implement the methods we wish. As explained earlier, we can implement “onCreate”, “onStartCommand”, or “onDestroy” depending on what we need. There are others that we could implement but those are not important for this basic tutorial. The one that we should implement to actually have a started service as we want is the “onStartCommand” one. Here is also where we can choose which type of started service we want to create. This is determined by what we return at the end. Return either “START_NOT_STICKY”, “START_STICKY”, or “START_REDELIVER_INTENT”, depending on what you wish to do.


Here you can see the code for creating a started not sticky service:

package com.servicesexample.innuy.servicesexample; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.support.annotation.Nullable; import android.widget.Toast; /** * Created by Innuy. */ public class ExampleService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "Here the service is started", Toast.LENGTH_LONG).show(); return START_NOT_STICKY; } }
What we need to do now is add our new service to the Android manifest. Just add the following line changing the name of the service to yours:


<service android:name=".ExampleService"></service>
The last step is to call this service. For this you just need to create an appropriate intent and call “startService” from an activity as it is shown in the code below:

startService(new Intent(getBaseContext(), ExampleService.class));
And there it goes! If you use the above code you will see the Toast being shown whenever you start the service. To stop this service just call “endService” with an intent of the same service.


Sources

Services Documentation - Android Developers https://developer.android.com/guide/components/services.html


Secondary links: