storj/cmd/storagenode-updater/binary.go

122 lines
2.8 KiB
Go
Raw Permalink Normal View History

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"archive/zip"
"bufio"
"bytes"
"context"
"io"
"net/http"
"os"
"os/exec"
"strings"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/common/sync2"
"storj.io/private/version"
)
func binaryVersion(location string) (version.SemVer, error) {
out, err := exec.Command(location, "version").CombinedOutput()
if err != nil {
zap.L().Info("Command output.", zap.ByteString("Output", out))
return version.SemVer{}, err
}
scanner := bufio.NewScanner(bytes.NewReader(out))
for scanner.Scan() {
line := scanner.Text()
prefix := "Version: "
if strings.HasPrefix(line, prefix) {
line = line[len(prefix):]
return version.NewSemVer(line)
}
}
return version.SemVer{}, errs.New("unable to determine binary version")
}
func downloadBinary(ctx context.Context, url, target string) error {
f, err := os.CreateTemp("", createPattern(url))
if err != nil {
return errs.New("cannot create temporary archive: %v", err)
}
defer func() {
err = errs.Combine(err,
f.Close(),
os.Remove(f.Name()),
)
}()
zap.L().Info("Download started.", zap.String("From", url), zap.String("To", f.Name()))
if err = downloadArchive(ctx, f, url); err != nil {
return errs.Wrap(err)
}
if err = unpackBinary(ctx, f.Name(), target); err != nil {
return errs.Wrap(err)
}
zap.L().Info("Download finished.", zap.String("From", url), zap.String("To", f.Name()))
return nil
}
func downloadArchive(ctx context.Context, file io.Writer, url string) (err error) {
resp, err := httpGet(ctx, url)
if err != nil {
return err
}
defer func() { err = errs.Combine(err, resp.Body.Close()) }()
if resp.StatusCode != http.StatusOK {
return errs.New("bad status: %s", resp.Status)
}
_, err = sync2.Copy(ctx, file, resp.Body)
return err
}
// unpackBinary unpack zip compressed binary.
func unpackBinary(ctx context.Context, archive, target string) (err error) {
zipReader, err := zip.OpenReader(archive)
if err != nil {
return err
}
defer func() { err = errs.Combine(err, zipReader.Close()) }()
if len(zipReader.File) != 1 {
return errs.New("archive should contain only one file")
}
zipedExec, err := zipReader.File[0].Open()
if err != nil {
return err
}
defer func() { err = errs.Combine(err, zipedExec.Close()) }()
newExec, err := os.OpenFile(target, os.O_CREATE|os.O_EXCL|os.O_WRONLY, os.FileMode(0755))
if err != nil {
return err
}
defer func() { err = errs.Combine(err, newExec.Close()) }()
_, err = sync2.Copy(ctx, newExec, zipedExec)
if err != nil {
return errs.Combine(err, os.Remove(newExec.Name()))
}
return nil
}
func httpGet(ctx context.Context, url string) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
return http.DefaultClient.Do(req)
}