Managing container time with docker-compose and faketime

Why do we need to manage time?

First, a little about myself, my main occupation is quality assurance on assigned projects, I am a Senior QA at Umbrella IT. Periodically, when testing microservices, I have to deal with the need to change the time to check the operation of a particular functionality. This may be functionality that is triggered by a cron “tick”, or related to the use of system time as one of the conditions for processing/storing/transferring data by the microservice being tested.

If a microservice is deployed in Docker, the container time is taken from the host machine's system time, and without additional tools, the maximum we can change is the container's time zone, but not the system time itself. The problem is that often, for testing purposes, simply changing the time zone, as well as changing the time within 24 hours, is not enough. What if we need to test the operation of a microservice in boundary date-time values, such as the beginning and end of a month/year, or use special dates such as February 29, the last dates of months with a change in the number of days, and so on?

Of course, you can start a virtual machine, change the system time on it, reboot Docker and deploy the microservice we need. It is quite possible that in cases where a microservice needs to support multiple network connections on different ports (mail, DB, gRPC, etc.), we will have to forward these ports, possibly (depending on the choice of implementation) set up tunnels, etc. Even if such an implementation works, we need to take into account the possible risks of reducing the value of testing due to the build-up of connection “crutches” and the shortcomings of the implementation using a separate virtual machine. All this distances the testing conditions from real conditions of use.

What should we do if we need at the same time:

  • maximum approximation to standard test conditions

  • Flexible time management during deployment

  • no interference with microservice code

  • minimal impact on resource consumption

  • maintaining standard network interaction (as much as possible, an experienced networker will immediately notice those nuances that will be described below in this article)

Before describing the instructions, I would like to express my gratitude Nesterovich Alexander for help in implementing and debugging the approach described below.

Instructions for use

So, we are faced with the task of deploying a container with a microservice in a test environment (for example, an FT cluster) and setting it a time different from the host machine time. Let's immediately list the main required accesses for this implementation in the FT environment (this can be a dedicated machine for testing, which is in the FT cluster):

  1. creating, editing and running files

  2. running apt-get, docker and docker-compose

  3. assigning rights to read and execute created files

  4. other accesses are optional and depend on specific cases, for example, access to obtain an image from a repository, or, for example, access to the entrypoint command line.

  1. Create docker-compose.yml

To configure the deployment of our microservice, we will create a file docker-compose.yml

version: '3.6'
services:
  my-test-service-workerhost:        
    image: myimg.repo.com/project/my-test-service-workerhost:1.0.0-Ft
    restart: always
    environment:
      ASPNETCORE_ENVIRONMENT: Staging
      FAKED_TIME: '@2007-01-01 00:00:01' #Поле по теме статьи
    cap_add:
      - NET_ADMIN
    volumes:
      - "./appsettings.json:/app/appsettings.Staging.json"
      - "./entrypoint.sh:/app/entrypoint.sh" #Поле по теме статьи
    entrypoint: ["/app/entrypoint.sh"] #Поле по теме статьи
    ports:
      - 5335:80

All fields, except those marked with the comment “Field on the topic of the article”, do not relate to the issue of system time management and are added only to make our docker-compose.yml the reality is a little closer to reality. Let's analyze the purposes of the marked fields:

  • FAKED_TIME – this is an environment variable through which we will pass the value to the customized entrypoint when deploying the microservice. We can call it any convenient and free name, if we wish, the main thing is not to forget to rename it in the script specified below.@” before the date means that the specified time Not will be fixed forever for the microservice, and will be the starting point for the system time. Time will run for the microservice starting from the specified starting point.

  • entrypoint – actually here we indicate that the standard entrypoint must be replaced with a custom one.

  • volumes"./entrypoint.sh:/app/entrypoint.sh" – here we specify where the file with the custom entrypoint script will be stored

  1. Writing a custom entrypoint

In the folder with the file docker-compose.yml (or another path that you previously specified in volumes for entrypoint) create a file entrypoint.sh and immediately give him the rights to read and execute the command chmod.

In the file itself, we write the following code in bash:

#!/bin/sh

# Проверяем FAKED_TIME на наличие данных
if [ -z "$FAKED_TIME" ]; then
    echo "Error: FAKED_TIME is empty or null with = $FAKED_TIME"
    exit 1    
else
    echo "FAKED_TIME is $FAKED_TIME"
fi

# Устанавливаем faketime 
apt-get update && apt-get install -y faketime

# Запускаем приложение с использованием faketime
exec faketime -f "$FAKED_TIME" dotnet /app/MyProject.TestApplication.WorkerHost.dll

To change the system time when starting a microservice, we use faketime. This is an application that replaces the system time value for the command executed with its help. For example, when executing the command faketime -f '2007-01-01 00:00:01' date in the console we will see the output of the date-time we substituted for the command date.

Example of faketime working with date command

Example of faketime working with date command

  1. Let's launch and check

All that's left for us to do is run the deploy command docker-compose up and check the container logs

And here we have 2007 again, as we can see the time in the container is not fixed and changes with its flow, which is reflected in the logs

Logs of a running container with system time substitution

Logs of a running container with system time substitution

Possible problems and solutions

Networking

No matter how native the approach to changing time is, we may encounter the fact that when interacting with a network using certificates (for example, simple https), we will not have a connection. The reason for this is the verification of the certificate, namely the dates associated with it. In case of such problems, you can test:

  • switch to interaction via an unencrypted protocol

  • raise the host/DB etc. you need next to it according to the same instructions with the time already changed, which is consistent with the microservice being tested

  • raise a mock service nearby according to the same instructions with the time already changed, which is consistent with the microservice being tested

The choice of solution depends on each specific case/project, what will be faster, more convenient, have less impact on testing efficiency or will simply be feasible.

No access to execute commands when deploying a container

It happens that for security reasons the default user is denied the ability to execute commands when building a container. In such cases, it is necessary to entrypoint.sh Before executing commands, switch to a user with sufficient rights, or add commands for installing and using faketime inside the Docker file

No faketime among the applications available for installation

If suddenly in your case a simple installation is via apt-get install -y faketime impossible, you can use the library libfaketime to clone the repository for its further installation

git clone https://github.com/wolfcw/libfaketime.git
cd libfaketime
make install

Instructions for using libfaketime can be found in readme repositories

Conclusion

Container system time management provides ample opportunities for testing the functionality of services that use this data in their logic. This allows you to increase test coverage, reduce the time spent on testing itself, flexibly manage the environment and, if necessary, reproduce specific cases.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *