multinode/console: embed web assets

Embed web static files in multinode binary to be able to
release multinode as single binary.
Add make commands to build multinode binary with embeded web
assets.

Change-Id: I348aff7a7d847fae5c021cbf59abc7f892c0df80
This commit is contained in:
Yaroslav Vorobiov 2021-07-12 18:11:37 +03:00
parent 30cd7d3da3
commit 6db6b76b27
4 changed files with 62 additions and 10 deletions

View File

@ -157,6 +157,24 @@ storagenode-console:
/usr/bin/env echo -e '\nfunc init() { FileSystem = AssetFile() }' >> storagenode/console/consoleassets/bindata.resource.go /usr/bin/env echo -e '\nfunc init() { FileSystem = AssetFile() }' >> storagenode/console/consoleassets/bindata.resource.go
gofmt -w -s storagenode/console/consoleassets/bindata.resource.go gofmt -w -s storagenode/console/consoleassets/bindata.resource.go
.PHONY: multinode-console
multinode-console:
# build web assets
rm -rf web/multinode/dist
# install npm dependencies and build the binaries
docker run --rm -i \
--mount type=bind,src="${PWD}",dst=/go/src/storj.io/storj \
-w /go/src/storj.io/storj/web/multinode \
-e HOME=/tmp \
-u $(shell id -u):$(shell id -g) \
node:14.15.3 \
/bin/bash -c "npm ci && npm run build"
# embed web assets into go
go-bindata -prefix web/multinode/ -fs -o multinode/console/consoleassets/bindata.resource.go -pkg consoleassets web/multinode/dist/... web/multinode/static/...
# configure existing go code to know about the new assets
/usr/bin/env echo -e '\nfunc init() { FileSystem = AssetFile() }' >> multinode/console/consoleassets/bindata.resource.go
gofmt -w -s multinode/console/consoleassets/bindata.resource.go
.PHONY: satellite-wasm .PHONY: satellite-wasm
satellite-wasm: satellite-wasm:
docker run --rm -i -v "${PWD}":/go/src/storj.io/storj -e GO111MODULE=on \ docker run --rm -i -v "${PWD}":/go/src/storj.io/storj -e GO111MODULE=on \
@ -283,13 +301,16 @@ uplink_%:
.PHONY: versioncontrol_% .PHONY: versioncontrol_%
versioncontrol_%: versioncontrol_%:
$(MAKE) binary-check COMPONENT=versioncontrol GOARCH=$(word 3, $(subst _, ,$@)) GOOS=$(word 2, $(subst _, ,$@)) $(MAKE) binary-check COMPONENT=versioncontrol GOARCH=$(word 3, $(subst _, ,$@)) GOOS=$(word 2, $(subst _, ,$@))
.PHONY: multinode_%
multinode_%: multinode-console
$(MAKE) binary-check COMPONENT=multinode GOARCH=$(word 3, $(subst _, ,$@)) GOOS=$(word 2, $(subst _, ,$@))
COMPONENTLIST := certificates identity inspector satellite storagenode storagenode-updater uplink versioncontrol COMPONENTLIST := certificates identity inspector satellite storagenode storagenode-updater uplink versioncontrol multinode
OSARCHLIST := darwin_amd64 linux_amd64 linux_arm linux_arm64 windows_amd64 freebsd_amd64 OSARCHLIST := darwin_amd64 linux_amd64 linux_arm linux_arm64 windows_amd64 freebsd_amd64
BINARIES := $(foreach C,$(COMPONENTLIST),$(foreach O,$(OSARCHLIST),$C_$O)) BINARIES := $(foreach C,$(COMPONENTLIST),$(foreach O,$(OSARCHLIST),$C_$O))
.PHONY: binaries .PHONY: binaries
binaries: ${BINARIES} ## Build certificates, identity, inspector, satellite, storagenode, uplink, and versioncontrol binaries (jenkins) binaries: ${BINARIES} ## Build certificates, identity, inspector, satellite, storagenode, uplink, versioncontrol and multinode binaries (jenkins)
.PHONY: sign-windows-installer .PHONY: sign-windows-installer
sign-windows-installer: sign-windows-installer:

View File

@ -0,0 +1,13 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package consoleassets
import (
"net/http"
)
// FileSystem is nil by default, but when our Makefile generates
// embedded resources via go-bindata, it will also drop an init method
// that sets this to not nil.
var FileSystem http.FileSystem

View File

