diff --git a/apis/swagger.yml b/apis/swagger.yml index db942ca2a..70ecea0f9 100644 --- a/apis/swagger.yml +++ b/apis/swagger.yml @@ -44,6 +44,20 @@ paths: 500: $ref: "#/responses/500ErrorResponse" + /version: + get: + summary: "Get version and build information" + description: | + Get version and build information, including GoVersion, OS, + Arch, Version, BuildDate, and GitCommit. + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/DragonflyVersion" + 500: + $ref: "#/responses/500ErrorResponse" + /peer/registry: post: summary: "registry a task" @@ -567,6 +581,30 @@ definitions: message: type: string + DragonflyVersion: + type: "object" + description: | + Version and build information of Dragonfly components. + properties: + Version: + type: "string" + description: "Version of Dragonfly components" + Revision: + type: "string" + description: "Git commit when building Dragonfly components" + BuildDate: + type: "string" + description: "Build Date of Dragonfly components" + GoVersion: + type: "string" + description: "Golang runtime version" + OS: + type: "string" + description: "Dragonfly components's operating system" + Arch: + type: "string" + description: "Dragonfly components's architecture target" + ResultInfo: type: "object" description: | diff --git a/apis/types/df_get_task.go b/apis/types/df_get_task.go index f0f6fdfe8..2348c1234 100644 --- a/apis/types/df_get_task.go +++ b/apis/types/df_get_task.go @@ -8,8 +8,9 @@ package types import ( "encoding/json" - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/dragonfly_version.go b/apis/types/dragonfly_version.go new file mode 100644 index 000000000..140d56a64 --- /dev/null +++ b/apis/types/dragonfly_version.go @@ -0,0 +1,59 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package types + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/swag" +) + +// DragonflyVersion Version and build information of Dragonfly components. +// +// swagger:model DragonflyVersion +type DragonflyVersion struct { + + // Dragonfly components's architecture target + Arch string `json:"Arch,omitempty"` + + // Build Date of Dragonfly components + BuildDate string `json:"BuildDate,omitempty"` + + // Golang runtime version + GoVersion string `json:"GoVersion,omitempty"` + + // Dragonfly components's operating system + OS string `json:"OS,omitempty"` + + // Git commit when building Dragonfly components + Revision string `json:"Revision,omitempty"` + + // Version of Dragonfly components + Version string `json:"Version,omitempty"` +} + +// Validate validates this dragonfly version +func (m *DragonflyVersion) Validate(formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *DragonflyVersion) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *DragonflyVersion) UnmarshalBinary(b []byte) error { + var res DragonflyVersion + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/apis/types/error.go b/apis/types/error.go index 59526bdb4..870807202 100644 --- a/apis/types/error.go +++ b/apis/types/error.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/apis/types/error_response.go b/apis/types/error_response.go index 1754282a3..a7e99e475 100644 --- a/apis/types/error_response.go +++ b/apis/types/error_response.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/apis/types/peer_create_request.go b/apis/types/peer_create_request.go index 08556b5bf..28d206e34 100644 --- a/apis/types/peer_create_request.go +++ b/apis/types/peer_create_request.go @@ -6,8 +6,9 @@ package types // Editing this file might prove futile when you re-run the swagger generate command import ( - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/peer_create_response.go b/apis/types/peer_create_response.go index ba57fe3b1..457311e66 100644 --- a/apis/types/peer_create_response.go +++ b/apis/types/peer_create_response.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/apis/types/peer_info.go b/apis/types/peer_info.go index 91015960a..38acb0930 100644 --- a/apis/types/peer_info.go +++ b/apis/types/peer_info.go @@ -6,8 +6,9 @@ package types // Editing this file might prove futile when you re-run the swagger generate command import ( - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/piece_info.go b/apis/types/piece_info.go index fad5fc990..0a8d60834 100644 --- a/apis/types/piece_info.go +++ b/apis/types/piece_info.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/apis/types/piece_pull_request.go b/apis/types/piece_pull_request.go index 70a368030..94879047d 100644 --- a/apis/types/piece_pull_request.go +++ b/apis/types/piece_pull_request.go @@ -8,8 +8,9 @@ package types import ( "encoding/json" - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/piece_update_request.go b/apis/types/piece_update_request.go index 7f34f8fcc..9620cf289 100644 --- a/apis/types/piece_update_request.go +++ b/apis/types/piece_update_request.go @@ -8,8 +8,9 @@ package types import ( "encoding/json" - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/preheat_create_request.go b/apis/types/preheat_create_request.go index 84d056a6d..eecee2c47 100644 --- a/apis/types/preheat_create_request.go +++ b/apis/types/preheat_create_request.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/apis/types/preheat_create_response.go b/apis/types/preheat_create_response.go index 5f5c0e468..0f57d7287 100644 --- a/apis/types/preheat_create_response.go +++ b/apis/types/preheat_create_response.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/apis/types/preheat_info.go b/apis/types/preheat_info.go index 61484631d..31664e307 100644 --- a/apis/types/preheat_info.go +++ b/apis/types/preheat_info.go @@ -8,8 +8,9 @@ package types import ( "encoding/json" - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/result_info.go b/apis/types/result_info.go index 660f62c08..ca5069e9d 100644 --- a/apis/types/result_info.go +++ b/apis/types/result_info.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/apis/types/task_create_request.go b/apis/types/task_create_request.go index c537e0561..a829efe8f 100644 --- a/apis/types/task_create_request.go +++ b/apis/types/task_create_request.go @@ -6,8 +6,9 @@ package types // Editing this file might prove futile when you re-run the swagger generate command import ( - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/task_create_response.go b/apis/types/task_create_response.go index c0804b452..2c8ebbb1c 100644 --- a/apis/types/task_create_response.go +++ b/apis/types/task_create_response.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/apis/types/task_info.go b/apis/types/task_info.go index 7b13ea4e8..719153c3e 100644 --- a/apis/types/task_info.go +++ b/apis/types/task_info.go @@ -8,8 +8,9 @@ package types import ( "encoding/json" - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/task_register_request.go b/apis/types/task_register_request.go index 19a78abfb..382973032 100644 --- a/apis/types/task_register_request.go +++ b/apis/types/task_register_request.go @@ -6,8 +6,9 @@ package types // Editing this file might prove futile when you re-run the swagger generate command import ( - "github.com/go-openapi/errors" strfmt "github.com/go-openapi/strfmt" + + "github.com/go-openapi/errors" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) diff --git a/apis/types/task_update_request.go b/apis/types/task_update_request.go index 6ccc424c0..da662bb9b 100644 --- a/apis/types/task_update_request.go +++ b/apis/types/task_update_request.go @@ -7,6 +7,7 @@ package types import ( strfmt "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) diff --git a/cmd/dfdaemon/app/root.go b/cmd/dfdaemon/app/root.go index fca7edd8d..bf450ff6c 100644 --- a/cmd/dfdaemon/app/root.go +++ b/cmd/dfdaemon/app/root.go @@ -18,7 +18,6 @@ package app import ( "encoding/json" - "fmt" "os" "os/exec" "path/filepath" @@ -29,14 +28,13 @@ import ( "github.com/dragonflyoss/Dragonfly/dfdaemon" "github.com/dragonflyoss/Dragonfly/dfdaemon/config" "github.com/dragonflyoss/Dragonfly/dfdaemon/constant" - "github.com/dragonflyoss/Dragonfly/version" + "github.com/mitchellh/mapstructure" "github.com/pkg/errors" - "gopkg.in/yaml.v2" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" + "gopkg.in/yaml.v2" ) var rootCmd = &cobra.Command{ @@ -45,11 +43,6 @@ var rootCmd = &cobra.Command{ Long: "The dfdaemon is a proxy between container engine and registry used for pulling images.", DisableAutoGenTag: true, // disable displaying auto generation tag in cli docs RunE: func(cmd *cobra.Command, args []string) error { - if viper.GetBool("version") { - fmt.Println("dfdaemon version:", version.DFDaemonVersion) - return nil - } - if err := readConfigFile(viper.GetViper(), cmd); err != nil { return errors.Wrap(err, "read config file") } @@ -83,7 +76,6 @@ func init() { rf := rootCmd.Flags() - rf.BoolP("version", "v", false, "version") rf.String("config", constant.DefaultConfigPath, "the path of dfdaemon's configuration file") rf.Bool("verbose", false, "verbose") diff --git a/cmd/dfdaemon/app/version.go b/cmd/dfdaemon/app/version.go new file mode 100644 index 000000000..ebb44902d --- /dev/null +++ b/cmd/dfdaemon/app/version.go @@ -0,0 +1,40 @@ +package app + +import ( + "fmt" + + "github.com/dragonflyoss/Dragonfly/version" + + "github.com/spf13/cobra" +) + +// versionDescription is used to describe version command in detail and auto generate command doc. +var versionDescription = "Display the version and build information of Dragonfly dfdaemon, " + + "including GoVersion, OS, Arch, Version, BuildDate and GitCommit." + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Show the current version of dfdaemon", + Long: versionDescription, + SilenceErrors: true, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println(version.Print("dfdaemon")) + return nil + }, + Example: versionExample(), +} + +func init() { + rootCmd.AddCommand(versionCmd) +} + +// versionExample shows examples in version command, and is used in auto-generated cli docs. +func versionExample() string { + return `dfdaemon, version 0.4.1 + Git commit: 6fd5c8f + Build date: 20190717-15:57:52 + Go version: go1.12.6 + OS/Arch: linux/amd64 +` +} diff --git a/cmd/dfget/app/version.go b/cmd/dfget/app/version.go index 1612e03b7..b00a0d09d 100644 --- a/cmd/dfget/app/version.go +++ b/cmd/dfget/app/version.go @@ -24,17 +24,33 @@ import ( "github.com/spf13/cobra" ) +// versionDescription is used to describe version command in detail and auto generate command doc. +var versionDescription = "Display the version and build information of Dragonfly dfget, " + + "including GoVersion, OS, Arch, Version, BuildDate and GitCommit." + var versionCmd = &cobra.Command{ Use: "version", - Short: "Show the current version", + Short: "Show the current version of dfget", + Long: versionDescription, SilenceErrors: true, SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - fmt.Printf("%s\n", version.DFGetVersion) + fmt.Println(version.Print("dfget")) return nil }, + Example: versionExample(), } func init() { rootCmd.AddCommand(versionCmd) } + +// versionExample shows examples in version command, and is used in auto-generated cli docs. +func versionExample() string { + return `dfget, version 0.4.1 + Git commit: 6fd5c8f + Build date: 20190717-15:57:52 + Go version: go1.12.6 + OS/Arch: linux/amd64 +` +} diff --git a/cmd/supernode/app/version.go b/cmd/supernode/app/version.go new file mode 100644 index 000000000..a83ab0e99 --- /dev/null +++ b/cmd/supernode/app/version.go @@ -0,0 +1,40 @@ +package app + +import ( + "fmt" + + "github.com/dragonflyoss/Dragonfly/version" + + "github.com/spf13/cobra" +) + +// versionDescription is used to describe version command in detail and auto generate command doc. +var versionDescription = "Display the version and build information of Dragonfly supernodeļ¼Œ " + + "including GoVersion, OS, Arch, Version, BuildDate and GitCommit." + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Show the current version of supernode", + Long: versionDescription, + SilenceErrors: true, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println(version.Print("supernode")) + return nil + }, + Example: versionExample(), +} + +func init() { + rootCmd.AddCommand(versionCmd) +} + +// versionExample shows examples in version command, and is used in auto-generated cli docs. +func versionExample() string { + return `supernode, version 0.4.1 + Git commit: 6fd5c8f + Build date: 20190717-15:57:52 + Go version: go1.12.6 + OS/Arch: linux/amd64 +` +} diff --git a/dfdaemon/handler/root_handler.go b/dfdaemon/handler/root_handler.go index e2d047bf2..5c725eea1 100644 --- a/dfdaemon/handler/root_handler.go +++ b/dfdaemon/handler/root_handler.go @@ -20,6 +20,8 @@ import ( "net/http" // pprof will inject handlers for users to profile this program _ "net/http/pprof" + + "github.com/dragonflyoss/Dragonfly/version" ) // New returns a new http mux for dfdaemon @@ -27,6 +29,6 @@ func New() *http.ServeMux { s := http.DefaultServeMux s.HandleFunc("/args", getArgs) s.HandleFunc("/env", getEnv) - s.HandleFunc("/debug/version", getVersion) + s.HandleFunc("/debug/version", version.Handler) return s } diff --git a/dfdaemon/handler/version_handler.go b/dfdaemon/handler/version_handler.go deleted file mode 100644 index 2c97bc554..000000000 --- a/dfdaemon/handler/version_handler.go +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright The Dragonfly Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package handler - -import ( - "net/http" - - "github.com/dragonflyoss/Dragonfly/version" -) - -func getVersion(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "text/plain;charset=utf-8") - w.Write([]byte(version.DFDaemonVersion)) -} diff --git a/hack/build.sh b/hack/build.sh index 7646f54f3..16a2e1a7d 100755 --- a/hack/build.sh +++ b/hack/build.sh @@ -4,8 +4,10 @@ DFGET_BINARY_NAME=dfget SUPERNODE_BINARY_NAME=supernode PKG=github.com/dragonflyoss/Dragonfly BUILD_IMAGE=golang:1.12.6 -VERSION=$(git describe --tags | cut -c 2-) -LDFLAGS="-X ${PKG}/version.Version=${VERSION}" +VERSION=$(git describe --tags "$(git rev-list --tags --max-count=1)") +REVISION=$(git rev-parse --short HEAD) +DATE=$(date "+%Y%m%d-%H:%M:%S") +LDFLAGS="-X ${PKG}/version.version=${VERSION:1} -X ${PKG}/version.revision=${REVISION} -X ${PKG}/version.buildDate=${DATE}" curDir=$(cd "$(dirname "$0")" && pwd) cd "${curDir}" || return diff --git a/supernode/server/router.go b/supernode/server/router.go index 7a6ea94dd..2f65e3267 100644 --- a/supernode/server/router.go +++ b/supernode/server/router.go @@ -7,6 +7,7 @@ import ( "net/http/pprof" "github.com/dragonflyoss/Dragonfly/apis/types" + "github.com/dragonflyoss/Dragonfly/version" "github.com/gorilla/mux" ) @@ -20,6 +21,7 @@ func initRoute(s *Server) *mux.Router { handlers := []*HandlerSpec{ // system {Method: http.MethodGet, Path: "/_ping", HandlerFunc: s.ping}, + {Method: http.MethodGet, Path: "/version", HandlerFunc: version.HandlerWithCtx}, // v0.3 {Method: http.MethodPost, Path: "/peer/registry", HandlerFunc: s.registry}, diff --git a/supernode/server/router_test.go b/supernode/server/router_test.go index c3a5efc05..a364ae452 100644 --- a/supernode/server/router_test.go +++ b/supernode/server/router_test.go @@ -1,16 +1,20 @@ package server import ( + "encoding/json" "io/ioutil" "math/rand" "net" "net/http" + "runtime" "strconv" "testing" "time" + "github.com/dragonflyoss/Dragonfly/apis/types" cutil "github.com/dragonflyoss/Dragonfly/common/util" "github.com/dragonflyoss/Dragonfly/supernode/config" + "github.com/dragonflyoss/Dragonfly/version" "github.com/go-check/check" ) @@ -46,6 +50,13 @@ func (rs *RouterTestSuite) SetUpSuite(c *check.C) { } s, err := New(testConf) c.Check(err, check.IsNil) + version.DFVersion = &types.DragonflyVersion{ + Version: "test", + Revision: "test", + Arch: runtime.GOARCH, + OS: runtime.GOOS, + GoVersion: runtime.Version(), + } router := initRoute(s) rs.listener, err = net.Listen("tcp", rs.addr) c.Check(err, check.IsNil) @@ -81,3 +92,20 @@ func (rs *RouterTestSuite) TestDebugHandler(c *check.C) { c.Assert(code, check.Equals, tc.code) } } + +func (rs *RouterTestSuite) TestVersionHandler(c *check.C) { + code, res, err := cutil.Get("http://"+rs.addr+"/version", 0) + c.Check(err, check.IsNil) + c.Assert(code, check.Equals, 200) + + expectDFVersion, err := json.Marshal(&types.DragonflyVersion{ + Version: "test", + Revision: "test", + Arch: runtime.GOARCH, + OS: runtime.GOOS, + GoVersion: runtime.Version(), + }) + + c.Check(err, check.IsNil) + c.Check(string(expectDFVersion), check.Equals, string(res)) +} diff --git a/version/version.go b/version/version.go index 0009d6313..580d366b6 100644 --- a/version/version.go +++ b/version/version.go @@ -17,14 +17,106 @@ // Package version represents the version the project Dragonfly. package version +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "runtime" + "strings" + "text/template" + + "github.com/dragonflyoss/Dragonfly/apis/types" +) + var ( - // Version is the version of the project Dragonfly + // version is the version of project Dragonfly + // populate via ldflags + version string + + // revision is the current git commit revision // populate via ldflags - Version string + revision string + + // buildDate is the build date of project Dragonfly + // populate via ldflags + buildDate string + + // goVersion is the running program's golang version + goVersion = runtime.Version() + + // os is the running program's operating system + os = runtime.GOOS + + // arch is the running program's architecture target + arch = runtime.GOARCH // DFDaemonVersion is the version of dfdaemon - DFDaemonVersion = Version + DFDaemonVersion = version // DFGetVersion is the version of dfget - DFGetVersion = Version + DFGetVersion = version + + // SupernodeVersion is the version of supernode + SupernodeVersion = version + + // DFVersion the global instance of DragonflyVersion + DFVersion *types.DragonflyVersion ) + +func init() { + DFVersion = &types.DragonflyVersion{ + BuildDate: buildDate, + Arch: arch, + OS: os, + GoVersion: goVersion, + Version: version, + } +} + +// versionInfoTmpl contains the template used by Info. +var versionInfoTmpl = ` +{{.program}}, version {{.version}} + Git commit: {{.revision}} + Build date: {{.buildDate}} + Go version: {{.goVersion}} + OS/Arch: {{.OS}}/{{.Arch}} +` + +// Print returns version information. +func Print(program string) string { + m := map[string]string{ + "program": program, + "version": version, + "revision": revision, + "buildDate": buildDate, + "goVersion": goVersion, + "OS": os, + "Arch": arch, + } + t := template.Must(template.New("version").Parse(versionInfoTmpl)) + + var buf bytes.Buffer + if err := t.ExecuteTemplate(&buf, "version", m); err != nil { + panic(err) + } + return strings.TrimSpace(buf.String()) +} + +// Handler returns build information +func Handler(w http.ResponseWriter, r *http.Request) { + data, err := json.Marshal(DFVersion) + if err != nil { + http.Error(w, fmt.Sprintf("error encoding JSON: %s", err), http.StatusInternalServerError) + } else { + w.WriteHeader(http.StatusOK) + w.Write(data) + } +} + +// HandlerWithCtx returns build information +func HandlerWithCtx(context context.Context, w http.ResponseWriter, r *http.Request) (err error) { + Handler(w, r) + return +}