Monitorable, gracefully restarting, self-upgrading binaries in Go (golang)

Overview

overseer

GoDoc Tests

overseer is a package for creating monitorable, gracefully restarting, self-upgrading binaries in Go (golang). The main goal of this project is to facilitate the creation of self-upgrading binaries which play nice with standard process managers, secondly it should expose a small and simple API with reasonable defaults.

overseer diagram

Commonly, graceful restarts are performed by the active process (dark blue) closing its listeners and passing these matching listening socket files (green) over to a newly started process. This restart causes any foreground process monitoring to incorrectly detect a program crash. overseer attempts to solve this by using a small process to perform this socket file exchange and proxying signals and exit code from the active process.

Features

  • Simple
  • Works with process managers (systemd, upstart, supervisor, etc)
  • Graceful, zero-down time restarts
  • Easy self-upgrading binaries

Install

go get github.com/jpillora/overseer

Quick example

This program works with process managers, supports graceful, zero-down time restarts and self-upgrades its own binary.

package main

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

	"github.com/jpillora/overseer"
	"github.com/jpillora/overseer/fetcher"
)

//create another main() to run the overseer process
//and then convert your old main() into a 'prog(state)'
func main() {
	overseer.Run(overseer.Config{
		Program: prog,
		Address: ":3000",
		Fetcher: &fetcher.HTTP{
			URL:      "http://localhost:4000/binaries/myapp",
			Interval: 1 * time.Second,
		},
	})
}

//prog(state) runs in a child process
func prog(state overseer.State) {
	log.Printf("app (%s) listening...", state.ID)
	http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "app (%s) says hello\n", state.ID)
	}))
	http.Serve(state.Listener, nil)
}

How it works:

  • overseer uses the main process to check for and install upgrades and a child process to run Program.
  • The main process retrieves the files of the listeners described by Address/es.
  • The child process is provided with these files which is converted into a Listener/s for the Program to consume.
  • All child process pipes are connected back to the main process.
  • All signals received on the main process are forwarded through to the child process.
  • Fetcher runs in a goroutine and checks for updates at preconfigured interval. When Fetcher returns a valid binary stream (io.Reader), the master process saves it to a temporary location, verifies it, replaces the current binary and initiates a graceful restart.
  • The fetcher.HTTP accepts a URL, it polls this URL with HEAD requests and until it detects a change. On change, we GET the URL and stream it back out to overseer. See also fetcher.S3.
  • Once a binary is received, it is run with a simple echo token to confirm it is a overseer binary.
  • Except for scheduled restarts, the active child process exiting will cause the main process to exit with the same code. So, overseer is not a process manager.

See Configuration options here and the runtime State available to your program here.

More examples

See the example/ directory and run example.sh, you should see the following output:

$ cd example/
$ sh example.sh
BUILT APP (1)
RUNNING APP
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) listening...
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) says hello
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) says hello
BUILT APP (2)
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) listening...
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) says hello
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) says hello
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) says hello
app#1 (c7940a5bfc3f0e8633d3bf775f54bb59f50b338e) exiting...
BUILT APP (3)
app#3 (b7614e7ff42eed8bb334ed35237743b0e4041678) listening...
app#3 (b7614e7ff42eed8bb334ed35237743b0e4041678) says hello
app#3 (b7614e7ff42eed8bb334ed35237743b0e4041678) says hello
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) says hello
app#2 (3dacb8bc673c1b4d38f8fb4fad5b017671aa8a67) exiting...
app#3 (b7614e7ff42eed8bb334ed35237743b0e4041678) says hello

Note: app#1 stays running until the last request is closed.

Only use graceful restarts

func main() {
	overseer.Run(overseer.Config{
		Program: prog,
		Address: ":3000",
	})
}

Send main a SIGUSR2 (Config.RestartSignal) to manually trigger a restart

Only use auto-upgrades, no restarts

func main() {
	overseer.Run(overseer.Config{
		Program: prog,
		NoRestart: true,
		Fetcher: &fetcher.HTTP{
			URL:      "http://localhost:4000/binaries/myapp",
			Interval: 1 * time.Second,
		},
	})
}

Your binary will be upgraded though it will require manual restart from the user, suitable for creating self-upgrading command-line applications.

Multi-platform binaries using a dynamic fetch URL

