From 896f31bbf3fcba31c25caa970ab90861a8fd2a9d Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Sat, 18 Nov 2017 20:34:28 +0100 Subject: [PATCH] '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 --- Makefile | 17 +++++++-- README.md | 10 +++++ cmd/config_job_control.go | 16 ++++++++ cmd/control.go | 79 +++++++++++++++++++++++++++++++++------ cmd/daemon.go | 1 + cmd/main.go | 4 ++ cmd/version.go | 42 +++++++++++++++++++++ 7 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 cmd/version.go diff --git a/Makefile b/Makefile index 5b14a63..28bc59b 100644 --- a/Makefile +++ b/Makefile @@ -8,13 +8,23 @@ _TESTPKGS := $(ROOT) $(foreach p,$(SUBPKGS),$(ROOT)/$(p)) 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 @for pkg in $(_TESTPKGS); do\ go generate "$$pkg" || exit 1; \ done; build: - go build -o $(ARTIFACTDIR)/zrepl + $(GO_BUILD) -o "$(ARTIFACTDIR)/zrepl" test: @for pkg in $(_TESTPKGS); do \ @@ -54,10 +64,11 @@ docs-clean: BUILDDIR=../artifacts/docs release-bins: $(ARTIFACTDIR) vet test - GOOS=linux GOARCH=amd64 go build -o "$(ARTIFACTDIR)/zrepl-linux-amd64" - GOOS=freebsd GOARCH=amd64 go build -o "$(ARTIFACTDIR)/zrepl-freebsd-amd64" + GOOS=linux GOARCH=amd64 $(GO_BUILD) -o "$(ARTIFACTDIR)/zrepl-linux-amd64" + GOOS=freebsd GOARCH=amd64 $(GO_BUILD) -o "$(ARTIFACTDIR)/zrepl-freebsd-amd64" release: release-bins docs + clean: docs-clean rm -rf "$(ARTIFACTDIR)" diff --git a/README.md b/README.md index f80949e..5c73709 100644 --- a/README.md +++ b/README.md @@ -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. 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 First, use `./lazy.sh devesetup` to install build dependencies and read `docs/installation.rst -> Compiling from Source`. diff --git a/cmd/config_job_control.go b/cmd/config_job_control.go index 0c636f0..7328cfb 100644 --- a/cmd/config_job_control.go +++ b/cmd/config_job_control.go @@ -1,8 +1,11 @@ package cmd import ( + "bytes" "context" + "encoding/json" "github.com/pkg/errors" + "io" "net" "net/http" "net/http/pprof" @@ -29,8 +32,20 @@ func (j *ControlJob) JobName() string { 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 ( ControlJobEndpointProfile string = "/debug/pprof/profile" + ControlJobEndpointVersion string = "/version" ) func (j *ControlJob) JobStart(ctx context.Context) { @@ -46,6 +61,7 @@ func (j *ControlJob) JobStart(ctx context.Context) { mux := http.NewServeMux() mux.Handle(ControlJobEndpointProfile, requestLogger{log, pprof.Profile}) + mux.Handle(ControlJobEndpointVersion, requestLogger{log, j.EndpointVersion}) server := http.Server{Handler: mux} outer: diff --git a/cmd/control.go b/cmd/control.go index b95379c..b0e0d92 100644 --- a/cmd/control.go +++ b/cmd/control.go @@ -1,7 +1,9 @@ package cmd import ( + "bytes" "context" + "encoding/json" "fmt" "github.com/spf13/cobra" "io" @@ -26,10 +28,33 @@ var pprofCmdArgs struct { seconds int64 } +var controlVersionCmd = &cobra.Command{ + Use: "version", + Short: "print version of running zrepl daemon", + Run: doControLVersionCmd, +} + func init() { RootCmd.AddCommand(controlCmd) controlCmd.AddCommand(pprofCmd) 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) { @@ -41,12 +66,6 @@ func doControlPProf(cmd *cobra.Command, args []string) { 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" { log.Printf("only CPU profiles are supported") log.Printf("%s", cmd.UsageString()) @@ -60,6 +79,7 @@ func doControlPProf(cmd *cobra.Command, args []string) { die() } var out io.Writer + var err error if outfn == "-" { out = os.Stdout } else { @@ -71,12 +91,10 @@ func doControlPProf(cmd *cobra.Command, args []string) { } log.Printf("connecting to daemon") - httpc := http.Client{ - Transport: &http.Transport{ - DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { - return net.Dial("unix", conf.Global.Control.Sockpath) - }, - }, + httpc, err := controlHttpClient() + if err != nil { + log.Printf("error parsing config: %s", err) + die() } log.Printf("profiling...") @@ -98,3 +116,40 @@ func doControlPProf(cmd *cobra.Command, args []string) { 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()) + +} diff --git a/cmd/daemon.go b/cmd/daemon.go index 572f53a..c91e689 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -37,6 +37,7 @@ func doDaemon(cmd *cobra.Command, args []string) { log := logger.NewLogger(conf.Global.logging.Outlets, 1*time.Second) + log.Info(NewZreplVersionInformation().String()) log.Debug("starting daemon") ctx := context.WithValue(context.Background(), contextKeyLog, log) ctx = context.WithValue(ctx, contextKeyLog, log) diff --git a/cmd/main.go b/cmd/main.go index 19719a0..4a5c626 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -20,6 +20,10 @@ import ( // Printf(format string, v ...interface{}) //} +var ( + zreplVersion string // set by build infrastructure +) + type Logger = *logger.Logger var RootCmd = &cobra.Command{ diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 0000000..4597d2d --- /dev/null +++ b/cmd/version.go @@ -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) +}