[MM-31085] Create a db init command to initialize the database (#16489)
Automatic Merge
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
6a3bc50f13
Коммит
e057e5b10b
72
cmd/mattermost/commands/db.go
Обычный файл
72
cmd/mattermost/commands/db.go
Обычный файл
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/config"
|
||||
"github.com/mattermost/mattermost-server/v5/store/sqlstore"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var DbCmd = &cobra.Command{
|
||||
Use: "db",
|
||||
Short: "Commands related to the database",
|
||||
}
|
||||
|
||||
var InitDbCmd = &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Initialize the database",
|
||||
Long: `Initialize the database for a given DSN, executing the migrations and loading the custom defaults if any.
|
||||
|
||||
This command should be run using a database configuration DSN.`,
|
||||
Example: ` # you can use the config flag to pass the DSN
|
||||
$ mattermost db init --config postgres://localhost/mattermost
|
||||
|
||||
# or you can use the MM_CONFIG environment variable
|
||||
$ MM_CONFIG=postgres://localhost/mattermost mattermost db init
|
||||
|
||||
# and you can set a custom defaults file to be loaded into the database
|
||||
$ MM_CUSTOM_DEFAULTS_PATH=custom.json MM_CONFIG=postgres://localhost/mattermost mattermost db init`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: initDbCmdF,
|
||||
}
|
||||
|
||||
func init() {
|
||||
DbCmd.AddCommand(
|
||||
InitDbCmd,
|
||||
)
|
||||
|
||||
RootCmd.AddCommand(
|
||||
DbCmd,
|
||||
)
|
||||
}
|
||||
|
||||
func initDbCmdF(command *cobra.Command, _ []string) error {
|
||||
dsn := getConfigDSN(command, config.GetEnvironment())
|
||||
if !config.IsDatabaseDSN(dsn) {
|
||||
return errors.New("this command should be run using a database configuration DSN")
|
||||
}
|
||||
|
||||
customDefaults, err := loadCustomDefaults()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error loading custom configuration defaults")
|
||||
}
|
||||
|
||||
configStore, err := config.NewStore(getConfigDSN(command, config.GetEnvironment()), false, customDefaults)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load configuration")
|
||||
}
|
||||
defer configStore.Close()
|
||||
|
||||
sqlStore := sqlstore.New(configStore.Get().SqlSettings, nil)
|
||||
defer sqlStore.Close()
|
||||
|
||||
fmt.Println("Database store correctly initialised")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/config"
|
||||
"github.com/mattermost/mattermost-server/v5/manualtesting"
|
||||
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
"github.com/mattermost/mattermost-server/v5/web"
|
||||
"github.com/mattermost/mattermost-server/v5/wsapi"
|
||||
@@ -24,8 +22,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const CUSTOM_DEFAULTS_ENV_VAR = "MM_CUSTOM_DEFAULTS_PATH"
|
||||
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Run the Mattermost server",
|
||||
@@ -38,27 +34,6 @@ func init() {
|
||||
RootCmd.RunE = serverCmdF
|
||||
}
|
||||
|
||||
func loadCustomDefaults() (*model.Config, error) {
|
||||
customDefaultsPath := os.Getenv(CUSTOM_DEFAULTS_ENV_VAR)
|
||||
if customDefaultsPath == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
file, err := os.Open(customDefaultsPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to open custom defaults file at %q", customDefaultsPath)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var customDefaults *model.Config
|
||||
err = json.NewDecoder(file).Decode(&customDefaults)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to decode custom defaults configuration")
|
||||
}
|
||||
|
||||
return customDefaults, nil
|
||||
}
|
||||
|
||||
func serverCmdF(command *cobra.Command, args []string) error {
|
||||
disableConfigWatch, _ := command.Flags().GetBool("disableconfigwatch")
|
||||
usedPlatform, _ := command.Flags().GetBool("platform")
|
||||
|
||||
@@ -5,15 +5,20 @@ package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const CUSTOM_DEFAULTS_ENV_VAR = "MM_CUSTOM_DEFAULTS_PATH"
|
||||
|
||||
// prettyPrintStruct will return a prettyPrint version of a given struct
|
||||
func prettyPrintStruct(t interface{}) string {
|
||||
return prettyPrintMap(structToMap(t))
|
||||
@@ -116,3 +121,24 @@ func getConfigDSN(command *cobra.Command, env map[string]string) string {
|
||||
|
||||
return configDSN
|
||||
}
|
||||
|
||||
func loadCustomDefaults() (*model.Config, error) {
|
||||
customDefaultsPath := os.Getenv(CUSTOM_DEFAULTS_ENV_VAR)
|
||||
if customDefaultsPath == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
file, err := os.Open(customDefaultsPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open custom defaults file at %q: %w", customDefaultsPath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var customDefaults *model.Config
|
||||
err = json.NewDecoder(file).Decode(&customDefaults)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode custom defaults configuration: %w", err)
|
||||
}
|
||||
|
||||
return customDefaults, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package config
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
@@ -83,7 +82,7 @@ func NewStoreFromBacking(backingStore BackingStore, customDefaults *model.Config
|
||||
}
|
||||
|
||||
func getBackingStore(dsn string, watch bool) (BackingStore, error) {
|
||||
if strings.HasPrefix(dsn, "mysql://") || strings.HasPrefix(dsn, "postgres://") {
|
||||
if IsDatabaseDSN(dsn) {
|
||||
return NewDatabaseStore(dsn)
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +173,10 @@ func Merge(cfg *model.Config, patch *model.Config, mergeConfig *utils.MergeConfi
|
||||
return &retCfg, nil
|
||||
}
|
||||
|
||||
func IsDatabaseDSN(dsn string) bool {
|
||||
return strings.HasPrefix(dsn, "mysql://") || strings.HasPrefix(dsn, "postgres://")
|
||||
}
|
||||
|
||||
// stripPassword remove the password from a given DSN
|
||||
func stripPassword(dsn, schema string) string {
|
||||
prefix := schema + "://"
|
||||
|
||||
@@ -145,6 +145,51 @@ func TestFixInvalidLocales(t *testing.T) {
|
||||
assert.Contains(t, *cfg.LocalizationSettings.AvailableLocales, *cfg.LocalizationSettings.DefaultClientLocale, "DefaultClientLocale should have been added to AvailableLocales")
|
||||
}
|
||||
|
||||
func TestIsDatabaseDSN(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
DSN string
|
||||
Expected bool
|
||||
}{
|
||||
{
|
||||
Name: "Mysql DSN",
|
||||
DSN: "mysql://localhost",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Name: "Mysql DSN",
|
||||
DSN: "mysql://localhost",
|
||||
Expected: true,
|
||||
},
|
||||
{
|
||||
Name: "Empty DSN",
|
||||
DSN: "",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Name: "Default file DSN",
|
||||
DSN: "config.json",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Name: "Relative path DSN",
|
||||
DSN: "configuration/config.json",
|
||||
Expected: false,
|
||||
},
|
||||
{
|
||||
Name: "Absolute path DSN",
|
||||
DSN: "/opt/mattermost/configuration/config.json",
|
||||
Expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.Expected, IsDatabaseDSN(tc.DSN))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripPassword(t *testing.T) {
|
||||
for name, test := range map[string]struct {
|
||||
DSN string
|
||||
|
||||
Ссылка в новой задаче
Block a user