'zrepl version' and 'zrepl control version' subcommand + maintainer README

Version is autodetected on build using git
If it cannot be detected with git, an override must be provided.

For tracability of distros, the distroy packagers should override as
well, which is why I added a README entry for package mainatiners.

refs #35
This commit is contained in:
Christian Schwarz 2017-11-18 20:34:28 +01:00
parent d59426a8cc
commit 896f31bbf3
7 changed files with 154 additions and 15 deletions

View File

@ -8,13 +8,23 @@ _TESTPKGS := $(ROOT) $(foreach p,$(SUBPKGS),$(ROOT)/$(p))
ARTIFACTDIR := artifacts ARTIFACTDIR := artifacts
ifndef ZREPL_VERSION
ZREPL_VERSION := $(shell git describe --dirty 2>/dev/null || echo "ZREPL_BUILD_INVALID_VERSION" )
ifeq ($(ZREPL_VERSION),ZREPL_BUILD_INVALID_VERSION) # can't use .SHELLSTATUS because Debian Stretch is still on gmake 4.1
$(error cannot infer variable ZREPL_VERSION using git and variable is not overriden by make invocation)
endif
endif
GO_LDFLAGS := "-X github.com/zrepl/zrepl/cmd.zreplVersion=$(ZREPL_VERSION)"
GO_BUILD := go build -ldflags $(GO_LDFLAGS)
generate: #not part of the build, must do that manually generate: #not part of the build, must do that manually
@for pkg in $(_TESTPKGS); do\ @for pkg in $(_TESTPKGS); do\
go generate "$$pkg" || exit 1; \ go generate "$$pkg" || exit 1; \
done; done;
build: build:
go build -o $(ARTIFACTDIR)/zrepl $(GO_BUILD) -o "$(ARTIFACTDIR)/zrepl"
test: test:
@for pkg in $(_TESTPKGS); do \ @for pkg in $(_TESTPKGS); do \
@ -54,10 +64,11 @@ docs-clean:
BUILDDIR=../artifacts/docs BUILDDIR=../artifacts/docs
release-bins: $(ARTIFACTDIR) vet test release-bins: $(ARTIFACTDIR) vet test
GOOS=linux GOARCH=amd64 go build -o "$(ARTIFACTDIR)/zrepl-linux-amd64" GOOS=linux GOARCH=amd64 $(GO_BUILD) -o "$(ARTIFACTDIR)/zrepl-linux-amd64"
GOOS=freebsd GOARCH=amd64 go build -o "$(ARTIFACTDIR)/zrepl-freebsd-amd64" GOOS=freebsd GOARCH=amd64 $(GO_BUILD) -o "$(ARTIFACTDIR)/zrepl-freebsd-amd64"
release: release-bins docs release: release-bins docs
clean: docs-clean clean: docs-clean
rm -rf "$(ARTIFACTDIR)" rm -rf "$(ARTIFACTDIR)"

View File

@ -20,6 +20,16 @@ zrepl is a ZFS filesystem backup & replication solution written in Go.
The above does not apply if you already implemented everything. The above does not apply if you already implemented everything.
Check out the *Coding Workflow* section below for details. Check out the *Coding Workflow* section below for details.
## Package Maintainer Information
* Follow the steps in `docs/installation.rst -> Compiling from Source` and read the Makefile / shell scripts used in this process.
* Make sure your distro is compatible with the paths in `docs/installation.rst`.
* Ship a default config that adheres to your distro's `hier` and logging system.
* Ship a service manager file and _please_ try to upstream it to this repository.
* Use `make release ZREPL_VERSION='mydistro-1.2.3_1'`
* Your distro's name and any versioning supplemental to zrepl's (e.g. package revesion) should be in this string
* Make sure you are informed about new zrepl versions, e.g. by subscribing to GitHub's release RSS feed.
## Developer Documentation ## Developer Documentation
First, use `./lazy.sh devesetup` to install build dependencies and read `docs/installation.rst -> Compiling from Source`. First, use `./lazy.sh devesetup` to install build dependencies and read `docs/installation.rst -> Compiling from Source`.

View File