func main() {
	overseer.Run(overseer.Config{
		Program: prog,
		Fetcher: &fetcher.HTTP{
			URL: "http://localhost:4000/binaries/app-"+runtime.GOOS+"-"+runtime.GOARCH,
			//e.g.http://localhost:4000/binaries/app-linux-amd64
		},
	})
}

Known issues

  • The master process's overseer.Config cannot be changed via an upgrade, the master process must be restarted.
    • Therefore, Addresses can only be changed by restarting the main process.
  • Currently shells out to mv for moving files because mv handles cross-partition moves unlike os.Rename.
  • Package init() functions will run twice on start, once in the main process and once in the child process.

More documentation

Third-party Fetchers

Contributing

See CONTRIBUTING.md

Comments
  • "access is denied" on Windows "move"

    On Windows once the temp binary is downloaded and confirmed to be executable overseer attempts to replace the currently running file with the new one. There is windows specific code added for this case:

    https://github.com/jpillora/overseer/blob/7279a36d9d3f916fe113c1a1712483251ffe46fd/sys_windows.go#L24-L40

    Which renames the temp file to match the name of the current file and then shells out to cmd to run "move". But for me this is returning:

    image

    It doesn't really appear to be anything with overseer in particular because if I just run this command directly:

    image

    Same result.

    I see this issue from around the time that windows support was added: https://github.com/jpillora/overseer/issues/7. But it's unclear to me if this is the same thing because that error message seems to be different. Also, @tgulacsi did work to this sys_windows file after that issue was created. So that makes me think it was working at that point - but not sure.

    Does anyone know if this is something specific to my system. Or is it as that issue describes and move just doesn't work right now? Do we need to rename the currently running file before doing the move and rename it back if the move fails?

    @tgulacsi if you're available I would love to hear your thoughts.

    opened by joshforbes 8
  • Lots of output: [overseer master] proxy signal (urgent I/O condition)

    Lots of output: [overseer master] proxy signal (urgent I/O condition)

    It seems it always outputting those lines every few seconds, is there any meaning about this? or is it because my VPS using OpenVZ?

    Mar 27 15:13:19 indo.x.com runSvc.sh[5645]: 2020/03/27 15:13:19 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:13:29 indo.x.com runSvc.sh[5645]: 2020/03/27 15:13:29 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:13:31 indo.x.com runSvc.sh[5645]: 2020/03/27 15:13:31 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:14:41 indo.x.com runSvc.sh[5645]: 2020/03/27 15:14:41 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:15:31 indo.x.com runSvc.sh[5645]: 2020/03/27 15:15:31 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:15:49 indo.x.com runSvc.sh[5645]: 2020/03/27 15:15:49 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:15:49 indo.x.com runSvc.sh[5645]: 2020/03/27 15:15:49 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:16:39 indo.x.com runSvc.sh[5645]: 2020/03/27 15:16:39 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:16:39 indo.x.com runSvc.sh[5645]: 2020/03/27 15:16:39 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:17:29 indo.x.com runSvc.sh[5645]: 2020/03/27 15:17:29 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:17:31 indo.x.com runSvc.sh[5645]: 2020/03/27 15:17:31 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:17:31 indo.x.com runSvc.sh[5645]: 2020/03/27 15:17:31 [overseer master] proxy signal (urgent I/O condition)
    Mar 27 15:17:31 indo.x.com runSvc.sh[5645]: 2020/03/27 15:17:31 [overseer master] proxy signal (urgent I/O condition)
    
    opened by kokizzu 8
  • "failed to run temp binary" on Windows

    Hello 👋

    Thanks for the great library. It was simple to get it implemented in our code base. We have it working successfully on Mac but I'm running into a bit of trouble on Windows. We're using the HTTP fetcher which is downloading the file and placing it in a temp directory successfully (can see it show up in file explorer). But when the fetch process tries to see if the downloaded binary is executable it returns an error and bails out:

    failed to run temp binary: exec: "C:\\Users\\Level\\AppData\\Local\\Temp\\overseer-1dc5f76e2b9039ce": file does not exist (C:\Users\Level\AppData\Local\Temp\overseer-1dc5f76e2b9039ce) output ""

    The file certainly does exist but for some reason Go thinks it doesn't. The error is coming from this line: https://github.com/jpillora/overseer/blob/6877a7e73f6a4bebbfe1999fcbf84e52a3c1e6ca/proc_master.go#L287

    image

    Does anyone have any experience or advice related to this error? Thanks!

    opened by joshforbes 6
  • Windows watchParent() fix

    Windows watchParent() fix

    Slave process watches its parent by sending Signal(0), but as stated in os/exec.go sending Interrupt on Windows is not implemented. This cause prog exits with status code 1 error.

    // Signal sends a signal to the Process.
    // Sending Interrupt on Windows is not implemented.
    func (p *Process) Signal(sig Signal) error {
    	return p.signal(sig)
    }
    

    This fix uses gopsutil to check for process existence on Windows.

    opened by chicknsoup 6
  • How can i use it with gin ?

    How can i use it with gin ?

    I can't find any documentation about using gin and overseer together.Below is my code, how can I use them together?

    router := gin.Default()
    router.Use(tlsHandler())
    router.RunTLS(":8080", ".pem", ".key")
    
    question 
    opened by LeaderBoy 5
  • Support binary diffs, versions

    Support binary diffs, versions

    I've stumbled upon https://github.com/sanbornm/go-selfupdate which solves the binary diff problem nicely, but lacks overseer's great graceful restart capabilities.

    Maybe a marriage made in heaven?

    a) copy code from go-selfupdate b) just use go-selfupdate cli to generate the versions, and add a proper fetcher for such config?

    I'm willing to help, but please decide which way to go, what kind and level of integration is desired!

    enhancement 
    opened by tgulacsi 5
  • fix windows update

    fix windows update

    The tmpBinPath did not have the .exe extension on Windows which would cause the update to fail because Windows only considers files that correlate with the PATHEXT variable and that does not include no extension files.

    Closes #55

    opened by audiolion 3
  • add notice for users don't printf in func init()

    add notice for users don't printf in func init()

    if use a printf in func init(){}

    This may cause the upgrade check to fail!

    user will got sanity check failed

    eg: add

    func init() {
    	fmt.Println("hahhahahhahahahaaha")
    }
    

    to github.com/jpillora/overseer/example/main.go

    bogon:example qiantao$ ./example.sh 
    BUILT APP (1)
    RUNNING APP
    hahhahahhahahahaaha
    2019/03/22 17:22:13 [overseer master] run
    2019/03/22 17:22:13 [overseer master] setupSignalling
    2019/03/22 17:22:13 [overseer master] checking for updates...
    2019/03/22 17:22:13 [overseer master] no updates
    2019/03/22 17:22:13 [overseer master] starting /Users/qiantao/work/GoTestSrc/src/github.com/jpillora/overseer/example/my_app
    hahhahahhahahahaaha
    2019/03/22 17:22:13 [overseer slave#1] run
    2019/03/22 17:22:13 [overseer slave#1] slave wait sp.Config.RestartSignal
    2019/03/22 17:22:13 [overseer slave#1] start program
    app#1 start
    app#1 BuildDate=20190322_172211
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) listening...
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=0)
    app#1 work
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=0)
    app#1 work
    BUILT APP (2)
    2019/03/22 17:22:18 [overseer master] streaming update...
    2019/03/22 17:22:18 [overseer master] sanity check failed
    2019/03/22 17:22:18 [overseer master] checking for updates...
    app#1 work
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=0)
    2019/03/22 17:22:19 [overseer master] no updates
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=0)
    app#1 work
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=5000000000)
    BUILT APP (3)
    app#1 work
    2019/03/22 17:22:23 [overseer master] streaming update...
    2019/03/22 17:22:23 [overseer master] sanity check failed
    2019/03/22 17:22:23 [overseer master] checking for updates...
    2019/03/22 17:22:24 [overseer master] no updates
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=0)
    app#1 work
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=0)
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=5000000000)
    app#1 (45fb899a1abd7bd65c698fbcbc6525bade97932f) says hello (d=0)
    app#1 work
    2019/03/22 17:22:27 [overseer master] proxy signal (terminated)
    2019/03/22 17:22:27 [overseer master] prog exited with -1
    
    
    bug help wanted 
    opened by guhan121 3
  • [Bug] Document and expose the overseer sanity check for cases where it needs to be run manually

    [Bug] Document and expose the overseer sanity check for cases where it needs to be run manually

    I'm using spf13/cobra with an "update" subcommand, and the sanity check fails as it calls the binary without arguments, which means the help page is printed, instead of the token.

    I can add the "missing" arg before execute, Iff I know I need it - when OVERSEER_BIN_UPD env var is set. So this constant should be exported, and the needed special behaviour of the binary documented.

    bug 
    opened by tgulacsi 3
  • [overseer master] proxy signal (broken pipe)

    [overseer master] proxy signal (broken pipe)

    sometimes happen, how to do with this ?

    [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe) [overseer master] proxy signal (broken pipe)

    opened by lihui-hub 2
  • Master gortoutine is blocked in handleSignal and SIGUSR1 doesn't work

    Master gortoutine is blocked in handleSignal and SIGUSR1 doesn't work

    When Restarting is triggered, master process is blocked in select and as a result, it cannot handle sigusr1 from child process. image

    image

    Can I create a new goroutine to call mp.triggerRestart() in func handleSignal

    opened by xyzhu1120 2
  • Can I not set Address And Addresses? My app isn't used to listen any port.

    Can I not set Address And Addresses? My app isn't used to listen any port.

    When I don't set both Address and Addresses, it has a mistake:

    panic: runtime error: invalid memory address or nil pointer dereference
    	panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x603e20]
    
    goroutine 1 [running]:
    net/http.(*onceCloseListener).close(0xc000092cf0)
    	/usr/local/go/src/net/http/server.go:3479 +0x20
    sync.(*Once).doSlow(0xc000100180?, 0xc0000c38d0?)
    	/usr/local/go/src/sync/once.go:68 +0xc2
    sync.(*Once).Do(...)
    	/usr/local/go/src/sync/once.go:59
    net/http.(*onceCloseListener).Close(0xc000092cf0?)
    	/usr/local/go/src/net/http/server.go:3475 +0x4a
    panic({0x640680, 0x84d140})
    	/usr/local/go/src/runtime/panic.go:838 +0x207
    net/http.(*onceCloseListener).Accept(0x6ee250?)
    	<autogenerated>:1 +0x24
    net/http.(*Server).Serve(0xc0001000e0, {0x0, 0x0})
    	/usr/local/go/src/net/http/server.go:3039 +0x385
    net/http.Serve(...)
    	/usr/local/go/src/net/http/server.go:2543
    main.prog({0x1, {0xc000020090, 0x28}, {0xc0a568efa87b5794, 0x4aa32, 0x85a2e0}, {0x0, 0x0}, {0x889d30, 0x0, ...}, ...})
    	/home/lee/Projects/overseer/example/main.go:26 +0x1ad
    github.com/jpillora/overseer.(*slave).run(0xc000100000)
    	/home/lee/Projects/overseer/proc_slave.go:77 +0x315
    github.com/jpillora/overseer.runErr(0xc0000fe000)
    	/home/lee/Projects/overseer/overseer.go:162 +0x12b
    github.com/jpillora/overseer.Run({0x0, 0x6a77a0, {0x0, 0x0}, {0x0, 0x0, 0x0}, {0x0, 0x0}, 0x0, ...})
    	/home/lee/Projects/overseer/overseer.go:99 +0x99
    main.main()
    
    
    opened by SteveCruise 1
  • Operational inquiries

    Operational inquiries

    Hello, I am interested in using this library but I have a few questions about how it works.

    Fetcher: &fetcher.HTTP{ URL: "http://localhost:4000/binaries/myapp", Interval: 1 * time.Second, }

    • The program that does every x time intervals?
    • How do you know you have an update?
    • When download a new binary, does it erase the old one?
    • Some example with systemd?

    I hope someone can answer my questions. Kind regards

    opened by tomaswarynyca 0
  • Error with windows build

    Error with windows build

    I'm trying to build a windows version (.exe) for my project on linux using CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GOOS=windows GOARCH=amd64 go build -ldflags -H=windowsgui -o ./builds/jacp-windows-raw.exe but it shows this error:

    ../../go/pkg/mod/github.com/jpillora/[email protected]/proc_slave_windows.go:12:2: missing go.sum entry for module providing package github.com/StackExchange/wmi (imported by github.com/jpillora/overseer); to add:
    

    @jpillora

    opened by hackermondev 3
