Fake the system clock for a single application with libfaketime
Tags: tutorial, docker, java, testing
Published: 2020-03-17 20:41:52

# A bit of a back story
My use case was quite specific - I wanted to conduct some end-to-end tests of a Java app running in a Docker container based on CentOS. The piece of code that I wanted to test was relying on date comparison:
```java
if (happenedYesterday(event)) {
    foo();
} else {
    bar();
}
```
Given that I was not able to modify the test data, the easiest thing was to somehow make the application think it's *yesterday*, make it create the`event`, then restore the original date and make the application invoke the above piece of code.

# The setup
Since I would like to show how I got from an idea to a working solution, I need to provide a way to reproduce all the mistakes that I've made as part of this exercise - I need an environment which is as close to the original as possible. To achieve it and also to keep the examples as simple as possible, I'll use [fabric8/java-centos-openjdk8-jdk](https://hub.docker.com/r/fabric8/java-centos-openjdk8-jdk) Docker image.

Let's start a container from an unmodified image and open up its shell:
```
[me@pc ~]$ docker run --name centos -d -it fabric8/java-centos-openjdk8-jdk /bin/bash
...
[me@pc ~]$ docker exec -u 0 -it centos /bin/bash
[root@centos /]#
```
> NOTE: **_-u 0_** argument makes the command log into the container as root (0 is root's user id)

In all the examples in this post, I'll use `pc` as in `[me@pc ~]$` to indicate commands invoked on my local machine and `centos` as in `[root@centos /]#` for commands invoked inside the container. 

# Setting the system date in Docker - naive approach
In my naive approach, I thought this step would be as simple as running one of these commands:
```
[root@centos /]# date -s "15 Oct 2019 19:05"
date: cannot set date: Operation not permitted
Tue Oct 15 19:05:00 UTC 2019
[root@centos /]# hwclock --set --date "15 Oct 2019 19:05"
hwclock: Cannot access the Hardware Clock via any known method.
hwclock: Use the --debug option to see the details of our search for an access method.
```
Unfortunately, it wasn't.

I tried to find some workarounds, but as far as I understand, Docker reuses the clock of the host machine, so overriding the date in the container is either not doable or not easily doable[^1][^2][^3]. As I'm just a casual user of Docker, I didn't want to dig deeper. However, when looking for the workarounds, I stumbled upon a different way to change the time - **libfaketime**.

# libfaketime
libfaketime is a library which is able to 'override' system calls that applications use to retrieve current date or time. It is then able to provide a fake value for these calls. What's more, **you don't have to change a line of your existing code** or add it to your app's dependency list - it'll be transparent. Since I'm not a Linux guru, using this library at first felt like it was a wrapper for my java application, though it's not how it works.

The installation is straightforward - you grab the source code of the library and run `make install` in the root directory of the checked out sources. It'll result in a bunch of files getting created in `/usr/local/lib/faketime`. To automate this process, I created the following Dockerfile:
```docker
FROM fabric8/java-centos-openjdk8-jdk

USER root

RUN yum -y groupinstall 'Development Tools' && \
    yum -y install make unzip wget && \
    mkdir faketime && \
    cd faketime && \
    wget https://github.com/wolfcw/libfaketime/archive/master.zip && \
    unzip master.zip && \
    cd libfaketime-master && \
    make install

ENTRYPOINT /bin/bash
```
Now, it should be possible to start it all up and test the library:
```
[me@pc ~]$ docker build -t centos .
...
[me@pc ~]$ docker run --name centos -d -it centos /bin/bash
...
[me@pc ~]$ docker exec -u 0 -it centos /bin/bash
[root@centos /]# date
Wed Feb 19 17:16:34 UTC 2020
[root@centos /]# LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 FAKETIME="-15d" date
Tue Feb  4 17:16:49 UTC 2020
```
With the path to libfaketime provided in the `LD_PRELOAD` variable, the `FAKETIME` variable set to *-15 days*, the invocation of `date` rendered a date 15 days in the past. To me, the most interesting bit is the `LD_PRELOAD` part.

# LD_PRELOAD variable and the preloading mechanism
The `LD_PRELOAD` variable allows to specify paths to libraries which are to be loaded **before** any other libraries are loaded. What's important, all the symbols (e.g. functions) contained in the preloaded libraries **take precedence** over the symbols from libraries loaded afterwards[^4][^5][^6].

It means that if a program uses a function `foo()` from library **A** and library **A** is linked at runtime, it is possible to provide a path to library **B** containing a different implementation of `foo()` in the `LD_PRELOAD` variable. As a result, when the program references `foo()` in its source code, the implementation from library **B** will be invoked.

libfaketime replaces the symbols related to interactions with the system clock using the preloading mechanism.

Given all the knowledge gathered so far, it's time to fake the date in a Java app.

# Setting a fake date for a Java app
First of all - to test the library, I created a simple application that prints the current date and time each second, forever. Here's the code:
```java
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main {
  private static final int NO_DELAY = 0;

  public static void main(String[] args) {
    Runnable printCurrentDateTime = () -> System.out.println(LocalDateTime.now());

    Executors
      .newSingleThreadScheduledExecutor()
      .scheduleAtFixedRate(printCurrentDateTime, NO_DELAY, 1, TimeUnit.SECONDS);
  }
}
```

Then, to test the library with the application, I ran the CentOS container again with a volume set to the directory where the java class was.
```
[me@pc ~]$ docker run --name centos -v /home/test/:/app -d -it centos /bin/bash
...
[me@pc ~]$ docker exec -u 0 -it centos /bin/bash
[root@centos /]# cd /app
[root@centos app] javac Main.java
[root@centos app] java Main
2020-02-20T21:53:36.132
2020-02-20T21:53:37.113
2020-02-20T21:53:38.113
^C
[root@centos app] LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 FAKETIME="-15d" FAKETIME_DONT_FAKE_MONOTONIC=1 java Main
2020-02-05T21:55:51.787
2020-02-05T21:55:52.766
2020-02-05T21:55:53.765
^C
```
It's working as expected, though I added another variable to the command: `FAKETIME_DONT_FAKE_MONOTONIC=1`. From the [README of libfaketime](https://github.com/wolfcw/libfaketime):
> Java-/JVM-based applications work but you need to pass in an extra argument
  (FAKETIME\_DONT\_FAKE\_MONOTONIC).  See usage basics below for details.  Without
  this argument the java command usually hangs.

Indeed, without it even this tiny program hanged after printing the first date. I must admit I wasted some time debugging it just because I skipped reading the readme.

# Cleaning it up
To shorten the commands, `LD_PRELOAD` and `FAKETIME_DONT_FAKE_MONOTONIC` values can be specified as the environment values of the Docker image. I omitted `FAKETIME` because this one is likely to change. 

```docker
...

USER root

ENV LD_PRELOAD /usr/local/lib/faketime/libfaketime.so.1
ENV FAKETIME_DONT_FAKE_MONOTONIC 1

RUN ...
```
After these changes, it should be possible to run `FAKETIME="-15d" java Main` to render the same output as before. 

# Changing the time dynamically at runtime
It is possible to specify the `FAKETIME` value in a file instead of a variable. It makes it possible to change the value at any moment. libfaketime will pick it up after ten seconds[^8].

It is possible to do this system-wide or just for a user. I'll describe the latter.

The file needs to be named `.faketimerc` and it needs to be placed in the home directory. It should contain only the value of the `FAKETIME` variable, just like this:
```
-15d
```

Running `java Main` in the shell should now render the expected output. While the program running keeps running, in another shell session we can type `echo -10d > ~/.faketimerc`. The output should change after ten seconds.

# Different variants of the FAKETIME value
Specifying a relative offset is not the only way to fake the time. Here are some more variants:
* different offset mutlipliers: all the examples used "-16d", but instead of "d" it can also be "m", "h", "y" or nothing for seconds; the offset can be set in the past (`-10d`) or in the future (`+10d`) 
> EXAMPLES:
>  * `-120` is 120 seconds behind
>  * `+2h` is 2 hours in the future
>  * `+1y` is 1 year in the future
* 'start at' date: `FAKETIME="@2020-12-24 20:30:00"`, where the clock will start ticking from this date **for each new process**, but it's possible to configure it to keep the clock ticking instead[^8]
* absolute date: `FAKETIME="2020-12-24 20:30:00"` will render a fixed value, as if the time stopped at this point

# Summary
These are not all the features that libfaketime supports. I suggest skimming through the list of features in the readme just to get familiar with the possiblities - just in case you ever need to use any of them.

# Key takeways
* I'll use libfaketime in cases similar to this one
* `LD_PRELOAD` mechanism can be used on Linux machines to replace pieces of code without modifying the original code
* I should have read the friendly manual earlier to save time

# Further reading
* [libfaketime manual - GitHub](https://github.com/wolfcw/libfaketime)
* [The LD_PRELOAD trick](http://www.goldsborough.me/c/low-level/kernel/2016/08/29/16-48-53-the_-ld_preload-_trick/)
* [All about LD_PRELOAD](https://blog.fpmurphy.com/2012/09/all-about-ld_preload.html)
* [Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs](https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/)

# Sources
[^1]: https://forums.docker.com/t/is-it-possible-to-change-time-dynamically-in-docker-container/56787/5 
[^2]: https://forums.docker.com/t/change-containers-year/58880/3
[^3]: https://stackoverflow.com/a/29561602
[^4]: http://www.goldsborough.me/c/low-level/kernel/2016/08/29/16-48-53-the_-ld_preload-_trick/
[^5]: https://blog.fpmurphy.com/2012/09/all-about-ld_preload.html
[^6]: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/
[^7]: http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
[^8]: https://github.com/wolfcw/libfaketime