Update 02 dec 2015, some people ask me why I used docker-py instead of REST API, so I added REST API equivalent with curl.
Everyday, I build tools using REST API, SOAP, RPC, HTTP requests on web app … But how can I manage a CLI ?
I started a project called cli2rest. The goal of this application is to wrap any CLI into a simple REST API like apache-libcloud do with different cloud provider.
First idea
- deploy and manage 2 nodes on GNU/Linux for failover
- deploy and manage 2 nodes on Windows
- build an application to wrap local CLI
- make a powerfull API to manage it
Wow.
Just to make a simple CLI run ?
Wait. Docker have a REST API and can wrap a CLI !
Let’s go
For example, I will use httpie and run http HEAD ahmet2mir.eu
remotely.
Create Docker image to wrap httpie CLI
Create a Dockerfile
:
FROM alpine:3.2
MAINTAINER Ahmet Demir <ahmet2mir+github@gmail.com>
RUN apk add --update python openssl
RUN wget //bootstrap.pypa.io/get-pip.py
RUN python get-pip.py
RUN pip install --upgrade pip setuptools httpie
RUN rm -rf /var/cache/apk/*
Build image:
$ docker build -t ahmet2mir/httpie .
We start a container using a timeout of 120s and execute command inside container:
$ docker run -d --name httpiedemo ahmet2mir/httpie /bin/sleep 120
12403c7c03c0b42b1589324d9e53d12123b46195534bae736ca703a41744a6c2
$ docker exec -t httpiedemo http HEAD ahmet2mir.eu
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 1309
Content-Type: text/html
Date: Sun, 22 Nov 2015 15:28:13 GMT
Last-Modified: Tue, 22 Sep 2015 06:25:21 GMT
Server: Apache/2.4.10 (Debian)
Vary: Accept-Encoding
$ docker rm -f httpiedemo
Create REST API
Prerequiste:
- docker-py, Python package supported by Docker Inc or
curl
$ pip install docker-py
or
$ apt install curl
- enable TCP socket and test if it’s works.
$ docker -H tcp://127.0.0.1:2375 images
Create a simple script to do the same thing we did with Docker CLI.
# -*- coding: utf-8 -*-
from docker import Client
def cli2rest(cli, image, name, cmd, timeout=120):
print("===> Create container: %s" % name)
container = cli.create_container(name=name,
image=image,
command=["/bin/sleep", str(timeout)])
print("===> Start container: %s" % name)
cli.start(container=container['Id'])
print("===> Create exec instance")
exec_instance = cli.exec_create(container['Id'], cmd=cmd, tty=True)
print("===> Start exec instance")
out = cli.exec_start(exec_id=exec_instance['Id'])
print("===> Remove container")
cli.remove_container(container=name, force=True)
return out
if __name__ == '__main__':
cli = Client(base_url='tcp://0.0.0.0:2375')
result = cli2rest(cli=cli,
image="ahmet2mir/httpie",
name="httpiedemo",
cmd=["http","HEAD","ahmet2mir.eu"])
print("======> Result")
print(result)
And run!
$ python cli2rest.py
===> Create container: httpiedemo
===> Start container: httpiedemo
===> Create exec instance
===> Start exec instance
===> Remove container
======> Result
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 1309
Content-Type: text/html
Date: Sun, 22 Nov 2015 15:30:44 GMT
Last-Modified: Tue, 22 Sep 2015 06:25:21 GMT
Server: Apache/2.4.10 (Debian)
Vary: Accept-Encoding
===> Remove container
With curl
$ curl -X create_container
Make it real on Windows
For this example, download pstools and run psinfo
FROM debian:jessie
MAINTAINER Ahmet Demir <ahmet2mir+github@gmail.com>
RUN dpkg --add-architecture i386 \
&& apt-get update \
&& apt-get install -y wine:i386 unzip
ADD http://download.sysinternals.com/files/PSTools.zip /tmp/PSTools.zip
RUN unzip /tmp/PSTools.zip -d /win/
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Build
$ docker build -t ahmet2mir/httpie .
Just replace some values to match the new image in the previous python script
# -*- coding: utf-8 -*-
if __name__ == '__main__':
result = cli2rest(cli=cli,
image="ahmet2mir/psinfo",
name="psinfodemo",
cmd=["wine", "/win/psinfo"])
print("======> Result")
print(result)
An run!
$ python cli2rest.py
===> Create container: psinfodemo
===> Start container: psinfodemo
===> Create exec instance
===> Start exec instance
===> Remove container
======> Result
PsInfo v1.77 - Local and remote system information viewer
Copyright (C) 2001-2009 Mark Russinovich
Sysinternals - www.sysinternals.com
Querying information for 2df06379fb0f...
System information for \\2df06379fb0f:
Uptime: 0 days 13 hours 33 minutes 19 seconds
Kernel version: Microsoft Windows XP, Uniprocessor Free
Product type: Professional
Product version: 5.1
Service pack: 3
Kernel build number: 2600
Registered organization:
Registered owner:
IE version: 6.0000
System root: C:\windows
Processors: 4
Processor speed: 3.2 GHz
Processor type: Intel(R) Pentium(R) 4 CPU
Physical memory: 0 MB
Video driver:
Conclusion
Also, I only used this for a Linux CLI in production based on Redhat image. I’ve another image for a Windows CLI not deployed in production.
Idempotency wasn’t implemented in this article because it’s depends on your needs. What you will do if the container exists ? Is it currently running something ? Previsouly failed and still up ? Not deleted ?
Now, you can integrate it in your favorite orchestrator and escape the hell :)
Picture by Blake Thornberry under licence CC BY-NC-ND 2.0