diff --git a/Dockerfile b/Dockerfile index 539c5a1..e90f7de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,11 @@ from golang:alpine3.14 -WORKDIR /go/src/app -COPY ./sandman.go . -RUN go build sandman.go +WORKDIR /root/ +COPY ./lazytainer.go . +RUN go build lazytainer.go FROM alpine:latest RUN apk --no-cache add docker-cli WORKDIR /root/ -COPY --from=0 /go/src/app/sandman ./ -ENV TIMEOUT=30 -ENV LABEL=sandman +COPY --from=0 /root/lazytainer ./ - -CMD ["./sandman"] \ No newline at end of file +CMD ["./lazytainer"] \ No newline at end of file diff --git a/README.md b/README.md index 61acc66..4677e8f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Sandman +# Lazytainer Putting your containers to sleep -*man me a sand* +*I don't really wanna do the work today* --- @@ -13,15 +13,14 @@ if it looks like you're trying to access a stopped container, it starts ### Want to test it? ``` -$ git clone https://github.com/vmorganp/sandman -$ cd sandman -$ docker-compose up -d --build +$ git clone https://github.com/vmorganp/Lazytainer +$ cd Lazytainer +$ docker-compose up ``` ## TODO - support multiple ports - test on common services -- better time adjustment - docker security probably? this really shouldn't be getting exposed except to forward traffic so idk probably firewall all non listed ports? - improve logging - inevitable bugfixes diff --git a/docker-compose.yaml b/docker-compose.yaml index 95e80b0..7dc81f7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,11 +1,11 @@ version: "3" services: - sandman: - container_name: sandman + lazytainer: + container_name: lazytainer build: . environment: - PORT=81 # TODO make this work with more than one port - - LABEL=sandman # sandman checks + - LABEL=lazytainer # value of com.lazytainer.marker for other containers that lazytainer checks - TIMEOUT=30 # number of seconds to let container idle ports: - 81:81 @@ -16,10 +16,10 @@ services: container_name: whoami2 image: containous/whoami command: --port 81 - network_mode: service:sandman + network_mode: service:lazytainer depends_on: - - sandman + - lazytainer # ports: # - 80:80 labels: - - "com.sandman.marker=sandman" \ No newline at end of file + - "com.lazytainer.marker=lazytainer" \ No newline at end of file diff --git a/sandman.go b/lazytainer.go similarity index 55% rename from sandman.go rename to lazytainer.go index 2b5c402..2a666d4 100644 --- a/sandman.go +++ b/lazytainer.go @@ -11,12 +11,37 @@ import ( func main() { inactive_seconds := 0 - // label := os.Getenv("LABEL") + // check if all of the necessary env vars are set, otherwise, use defaults + // how long a container is allowed to have no traffic before being stopped inactive_timeout, err := strconv.Atoi(os.Getenv("TIMEOUT")) - check(err) - // tx - // port := os.Getenv("PORT") - rx_history := []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + if check_env_fetch_recoverable(err) { + fmt.Println("using default because env variable TIMEOUT not set ") + inactive_timeout = 30 + } + + // how many polls to keep around + rx_history_length, err := strconv.Atoi(os.Getenv("RXHISTLENGTH")) + if check_env_fetch_recoverable(err) { + fmt.Println("using default because env variable RXHISTLENGTH not set ") + rx_history_length = 10 + } + + // number of packets required between first and last poll to keep container alive + min_packet_threshhold, err := strconv.Atoi(os.Getenv("MINPACKETTHRESH")) + if check_env_fetch_recoverable(err) { + fmt.Println("using default because env variable MINPACKETTHRESH not set ") + min_packet_threshhold = 10 + } + + // how many seconds to wait in between polls + poll_rate, err := strconv.Atoi(os.Getenv("POLLRATE")) + if check_env_fetch_recoverable(err) { + fmt.Println("using default because env variable POLLRATE not set ") + poll_rate = 1 + } + + rx_history := make([]int, rx_history_length) + for { // check if container is on container_state_on := is_container_on() @@ -27,6 +52,7 @@ func main() { rx_packets, err := strconv.Atoi(strings.TrimSpace(string(rx))) check(err) rx_history = append(rx_history[1:], rx_packets) + fmt.Println(rx_packets, "rx packets") // if the container is running, see if it needs to be stopped if container_state_on { @@ -37,27 +63,29 @@ func main() { check(err) // log out the results - println(active_clients, "active clients") - println(rx_packets, "rx packets") - fmt.Printf("%v rx history\n\n", rx_history) + fmt.Println(active_clients, "active clients") + // println(rx_packets, "rx packets") + // fmt.Printf("%v rx history", rx_history) - if active_clients == 0 && rx_history[0]+10 > rx_history[9] { + if active_clients == 0 && rx_history[0]+min_packet_threshhold > rx_history[len(rx_history)-1] { // count up if we have no active clients // if no clients are active and less than 10 packets recieved in the last 10 seconds inactive_seconds++ - println(inactive_seconds, "seconds without an active client") + fmt.Println(inactive_seconds, "seconds without an active client") if inactive_seconds > inactive_timeout { stop_containers() } } } else { // if more than 10 rx in last 10 seconds, start the container - if rx_history[0]+10 < rx_history[9] { + if rx_history[0]+min_packet_threshhold < rx_history[len(rx_history)-1] { start_containers() } } - - time.Sleep(time.Second) + fmt.Println("//////////////////////////////////////////////////////////////////////////////////") + for i := 0; i < poll_rate; i++ { + time.Sleep(time.Second) + } } } @@ -74,7 +102,7 @@ func newContainer(id string, state string) *container { func get_containers() []container { containers := []container{} - out, err := exec.Command("/bin/sh", "-c", "docker ps -a --no-trunc --filter label=\"com.sandman.marker=$LABEL\" --format \"{{.ID}} {{.State}}\"").Output() // todo make this handle multiple ports? + out, err := exec.Command("/bin/sh", "-c", "docker ps -a --no-trunc --filter label=\"com.lazytainer.marker=$LABEL\" --format \"{{.ID}} {{.State}}\"").Output() // todo make this handle multiple ports? check(err) fmt.Println(string(out)) if strings.TrimSpace(string(out)) == "" { @@ -98,7 +126,7 @@ func is_container_on() bool { } func stop_containers() { - println("stopping container(s)") + fmt.Println("stopping container(s)") containers := get_containers() idString := "" for _, c := range containers { @@ -111,7 +139,7 @@ func stop_containers() { } func start_containers() { - println("starting container(s)") + fmt.Println("starting container(s)") containers := get_containers() idString := "" for _, c := range containers { @@ -125,6 +153,17 @@ func start_containers() { func check(err error) { if err != nil { - panic(err) + fmt.Println(err) + // panic(err) } } + +func check_env_fetch_recoverable(err error) bool { + if err != nil { + if strings.Contains(err.Error(), "strconv.Atoi: parsing \"\": invalid syntax") { + return true + } + panic(err) + } + return false +}