gup

package module
v0.0.0-...-760a7c8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 13, 2017 License: MIT Imports: 14 Imported by: 0

README

gup: an opinionated Go binary updater GoDoc

Gup is a library for self-updating Go binaries. That is, fetching an update to your Go binary from the internet and applying the update.

Unlike other libraries like go-updater, which gup uses internally, gup makes the following assumptions:

  1. Updates should be downloaded from an HTTPS web server (e.g. a Google Cloud Storage bucket).
  2. Security matters to you and you always want to verify the SHA256 and ECDSA binary signatures of updates.
  3. You don't particularly care about the directory structure of your web server, i.e. you can give Gup control of an entire directory.

These assumptions make Gup much easier to use than the alternatives, which typically have security etc. as an add-on or paid feature.

Generating Keys

First, you'll want to generate a public and private ECDSA P256 keypair. To do this, just run gup genkey:

$ gup genkey
# wrote private_key.pem
# wrote public_key.pem
Note: You may copy+paste the following public key into your Go program:

gup.Config.PublicKey = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEm5DKV8SqS7HjIVtsFjGc93TYr/LA\naE9p72sF6qc1MjYMUoukScQoY0MysgEdekf/cmiWpKYwLc2rn8BnBRdz+w==\n-----END PUBLIC KEY-----\n"

As it suggests, you'll copy+paste the Go code it printed out into your Go program below.

Example code

A basic program looks like this:

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/slimsag/gup"
)

func main() {
	// Configure and start Gup.
	gup.Config.PublicKey = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEm5DKV8SqS7HjIVtsFjGc93TYr/LA\naE9p72sF6qc1MjYMUoukScQoY0MysgEdekf/cmiWpKYwLc2rn8BnBRdz+w==\n-----END PUBLIC KEY-----\n"
	gup.Config.UpdateURL = "https://storage.googleapis.com/my-bucket/updates/$GUP"
	gup.Config.CheckInterval = 5 * time.Second // For production, you'll want to use something larger
	gup.Start()

	// Wait for updates to become available.
	<-gup.UpdateAvailable
	fmt.Println("an update is available!")

	// Perform the update.
	_, err := gup.Update()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("update successful, please relaunch the program")
}

Creating index.json

The very first time you get started out with Gup, you'll want to create your "first patch" and the index.json file. This index file keeps track of all the versions of your program that are available, and it will also be uploaded to your webserver. To do this run build your binary and run gup bundle myprogram:

$ go build -o gup/bin/myprogram-latest ./example/
$ gup bundle gup/bin/myprogram-latest
wrote replacement patch bundle: gup/main-darwin-amd64-0.tgz
wrote index: gup/index.json
...

Now you'll want to upload the new files it wrote to the gup/ folder to your webserver (gup.Config.UpdateURL). For example with Google Cloud Storage:

$ gsutil -m -h Cache-Control:"Cache-Control:private, max-age=0, no-transform" cp -a public-read -r gup/* gs://my-bucket/updates

Creating a patch

To release a new version of your application, you'll create a patch using gup patch. This command will produce another gup/*.tgz patch bundle and update the gup/index.json to reflect the version being available. First, make a change to your application like adding fmt.Println("MyApp started!") at the top of main, then run:

$ mv gup/bin/myprogram-latest gup/bin/myprogram-prev
$ go build -o gup/bin/myprogram-latest ./example/
$ gup bundle ./gup/bin/myprogram-prev ./gup/bin/myprogram-latest 
wrote diff patch bundle: gup/main-darwin-amd64-1.tgz
updated index: gup/index.json

Now, in one terminal start the old program:

$ cp ./gup/bin/myprogram-prev myprogram-old
$ ./myprogram-old 

And in a second terminal, release the uploading these files to your web server:

$ gsutil -m -h Cache-Control:"Cache-Control:private, max-age=0, no-transform" cp -a public-read -r gup/* gs://my-bucket/updates

And you'll see:

$ ./myprogram-old 
an update is available!
2017/02/09 17:55:50 applying update main-darwin-amd64-1.tgz
update successful, please relaunch the program

Running the program once more, you'll see:

$ ./myprogram-old 
MyApp started!

Notes

  • You should backup at least private_key.pem, gup/index.json, and your 'latest' binary (e.g. gup/bin/myprogram-latest).
    • Failure to do this means you will no longer be able to publish updates, as Gup needs all three of these to successfully publish a new update.
  • In a real application, you should use a higher gup.Config.CheckInterval, e.g. 5 * time.Minute.
  • While the above shows commands for Google Cloud Storage, any HTTPS file host will do.
  • Gup patch bundle files (e.g. main-darwin-amd64-0.tgz) are designed to be easily introspected, e.g. try tar -xzf -C tmp/ main-darwin-amd64-0.tgz.
  • The above didn't cover replacement patches, which are different than the default binary patch mode. See gup bundle -h for details.
  • When uploading files, you do not want to overwrite the entire directory (i.e. you want to keep old .tgz files that are in the directory). Also be aware of how your file host caches index.json as that will affect your application's ability to discover updates quickly.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Config = struct {
	// UpdateURL is a template URL that is contacted to look for both the
	// index.json file, as well as all update tarballs. It must contain "$GUP"
	// as the file portion of the URL.
	UpdateURL string

	// PublicKey is the ECDSA public key to use for verifying the binary
	// against the signature file in a gup bundle.
	PublicKey string

	// Tag, which defaults to "main", specifies the tag to use when looking for
	// updates. If the tag is invalid (i.e. not in index.json) updates will
	// fail.
	Tag string

	// CheckInterval is the interval at which updates are checked for while the
	// program is running. Zero signals to not check for updates while the
	// program is running. The default is one hour.
	CheckInterval time.Duration

	// Client is the HTTP client to use when fetching updates. By default, it
	// has a 5s timeout.
	Client *http.Client
}{
	CheckInterval: 1 * time.Hour,
	Tag:           "main",
	Client:        &http.Client{Timeout: 5 * time.Second},
}
View Source
var UpdateAvailable = make(chan bool, 1)

UpdateAvailable is a channel that users can read from to get a signal for when an update is available.

Functions

func CheckNow

func CheckNow() bool

CheckNow checks for updates immediately, and returns true if one is available.

func Start

func Start()

Start starts gup. If the configuration is invalid, a panic occurs.

func Update

func Update() (bool, error)

Update checks if an update is available, and if it is, applies it. If updating is attempted but fails, an error is returned. If no update is available, Update simply returns (after making the HTTP request).

Types

This section is empty.

Directories

Path Synopsis
cmd
gup

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL