Skip to content

Commit

Permalink
distro: support dynamic config (#1094)
Browse files Browse the repository at this point in the history
  • Loading branch information
baurine authored Dec 22, 2021
1 parent 1813ce9 commit 0807daf
Show file tree
Hide file tree
Showing 29 changed files with 306 additions and 134 deletions.
27 changes: 25 additions & 2 deletions cmd/tidb-dashboard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
_ "net/http/pprof" // #nosec
"os"
"os/signal"
"path"
"strings"
"sync"
"syscall"
Expand All @@ -31,13 +32,13 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"

_ "github.com/pingcap/tidb-dashboard/internal/resource/distrores"
"github.com/pingcap/tidb-dashboard/pkg/apiserver"
"github.com/pingcap/tidb-dashboard/pkg/config"
keyvisualregion "github.com/pingcap/tidb-dashboard/pkg/keyvisual/region"
"github.com/pingcap/tidb-dashboard/pkg/swaggerserver"
"github.com/pingcap/tidb-dashboard/pkg/uiserver"
"github.com/pingcap/tidb-dashboard/pkg/utils/version"
"github.com/pingcap/tidb-dashboard/util/distro"
)

type DashboardCLIConfig struct {
Expand Down Expand Up @@ -147,6 +148,26 @@ func buildTLSConfig(caPath, keyPath, certPath *string) *tls.Config {
return tlsConfig
}

const (
distroResFolderName string = "distro-res"
distroStringsResFileName string = "strings.json"
)

func loadDistroStringsRes() {
exePath, err := os.Executable()
if err != nil {
log.Fatal("Failed to get executable path", zap.Error(err))
}

distroStringsResPath := path.Join(path.Dir(exePath), distroResFolderName, distroStringsResFileName)
distroStringsRes, err := distro.ReadResourceStringsFromFile(distroStringsResPath)
if err != nil {
log.Fatal("Failed to load distro strings res", zap.String("path", distroStringsResPath), zap.Error(err))
}

distro.ReplaceGlobal(distroStringsRes)
}

func main() {
// Flushing any buffered log entries
defer log.Sync() //nolint:errcheck
Expand All @@ -164,10 +185,12 @@ func main() {
log.SetLevel(zapcore.DebugLevel)
}

loadDistroStringsRes()

listenAddr := fmt.Sprintf("%s:%d", cliConfig.ListenHost, cliConfig.ListenPort)
listener, err := net.Listen("tcp", listenAddr)
if err != nil {
log.Fatal("TiDB Dashboard server listen failed", zap.String("addr", listenAddr), zap.Error(err))
log.Fatal("Dashboard server listen failed", zap.String("addr", listenAddr), zap.Error(err))
}

var customKeyVisualProvider *keyvisualregion.DataProvider
Expand Down
11 changes: 0 additions & 11 deletions internal/resource/distrores/populate.go

This file was deleted.

11 changes: 0 additions & 11 deletions internal/resource/distrores/strings.go

This file was deleted.

12 changes: 11 additions & 1 deletion pkg/uiserver/embedded_assets_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,27 @@ package uiserver
import (
"net/http"
"os"
"path"
"sync"
"time"

"go.uber.org/zap"

"github.com/pingcap/log"
"github.com/pingcap/tidb-dashboard/pkg/config"
)

var once sync.Once

func Assets(cfg *config.Config) http.FileSystem {
once.Do(func() {
RewriteAssets(assets, cfg, func(fs http.FileSystem, f http.File, path, newContent string, bs []byte) {
exePath, err := os.Executable()
if err != nil {
log.Fatal("Failed to get executable path", zap.Error(err))
}

distroResFolderPath := path.Join(path.Dir(exePath), distroResFolderName)
RewriteAssets(assets, cfg, distroResFolderPath, func(fs http.FileSystem, f http.File, path, newContent string, bs []byte) {
m := fs.(vfsgen۰FS)
fi := f.(os.FileInfo)
m[path] = &vfsgen۰CompressedFileInfo{
Expand Down
76 changes: 75 additions & 1 deletion pkg/uiserver/uiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,32 @@ package uiserver
import (
"bytes"
"compress/gzip"
"encoding/base64"
"encoding/json"
"errors"
"html"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"strings"

"github.com/pingcap/log"
"github.com/shurcooL/httpgzip"
"go.uber.org/zap"

"github.com/pingcap/tidb-dashboard/pkg/config"
"github.com/pingcap/tidb-dashboard/util/distro"
)

const (
distroResFolderName = "distro-res"
)

type UpdateContentFunc func(fs http.FileSystem, oldFile http.File, path, newContent string, zippedBytes []byte)

func RewriteAssets(fs http.FileSystem, cfg *config.Config, updater UpdateContentFunc) {
func RewriteAssets(fs http.FileSystem, cfg *config.Config, distroResFolderPath string, updater UpdateContentFunc) {
if fs == nil {
return
}
Expand All @@ -39,6 +49,9 @@ func RewriteAssets(fs http.FileSystem, cfg *config.Config, updater UpdateContent
tmplText := string(bs)
updated := strings.ReplaceAll(tmplText, "__PUBLIC_PATH_PREFIX__", html.EscapeString(cfg.PublicPathPrefix))

distroStrings, _ := json.Marshal(distro.R()) // this will never fail
updated = strings.ReplaceAll(updated, "__DISTRO_STRINGS_RES__", base64.StdEncoding.EncodeToString(distroStrings))

var b bytes.Buffer
w := gzip.NewWriter(&b)
if _, err := w.Write([]byte(updated)); err != nil {
Expand All @@ -53,6 +66,67 @@ func RewriteAssets(fs http.FileSystem, cfg *config.Config, updater UpdateContent

rewrite("/index.html")
rewrite("/diagnoseReport.html")

if err := overrideDistroAssetsRes(fs, distroResFolderPath, updater); err != nil {
log.Fatal("Failed to load distro assets res", zap.Error(err))
}
}

func overrideDistroAssetsRes(fs http.FileSystem, distroResFolderPath string, updater UpdateContentFunc) error {
info, err := os.Stat(distroResFolderPath)
if errors.Is(err, os.ErrNotExist) || !info.IsDir() {
// just ignore if the folder doesn't exist or it's not a folder
return nil
}
if err != nil {
return err
}

// traverse
files, err := ioutil.ReadDir(distroResFolderPath)
if err != nil {
return err
}
for _, file := range files {
if err := overrideSingleDistroAsset(fs, distroResFolderPath, file.Name(), updater); err != nil {
return err
}
}
return nil
}

func overrideSingleDistroAsset(fs http.FileSystem, distroResFolderPath, assetName string, updater UpdateContentFunc) error {
assetPath := path.Join("/", distroResFolderName, assetName)
targetFile, err := fs.Open(assetPath)
if err != nil {
// has no target asset to be overried, skip
return nil
}
defer targetFile.Close()

assetFullPath := path.Join(distroResFolderPath, assetName)
sourceFile, err := os.Open(assetFullPath)
if err != nil {
return err
}
defer sourceFile.Close()

data, err := ioutil.ReadAll(sourceFile)
if err != nil {
return err
}

var b bytes.Buffer
w := gzip.NewWriter(&b)
if _, err := w.Write(data); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}

updater(fs, targetFile, assetPath, string(data), b.Bytes())
return nil
}

func Handler(root http.FileSystem) http.Handler {
Expand Down
28 changes: 0 additions & 28 deletions scripts/distro/replace_resources.sh

This file was deleted.

1 change: 0 additions & 1 deletion scripts/distro/write_strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/pingcap/log"
"go.uber.org/zap"

_ "github.com/pingcap/tidb-dashboard/internal/resource/distrores"
"github.com/pingcap/tidb-dashboard/util/distro"
)

Expand Down
18 changes: 5 additions & 13 deletions scripts/distro/write_strings.sh
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
#!/usr/bin/env bash

# This script writes distribution strings in json which is required for the frontend.
# This script outputs distribution strings to json which is required for the frontend.

set -euo pipefail

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
PROJECT_DIR=$(cd "$DIR/../.."; pwd)
BUILD_TAG=""

if [[ -f "${PROJECT_DIR}/internal/resource/distrores/strings.go" ]]; then
echo "+ Existing distribution resource is detected, using it to write strings"
BUILD_TAG=distro
fi

echo "+ Write resource strings"
echo "+ Write distro strings"
cd "$PROJECT_DIR"
# FIXME: distro/write_strings needs to access the /internal package, which is not allowed to be invoked in another module
# Currently we workaround this by invoking in the TiDB Dashboard module.
go run -tags="${BUILD_TAG}" scripts/distro/write_strings.go -o="${PROJECT_DIR}/ui/lib/distribution.json"
go run scripts/distro/write_strings.go -o="${PROJECT_DIR}/ui/lib/distro_strings.json"

echo " - Success! Resource strings:"
cat "${PROJECT_DIR}/ui/lib/distribution.json"
echo " - Success! Distro strings:"
cat "${PROJECT_DIR}/ui/lib/distro_strings.json"

echo
43 changes: 32 additions & 11 deletions ui/config-overrides.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path')
const fs = require('fs')
const fs = require('fs-extra')
const os = require('os')
const {
override,
Expand All @@ -17,16 +17,37 @@ const WebpackBar = require('webpackbar')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const rewireHtmlWebpackPlugin = require('react-app-rewire-html-webpack-plugin')

function copyDistroRes() {
const distroResPath = '../bin/distro-res'
if (fs.existsSync(distroResPath)) {
fs.copySync(distroResPath, './public/distro-res')
}
}

function injectDistroToHTML(config, env) {
const distroInfo = Object.entries(require('./lib/distribution.json')).reduce(
(prev, [k, v]) => {
return {
...prev,
[`distro_${k}`]: v,
}
let distroStringsResMeta = '__DISTRO_STRINGS_RES__'

// For dev mode,
// we copy distro assets from bin/distro-res to public/distro-res to override the default assets,
// read distro strings res from public/distro-res/strings.json and encode it by base64 if it exists.
// For production mode, we keep the "__DISTRO_STRINGS_RES__" value, it will be replaced by the backend RewriteAssets() method in the run time.
if (isBuildAsDevServer()) {
copyDistroRes()

const distroStringsResFilePath = './public/distro-res/strings.json'
if (fs.existsSync(distroStringsResFilePath)) {
const distroStringsRes = require(distroStringsResFilePath)
distroStringsResMeta = btoa(JSON.stringify(distroStringsRes))
}
}

// Store the distro strings res in the html head meta,
// HtmlWebpacPlugin will write this meta into the html head.
const distroInfo = {
meta: {
'x-distro-strings-res': distroStringsResMeta,
},
{}
)
}
return rewireHtmlWebpackPlugin(config, env, distroInfo)
}

Expand Down Expand Up @@ -183,10 +204,10 @@ module.exports = override(
)
),
disableMinimizeByEnv(),
addExtraEntries(),
supportDynamicPublicPathPrefix(),
overrideProcessEnv({
REACT_APP_RELEASE_VERSION: JSON.stringify(getInternalVersion()),
}),
injectDistroToHTML
injectDistroToHTML,
addExtraEntries()
)
3 changes: 3 additions & 0 deletions ui/dashboardApp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import AppRegistry from '@lib/utils/registry'
import * as routing from '@lib/utils/routing'
import * as auth from '@lib/utils/auth'
import * as i18n from '@lib/utils/i18n'
import { distro } from '@lib/utils/i18n'
import { saveAppOptions, loadAppOptions } from '@lib/utils/appOptions'
import {
initSentryRoutingInstrument,
Expand Down Expand Up @@ -150,6 +151,8 @@ async function webPageStart() {
}

async function main() {
document.title = `${distro.tidb} Dashboard`

if (routing.isPortalPage()) {
// the portal page is only used to receive options
function handlePortalEvent(event) {
Expand Down
Loading

0 comments on commit 0807daf

Please sign in to comment.