Skip to content

Instantly share code, notes, and snippets.

@mechinn
Last active November 1, 2019 08:40
Show Gist options
  • Save mechinn/1c9b74bc34858b828bd40b4c79c8d0cb to your computer and use it in GitHub Desktop.
Save mechinn/1c9b74bc34858b828bd40b4c79c8d0cb to your computer and use it in GitHub Desktop.
package main
import (
"archive/tar"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"runtime"
"strings"
"syscall"
)
const (
rootfs = "/k3os/system"
repo = "rancher/k3os"
latestReleaseURL = "https://api.github.com/repos/" + repo + "/releases/latest"
downloadReleaseURL = "https://github.com/" + repo + "/releases/download/%s/k3os-rootfs-" + runtime.GOARCH + ".tar.gz"
)
type githubRelease struct {
TagName string `json:"tag_name"`
}
// Returns the latest release tag from GitHub
func getRelease() (*githubRelease, error) {
resp, err := http.Get(latestReleaseURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
response := new(githubRelease)
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
return nil, err
}
return response, nil
}
// Remount the system partition as read/write
func remountFs() error {
return syscall.Mount("", rootfs, "", syscall.MS_REMOUNT, "")
}
// Fetch the latest root fs tarball and untar it into the system directory
func updateRootFs(releaseTag string) error {
k3OSRootFsURL := fmt.Sprintf(downloadReleaseURL, releaseTag)
resp, err := http.Get(k3OSRootFsURL)
if err != nil {
return err
}
defer resp.Body.Close()
gzr, err := gzip.NewReader(resp.Body)
if err != nil {
return err
}
defer gzr.Close()
tr := tar.NewReader(gzr)
for {
header, err := tr.Next()
switch err {
case nil:
//we have a header
case io.EOF:
// done
return nil
default:
// return any other error
return err
}
//remove the release from the header name to get the path on the rootfs
target := strings.TrimPrefix(header.Name, releaseTag)
//if the file is in the rootfs extract it
if strings.HasPrefix(target, rootfs) {
switch header.Typeflag {
case tar.TypeDir:
// if its a dir and it doesn't exist create it
if err := mkdirAll(target, os.FileMode(header.Mode)); err != nil {
return err
}
case tar.TypeSymlink:
if err := replaceSymlink(target, header.Linkname); err != nil {
return err
}
case tar.TypeReg:
if err := replaceFile(target, os.FileMode(header.Mode), tr); err != nil {
return err
}
}
}
}
}
func mkdirAll(dir string, mode os.FileMode) error {
if _, err := os.Stat(dir); err != nil {
//expected, make the directory
if err := os.MkdirAll(dir, mode); err != nil {
return err
}
}
//directory tree already exists
return nil
}
func replaceSymlink(link, dst string) error {
if _, err := os.Stat(link); err == nil {
//symlink exists, remove
if err := os.Remove(link); err != nil {
return err
}
}
return os.Symlink(dst, link)
}
func replaceFile(target string, mode os.FileMode, tr *tar.Reader) error {
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, mode)
if err != nil {
return err
}
_, err = io.Copy(f, tr)
f.Close()
return err
}
func main() {
release, err := getRelease()
if err != nil {
panic(err)
}
fmt.Println("Upgrading to k3OS " + release.TagName)
if err := remountFs(); err != nil {
panic(err)
}
if err := updateRootFs(release.TagName); err != nil {
panic(err)
}
fmt.Println("Complete!")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment