use bin dir as tmp dir for next binary, copy owner

master
Jaime Pillora 2016-02-09 17:01:06 +11:00
parent 0de2aea26b
commit 29e33ed1c7
5 changed files with 50 additions and 31 deletions

View File

@ -110,12 +110,12 @@ app#3 (286848c2aefcd3f7321a65b5e4efae987fb17911) exiting...
1. Then run it with: 1. Then run it with:
```sh ```sh
#run app inside alpine linux (5MB linux distro) docker run -d -v /path/on/docker/host/myapp/:/home/ -w /home/ debian -w /home/app
docker run -d -v /path/on/docker/host/myapp/:/home/ -w /home/ alpine -w /home/app
``` ```
1. For testing, swap out `-d` (daemonize) for `--rm -it` (remove on exit, input, terminal) 1. For testing, swap out `-d` (daemonize) for `--rm -it` (remove on exit, input, terminal)
1. `app` can use the current working directory as storage 1. `app` can use the current working directory as storage
1. `debian` doesn't ship with TLS certs, you can mount them in with `-v /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt`
### Alternatives ### Alternatives
@ -124,8 +124,8 @@ app#3 (286848c2aefcd3f7321a65b5e4efae987fb17911) exiting...
### TODO ### TODO
* Github fetcher (given a repo) * Log levels
* S3 fetcher (given a bucket and credentials) * Github fetcher (given a repo, poll releases)
* etcd fetcher (given a cluster, watch key) * etcd fetcher (given a cluster, watch key)
* `overseer` CLI tool ([TODO](cmd/overseer/TODO.md)) * `overseer` CLI tool ([TODO](cmd/overseer/TODO.md))
* `overseer` package * `overseer` package

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/jpillora/overseer" "github.com/jpillora/overseer"
"github.com/jpillora/overseer/fetcher"
) )
//see example.sh for the use-case //see example.sh for the use-case
@ -32,9 +33,9 @@ func main() {
Log: true, //display log of overseer actions Log: true, //display log of overseer actions
Program: prog, Program: prog,
Address: ":5001", Address: ":5001",
// Fetcher: &fetcher.HTTP{ Fetcher: &fetcher.HTTP{
// URL: "http://localhost:5002/myappnew", URL: "http://localhost:5002/myappnew",
// Interval: 1 * time.Second, Interval: 1 * time.Second,
// }, },
}) })
} }

View File

@ -21,14 +21,14 @@ import (
"github.com/kardianos/osext" "github.com/kardianos/osext"
) )
var tmpBinPath = filepath.Join(os.TempDir(), "overseer") var tmpBinPath = filepath.Join(os.TempDir(), "overseer-"+token())
//a overseer master process //a overseer master process
type master struct { type master struct {
Config Config
slaveCmd *exec.Cmd slaveCmd *exec.Cmd
slaveExtraFiles []*os.File slaveExtraFiles []*os.File
binPath string binPath, tmpBinPath string
binPerms os.FileMode binPerms os.FileMode
binHash []byte binHash []byte
restartMux sync.Mutex restartMux sync.Mutex
@ -90,12 +90,11 @@ func (mp *master) checkBinary() error {
io.Copy(hash, f) io.Copy(hash, f)
mp.binHash = hash.Sum(nil) mp.binHash = hash.Sum(nil)
f.Close() f.Close()
//tmp path //test bin<->tmpbin moves
tmpPath := mp.binPath + ".tmp" if err := os.Rename(mp.binPath, tmpBinPath); err != nil {
if err := os.Rename(mp.binPath, tmpPath); err != nil {
return fmt.Errorf("cannot move binary (%s)", err) return fmt.Errorf("cannot move binary (%s)", err)
} }
if err := os.Rename(tmpPath, mp.binPath); err != nil { if err := os.Rename(tmpBinPath, mp.binPath); err != nil {
return fmt.Errorf("cannot move binary back (%s)", err) return fmt.Errorf("cannot move binary back (%s)", err)
} }
return nil return nil
@ -195,6 +194,7 @@ func (mp *master) fetch() {
if mp.restarting { if mp.restarting {
return //skip if restarting return //skip if restarting
} }
mp.logf("checking for updates...") mp.logf("checking for updates...")
reader, err := mp.Fetcher.Fetch() reader, err := mp.Fetcher.Fetch()
if err != nil { if err != nil {
@ -205,19 +205,20 @@ func (mp *master) fetch() {
mp.logf("no updates") mp.logf("no updates")
return //fetcher has explicitly said there are no updates return //fetcher has explicitly said there are no updates
} }
mp.logf("streaming update...")
//optional closer //optional closer
if closer, ok := reader.(io.Closer); ok { if closer, ok := reader.(io.Closer); ok {
defer closer.Close() defer closer.Close()
} }
tmpBin, err := os.Create(tmpBinPath) tmpBin, err := os.OpenFile(tmpBinPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil { if err != nil {
mp.logf("failed to open temp binary: %s", err) mp.logf("failed to open temp binary: %s", err)
return return
} }
defer func() { // defer func() {
tmpBin.Close() // tmpBin.Close()
os.Remove(tmpBinPath) // os.Remove(tmpBinPath)
}() // }()
//tee off to sha1 //tee off to sha1
hash := sha1.New() hash := sha1.New()
reader = io.TeeReader(reader, hash) reader = io.TeeReader(reader, hash)
@ -235,20 +236,30 @@ func (mp *master) fetch() {
} }
//copy permissions //copy permissions
if err := tmpBin.Chmod(mp.binPerms); err != nil { if err := tmpBin.Chmod(mp.binPerms); err != nil {
mp.logf("failed to make binary executable: %s", err) mp.logf("failed to make temp binary executable: %s", err)
return
}
if err := tmpBin.Chown(uid, gid); err != nil {
mp.logf("failed to change owner of binary: %s", err)
return
}
if _, err := tmpBin.Stat(); err != nil {
mp.logf("failed to stat temp binary: %s", err)
return return
} }
tmpBin.Close() tmpBin.Close()
if _, err := os.Stat(tmpBinPath); err != nil {
mp.logf("failed to stat temp binary by path: %s", err)
return
}
if mp.Config.PreUpgrade != nil { if mp.Config.PreUpgrade != nil {
if err := mp.Config.PreUpgrade(tmpBinPath); err != nil { if err := mp.Config.PreUpgrade(tmpBinPath); err != nil {
mp.logf("user cancelled upgrade: %s", err) mp.logf("user cancelled upgrade: %s", err)
return return
} }
} }
//overseer sanity check, dont replace our good binary with a text file //overseer sanity check, dont replace our good binary with a non-executable file
buff := make([]byte, 8) tokenIn := token()
rand.Read(buff)
tokenIn := hex.EncodeToString(buff)
cmd := exec.Command(tmpBinPath) cmd := exec.Command(tmpBinPath)
cmd.Env = []string{envBinCheck + "=" + tokenIn} cmd.Env = []string{envBinCheck + "=" + tokenIn}
tokenOut, err := cmd.Output() tokenOut, err := cmd.Output()
@ -261,7 +272,7 @@ func (mp *master) fetch() {
return return
} }
//overwrite! //overwrite!
if err := move(mp.binPath, tmpBinPath); err != nil { if err := os.Rename(tmpBinPath, mp.binPath); err != nil {
mp.logf("failed to overwrite binary: %s", err) mp.logf("failed to overwrite binary: %s", err)
return return
} }
@ -372,3 +383,9 @@ func (mp *master) logf(f string, args ...interface{}) {
log.Printf("[overseer master] "+f, args...) log.Printf("[overseer master] "+f, args...)
} }
} }
func token() string {
buff := make([]byte, 8)
rand.Read(buff)
return hex.EncodeToString(buff)
}

View File

@ -11,9 +11,10 @@ import (
"syscall" "syscall"
) )
const supported = true
var ( var (
supported = true
uid = syscall.Getuid()
gid = syscall.Getgid()
SIGUSR1 = syscall.SIGUSR1 SIGUSR1 = syscall.SIGUSR1
SIGUSR2 = syscall.SIGUSR2 SIGUSR2 = syscall.SIGUSR2
SIGTERM = syscall.SIGTERM SIGTERM = syscall.SIGTERM

View File

@ -2,4 +2,4 @@
package overseer package overseer
const supported = false var supported = false