Nejc Saje   

blog | about

Dependency-less REST CPU burner in Go

Disclaimer: not really REST, but writing HTTP-controlled made the title too long.

TL;DR

When compiled with CGO_ENABLED=0, this code produces a static binary file that can be uploaded to a server and run. CPU burn can be started by requesting /start_cpu and stopped by requesting /stop_cpu.

Long version

Recently I had to implement an integration test in which I had to fire up a virtual machine and control its CPU load. The first idea was to write a simple Python web service, so I could control the CPU load via HTTP requests. The testing was being done on devstack though, and its test VM image is that of CirrOS, which only includes Busybox and no Python.

So I needed a static binary I could just scp on the VM and run. Go turned out to be the answer to my problem. Even though I never programmed in Go before, I soon had a working HTTP-controlled staticly linked web service. To achieve static linking, the compiler and the code needs to be compiled with environment variable CGO_ENABLED=0 (issue).

Cross compilation

On top of that, Go allowed me to easily cross-compile a Linux binary on OSX just by (quickly) recompiling Go compiler with Linux support

cd /usr/local/go/src
sudo GOOS=linux GOARCH=386 CGO_ENABLED=0 ./make.bash --no-clean

and then compiling my code with additional options GOOS=linux GOARCH=386. Other platforms are similar, for more information take a look at An introduction to cross compilation with Go.

package main

import (
    "fmt"
    "net/http"
    "time"
)

var burnCpu = make(chan bool)

func cpuBurn() {
    var burn bool = false
    for {
        select {
        case burn = <-burnCpu:
        default:
            if burn {
                _ = 5*5
            } else {
                time.Sleep(100 * time.Millisecond)
            }
        }
    }
}

func startCpu(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Starting CPU burn\n")
    burnCpu <- true
}

func stopCpu(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Stopping CPU burn\n")
    burnCpu <- false
}

func main() {
    go cpuBurn()
    http.HandleFunc("/start_cpu", startCpu)
    http.HandleFunc("/stop_cpu", stopCpu)
    http.ListenAndServe(":8080", nil)
}
comments powered by Disqus