Owner
Jaime Pillora
Jaime Pillora
A bytecode-based virtual machine to implement scripting/filtering support in your golang project.

eval-filter Implementation Scripting Facilities Types Built-In Functions Conditionals Loops Functions Case/Switch Use Cases Security Denial of service

Steve Kemp 92 Dec 30, 2022
Gentee - script programming language for automation. It uses VM and compiler written in Go (Golang).

Gentee script programming language Gentee is a free open source script programming language. The Gentee programming language is designed to create scr

Alexey Krivonogov 101 Dec 15, 2022
A BASIC interpreter written in golang.

05 PRINT "Index" 10 PRINT "GOBASIC!" 20 PRINT "Limitations" Arrays Line Numbers IF Statement DATA / READ Statements Builtin Functions Types 30 PRINT "

Steve Kemp 289 Dec 24, 2022
This package is built for Embedding PHP into Golang.

GoEmPHP This package is built for Embedding PHP into Golang. It is easy to use: script = php.New() script.Startup() defer script.Close()

Xing 215 Jul 2, 2022
PHP bindings for the Go programming language (Golang)

PHP bindings for Go This package implements support for executing PHP scripts, exporting Go variables for use in PHP contexts, attaching Go method rec

Alex Palaistras 883 Dec 27, 2022
v8 javascript engine binding for golang