@ -6,9 +6,9 @@ package server
import ( import (
"context" "context"
"html/template" "html/template"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"path/filepath"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/zeebo/errs" "github.com/zeebo/errs"
@ -52,7 +52,7 @@ type Server struct {
log *zap.Logger log *zap.Logger
listener net.Listener listener net.Listener
http http.Server http http.Server
config Config assets http.FileSystem
nodes *nodes.Service nodes *nodes.Service
payouts *payouts.Service payouts *payouts.Service
@ -65,11 +65,11 @@ type Server struct {
} }
// NewServer returns new instance of Multinode Dashboard http server. // NewServer returns new instance of Multinode Dashboard http server.
func NewServer(log *zap.Logger, listener net.Listener, config Config, services Services) (*Server, error) { func NewServer(log *zap.Logger, listener net.Listener, assets http.FileSystem, services Services) (*Server, error) {
server := Server{ server := Server{
log: log, log: log,
listener: listener, listener: listener,
config: config, assets: assets,
nodes: services.Nodes, nodes: services.Nodes,
operators: services.Operators, operators: services.Operators,
payouts: services.Payouts, payouts: services.Payouts,
@ -79,7 +79,6 @@ func NewServer(log *zap.Logger, listener net.Listener, config Config, services S
} }
router := mux.NewRouter() router := mux.NewRouter()
fs := http.FileServer(http.Dir(server.config.StaticDir))
apiRouter := router.PathPrefix("/api/v0").Subrouter() apiRouter := router.PathPrefix("/api/v0").Subrouter()
apiRouter.NotFoundHandler = controllers.NewNotFound(server.log) apiRouter.NotFoundHandler = controllers.NewNotFound(server.log)
@ -133,7 +132,8 @@ func NewServer(log *zap.Logger, listener net.Listener, config Config, services S
reputationRouter := apiRouter.PathPrefix("/reputation").Subrouter() reputationRouter := apiRouter.PathPrefix("/reputation").Subrouter()
reputationRouter.HandleFunc("/satellites/{satelliteID}", reputationController.Stats) reputationRouter.HandleFunc("/satellites/{satelliteID}", reputationController.Stats)
if server.config.StaticDir != "" { if server.assets != nil {
fs := http.FileServer(server.assets)
router.PathPrefix("/static/").Handler(http.StripPrefix("/static", fs)) router.PathPrefix("/static/").Handler(http.StripPrefix("/static", fs))
router.PathPrefix("/").HandlerFunc(server.appHandler) router.PathPrefix("/").HandlerFunc(server.appHandler)
} }
@ -193,9 +193,19 @@ func (server *Server) Close() error {
// initializeTemplates is used to initialize all templates. // initializeTemplates is used to initialize all templates.
func (server *Server) initializeTemplates() (err error) { func (server *Server) initializeTemplates() (err error) {
server.index, err = template.ParseFiles(filepath.Join(server.config.StaticDir, "dist", "index.html")) f, err := server.assets.Open("/dist/index.html")
if err != nil { if err != nil {
server.log.Error("dist folder is not generated. use 'npm run build' command", zap.Error(err)) server.log.Error("dist folder is not generated. use 'npm run build' command", zap.Error(err))
return nil
}
b, err := ioutil.ReadAll(f)
if err != nil {
return Error.Wrap(err)
}
server.index, err = template.New("index.html").Parse(string(b))
if err != nil {
return Error.Wrap(err)
} }
return nil return nil

View File

@ -6,6 +6,7 @@ package multinode
import ( import (
"context" "context"
"net" "net"
"net/http"
"github.com/spacemonkeygo/monkit/v3" "github.com/spacemonkeygo/monkit/v3"
"go.uber.org/zap" "go.uber.org/zap"
@ -16,6 +17,7 @@ import (
"storj.io/common/rpc" "storj.io/common/rpc"
"storj.io/private/debug" "storj.io/private/debug"
"storj.io/storj/multinode/bandwidth" "storj.io/storj/multinode/bandwidth"
"storj.io/storj/multinode/console/consoleassets"
"storj.io/storj/multinode/console/server" "storj.io/storj/multinode/console/server"
"storj.io/storj/multinode/nodes" "storj.io/storj/multinode/nodes"
"storj.io/storj/multinode/operators" "storj.io/storj/multinode/operators"
@ -173,10 +175,16 @@ func New(log *zap.Logger, full *identity.FullIdentity, config Config, db DB) (_
return nil, err return nil, err
} }
assets := consoleassets.FileSystem
if config.Console.StaticDir != "" {
// a specific directory has been configured. use it
assets = http.Dir(config.Console.StaticDir)
}
peer.Console.Endpoint, err = server.NewServer( peer.Console.Endpoint, err = server.NewServer(
peer.Log.Named("console:endpoint"), peer.Log.Named("console:endpoint"),
peer.Console.Listener, peer.Console.Listener,
config.Console, assets,
server.Services{ server.Services{
Nodes: peer.Nodes.Service, Nodes: peer.Nodes.Service,
Payouts: peer.Payouts.Service, Payouts: peer.Payouts.Service,