Last active
November 1, 2019 08:40
-
-
Save mechinn/1c9b74bc34858b828bd40b4c79c8d0cb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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