@ -1,8 +1,11 @@
package cmd package cmd
import ( import (
"bytes"
"context" "context"
"encoding/json"
"github.com/pkg/errors" "github.com/pkg/errors"
"io"
"net" "net"
"net/http" "net/http"
"net/http/pprof" "net/http/pprof"
@ -29,8 +32,20 @@ func (j *ControlJob) JobName() string {
return j.Name return j.Name
} }
func (j *ControlJob) EndpointVersion(w http.ResponseWriter, r *http.Request) {
var buf bytes.Buffer
err := json.NewEncoder(&buf).Encode(NewZreplVersionInformation())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
io.WriteString(w, err.Error())
} else {
io.Copy(w, &buf)
}
}
const ( const (
ControlJobEndpointProfile string = "/debug/pprof/profile" ControlJobEndpointProfile string = "/debug/pprof/profile"
ControlJobEndpointVersion string = "/version"
) )
func (j *ControlJob) JobStart(ctx context.Context) { func (j *ControlJob) JobStart(ctx context.Context) {
@ -46,6 +61,7 @@ func (j *ControlJob) JobStart(ctx context.Context) {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle(ControlJobEndpointProfile, requestLogger{log, pprof.Profile}) mux.Handle(ControlJobEndpointProfile, requestLogger{log, pprof.Profile})
mux.Handle(ControlJobEndpointVersion, requestLogger{log, j.EndpointVersion})
server := http.Server{Handler: mux} server := http.Server{Handler: mux}
outer: outer:

View File

@ -1,7 +1,9 @@
package cmd package cmd
import ( import (
"bytes"
"context" "context"
"encoding/json"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"io" "io"
@ -26,10 +28,33 @@ var pprofCmdArgs struct {
seconds int64 seconds int64
} }
var controlVersionCmd = &cobra.Command{
Use: "version",
Short: "print version of running zrepl daemon",
Run: doControLVersionCmd,
}
func init() { func init() {
RootCmd.AddCommand(controlCmd) RootCmd.AddCommand(controlCmd)
controlCmd.AddCommand(pprofCmd) controlCmd.AddCommand(pprofCmd)
pprofCmd.Flags().Int64Var(&pprofCmdArgs.seconds, "seconds", 30, "seconds to profile") pprofCmd.Flags().Int64Var(&pprofCmdArgs.seconds, "seconds", 30, "seconds to profile")
controlCmd.AddCommand(controlVersionCmd)
}
func controlHttpClient() (client http.Client, err error) {
conf, err := ParseConfig(rootArgs.configFile)
if err != nil {
return http.Client{}, err
}
return http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", conf.Global.Control.Sockpath)
},
},
}, nil
} }
func doControlPProf(cmd *cobra.Command, args []string) { func doControlPProf(cmd *cobra.Command, args []string) {
@ -41,12 +66,6 @@ func doControlPProf(cmd *cobra.Command, args []string) {
os.Exit(1) os.Exit(1)
} }
conf, err := ParseConfig(rootArgs.configFile)
if err != nil {
log.Printf("error parsing config: %s", err)
die()
}
if cmd.Flags().Arg(0) != "cpu" { if cmd.Flags().Arg(0) != "cpu" {
log.Printf("only CPU profiles are supported") log.Printf("only CPU profiles are supported")
log.Printf("%s", cmd.UsageString()) log.Printf("%s", cmd.UsageString())
@ -60,6 +79,7 @@ func doControlPProf(cmd *cobra.Command, args []string) {
die() die()
} }
var out io.Writer var out io.Writer
var err error
if outfn == "-" { if outfn == "-" {
out = os.Stdout out = os.Stdout
} else { } else {
@ -71,12 +91,10 @@ func doControlPProf(cmd *cobra.Command, args []string) {
} }
log.Printf("connecting to daemon") log.Printf("connecting to daemon")
httpc := http.Client{ httpc, err := controlHttpClient()
Transport: &http.Transport{ if err != nil {
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { log.Printf("error parsing config: %s", err)
return net.Dial("unix", conf.Global.Control.Sockpath) die()
},
},
} }
log.Printf("profiling...") log.Printf("profiling...")
@ -98,3 +116,40 @@ func doControlPProf(cmd *cobra.Command, args []string) {
log.Printf("finished") log.Printf("finished")
} }
func doControLVersionCmd(cmd *cobra.Command, args []string) {
log := golog.New(os.Stderr, "", 0)
die := func() {
log.Printf("exiting after error")
os.Exit(1)
}
httpc, err := controlHttpClient()
if err != nil {
log.Printf("could not connect to daemon: %s", err)
die()
}
resp, err := httpc.Get("http://unix" + ControlJobEndpointVersion)
if err != nil {
log.Printf("error: %s", err)
die()
} else if resp.StatusCode != http.StatusOK {
var msg bytes.Buffer
io.CopyN(&msg, resp.Body, 4096)
log.Printf("error: %s", msg.String())
die()
}
var info ZreplVersionInformation
err = json.NewDecoder(resp.Body).Decode(&info)
if err != nil {
log.Printf("error unmarshaling response: %s", err)
die()
}
fmt.Println(info.String())
}

View File

@ -37,6 +37,7 @@ func doDaemon(cmd *cobra.Command, args []string) {
log := logger.NewLogger(conf.Global.logging.Outlets, 1*time.Second) log := logger.NewLogger(conf.Global.logging.Outlets, 1*time.Second)
log.Info(NewZreplVersionInformation().String())
log.Debug("starting daemon") log.Debug("starting daemon")
ctx := context.WithValue(context.Background(), contextKeyLog, log) ctx := context.WithValue(context.Background(), contextKeyLog, log)
ctx = context.WithValue(ctx, contextKeyLog, log) ctx = context.WithValue(ctx, contextKeyLog, log)

View File

@ -20,6 +20,10 @@ import (
// Printf(format string, v ...interface{}) // Printf(format string, v ...interface{})
//} //}
var (
zreplVersion string // set by build infrastructure
)
type Logger = *logger.Logger type Logger = *logger.Logger
var RootCmd = &cobra.Command{ var RootCmd = &cobra.Command{

42
cmd/version.go Normal file
View File

@ -0,0 +1,42 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"runtime"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "print version of zrepl binary (for running daemon 'zrepl control version' command)",
Run: doVersion,
}
func init() {
RootCmd.AddCommand(versionCmd)
}
func doVersion(cmd *cobra.Command, args []string) {
fmt.Println(NewZreplVersionInformation().String())
}
type ZreplVersionInformation struct {
Version string
RuntimeGOOS string
RuntimeGOARCH string
RUNTIMECompiler string
}
func NewZreplVersionInformation() *ZreplVersionInformation {
return &ZreplVersionInformation{
Version: zreplVersion,
RuntimeGOOS: runtime.GOOS,
RuntimeGOARCH: runtime.GOARCH,
RUNTIMECompiler: runtime.Compiler,
}
}
func (i *ZreplVersionInformation) String() string {
return fmt.Sprintf("zrepl version=%s GOOS=%s GOARCH=%s Compiler=%s",
i.Version, i.RuntimeGOOS, i.RuntimeGOARCH, i.RUNTIMECompiler)
}