Go-V8 V8 JavaScript engine bindings for Go. Features Thread safe Thorough and careful testing Boolean, Number, String, Object, Array, Regexp, Function

Hoping White 208 Nov 21, 2022
A simple virtual machine - compiler & interpreter - written in golang

go.vm Installation Build without Go Modules (Go before 1.11) Build with Go Modules (Go 1.11 or higher) Usage Opcodes Notes The compiler The interprete

Steve Kemp 262 Dec 17, 2022
A JavaScript interpreter in Go (golang)

otto -- import "github.com/robertkrimen/otto" Package otto is a JavaScript parser and interpreter written natively in Go. http://godoc.org/github.com/

Robert Krimen 7.1k Jan 2, 2023
Genetic Algorithms library written in Go / golang

Description Genetic Algorithms for Go/Golang Install $ go install git://github.com/thoj/go-galib.git Compiling examples: $ git clone git://github.com

Thomas Jager 193 Sep 27, 2022
This is a Virtual Operating System made by using GOLANG and FYNE.

Virtual-Operating-System This is a Virtual Operating System made by using GOLANG and FYNE. Hello! All In this project I have made a virtual Operating

SURBHI SINHA 1 Nov 1, 2021
Virtual Operating System Using Golang

Virtual Operating System Virtual Operating System Using Golang And Fyne Installation 1.Install Go 2.Install Gcc 3.Install Fyne Using This Command:- g

Ranjit Kumar Sahoo 2 Jun 5, 2022
Curry é um crawler escrito em Golang com a finalidade de verificar o valor do câmbio de USD(Dólar) x BRL(Real) em lojas no Paraguay.

?? Curry - Câmbios ?? Valor do câmbio(USDxBRL) em lojas no Paraguay ?? Sobre Curry é um WebCrawler escrito em Golang com finalidade de verificar os va

bitebait 3 Nov 15, 2022
interpreter for the basic language written in golang

jirachi interpreter for the basic language written in golang The plan supports the following functions: Arithmetic Operations (+, -, *, /, ^) Comparis

菜菜 6 Sep 17, 2022
Virtual-Operating-System - Virtual Operating System Using Golang And Fyne Implemented Gallery app

Virtual Operating System Virtual Operating System Using Golang And Fyne Implemen

Arjit Kumar 0 Jan 1, 2022
Cc - Toy C compiler for golang

Grammars program = funcDecl* decl = declspec declarator ("{" compou

Kazuki Nitta 1 May 2, 2022
OperatingSys-GO - A Virtual Operating System made by using GOLANG and FYNE

Operating-System This is a Virtual Operating System made by using GOLANG and FYN

null 0 Jan 2, 2022
Monkey-go - Writing An Interpreter In Golang

monkey-go Learning how to write an Interpreter called Monkey using Go! If you're

Saad Guessous 3 Oct 5, 2022
BfInterpreterGo - Brainfuck interpreter built in golang

bfInterpreterGo Brainfuck interpreter built in golang. Usage main.exe --filename

voviner 1 Jan 7, 2022
Brainfuck interpreter in golang

Brainfuck Interpreter in Go brianfuck interpreter implimented in go brianfuck he

null 2 Aug 2, 2022