Skip to content

Commit

Permalink
modules: tt.yaml modules/directory support list
Browse files Browse the repository at this point in the history
The logic of working with configuration file tt.yaml has been updated,
now  modules/directory can be specified both as a single line and
as a list.

Closes #1012
  • Loading branch information
dmyger committed Nov 26, 2024
1 parent ebd3470 commit f083b8d
Show file tree
Hide file tree
Showing 18 changed files with 171 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ operations ending with the given timestamp. This value can be specified
as a number or using [RFC3339/RFC3339Nano](https://go.dev/src/time/format.go) time format.
- `tt connect`: add new `--evaler` option to support for customizing
the way user input is processed.
- `tt.yaml` allow specify a list of modules directories.

### Changed

Expand Down
9 changes: 6 additions & 3 deletions cli/cfg/dump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ env:
restart_on_failure: false
tarantoolctl_layout: false
modules:
directory: /root/modules
directory:
- /root/modules
app:
run_dir: var/run
log_dir: var/log
Expand Down Expand Up @@ -129,7 +130,8 @@ env:
restart_on_failure: false
tarantoolctl_layout: false
modules:
directory: %[1]s/my_modules
directory:
- %[1]s/my_modules
app:
run_dir: var/run
log_dir: var/log
Expand Down Expand Up @@ -166,7 +168,8 @@ env:
restart_on_failure: false
tarantoolctl_layout: false
modules:
directory: /root/modules
directory:
- /root/modules
app:
run_dir: var/run
log_dir: var/log
Expand Down
8 changes: 6 additions & 2 deletions cli/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ package config
// ee:
// credential_path: path

// FieldStringArrayType a custom type used with mapstructure's hook to accept values
// as a single string as well as a list of strings.
type FieldStringArrayType []string

// ModuleOpts is used to store all module options.
type ModulesOpts struct {
// Directory is a path to directory where the external modules
// Directories is a list of paths to directories where the external modules
// are stored.
Directory string
Directories FieldStringArrayType `mapstructure:"directory" yaml:"directory"`
}

// EEOpts is used to store tarantool-ee options.
Expand Down
49 changes: 44 additions & 5 deletions cli/configure/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"os/exec"
"path/filepath"
"reflect"
"strings"
"syscall"

Expand Down Expand Up @@ -103,7 +104,7 @@ func getSystemAppOpts() *config.AppOpts {
// GetDefaultCliOpts returns `CliOpts` filled with default values.
func GetSystemCliOpts() *config.CliOpts {
modules := config.ModulesOpts{
Directory: ModulesPath,
Directories: []string{ModulesPath},
}
ee := config.EEOpts{
CredPath: "",
Expand All @@ -128,7 +129,7 @@ func GetSystemCliOpts() *config.CliOpts {
// GetDefaultCliOpts returns `CliOpts` filled with default values.
func GetDefaultCliOpts() *config.CliOpts {
modules := config.ModulesOpts{
Directory: ModulesPath,
Directories: []string{ModulesPath},
}
ee := config.EEOpts{
CredPath: "",
Expand Down Expand Up @@ -179,6 +180,24 @@ func adjustPathWithConfigLocation(filePath string, configDir string,
return filepath.Abs(filepath.Join(configDir, filePath))
}

func adjustListPathWithConfigLocation(listPaths []string, configDir string,
defaultDirName string) ([]string, error) {
if len(listPaths) == 0 {
listPaths = append(listPaths, defaultDirName)
}

result := make([]string, 0, len(listPaths))
for _, path := range listPaths {
path, err := adjustPathWithConfigLocation(path, configDir, defaultDirName)
if err != nil {
return result, err
}
result = append(result, path)

}
return result, nil
}

// resolveConfigPaths resolves all paths in config relative to specified location, and
// sets uninitialized values to defaults.
func updateCliOpts(cliOpts *config.CliOpts, configDir string) error {
Expand Down Expand Up @@ -211,8 +230,8 @@ func updateCliOpts(cliOpts *config.CliOpts, configDir string) error {
}

if cliOpts.Modules != nil {
if cliOpts.Modules.Directory, err = adjustPathWithConfigLocation(cliOpts.Modules.Directory,
configDir, ModulesPath); err != nil {
if cliOpts.Modules.Directories, err = adjustListPathWithConfigLocation(
cliOpts.Modules.Directories, configDir, ModulesPath); err != nil {
return err
}
}
Expand All @@ -227,6 +246,26 @@ func updateCliOpts(cliOpts *config.CliOpts, configDir string) error {
return nil
}

func decodeStringAsArrayField(from reflect.Type, to reflect.Type, value interface{}) (
interface{}, error) {
if to != reflect.TypeOf(config.FieldStringArrayType{}) || from.Kind() != reflect.String {
return value, nil
}
return []string{value.(string)}, nil
}

func decodeConfig(input map[string]any, cfg *config.CliOpts) error {
decoder_config := mapstructure.DecoderConfig{
Result: cfg,
DecodeHook: mapstructure.ComposeDecodeHookFunc(decodeStringAsArrayField),
}
decoder, err := mapstructure.NewDecoder(&decoder_config)
if err != nil {
return err
}
return decoder.Decode(input)
}

// GetCliOpts returns Tarantool CLI options from the config file
// located at path configurePath.
func GetCliOpts(configurePath string, repository integrity.Repository) (
Expand All @@ -250,7 +289,7 @@ func GetCliOpts(configurePath string, repository integrity.Repository) (
return nil, "", fmt.Errorf("failed to parse Tarantool CLI configuration: %s", err)
}

if err := mapstructure.Decode(rawConfigOpts, &cfg); err != nil {
if err := decodeConfig(rawConfigOpts, cfg); err != nil {
return nil, "", fmt.Errorf("failed to parse Tarantool CLI configuration: %s", err)
}

Expand Down
63 changes: 62 additions & 1 deletion cli/configure/configure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,67 @@ func TestUpdateCliOpts(t *testing.T) {
assert.Equal(t, "./var/lib/vinyl", cliOpts.App.VinylDir)
assert.Equal(t, "./var/lib/snap", cliOpts.App.MemtxDir)
assert.Equal(t, filepath.Join(configDir, "..", "include_dir"), cliOpts.Env.IncludeDir)
assert.Equal(t, filepath.Join(configDir, ModulesPath), cliOpts.Modules.Directory)
assert.Equal(t, 1, len(cliOpts.Modules.Directories))
assert.Equal(t, filepath.Join(configDir, ModulesPath), cliOpts.Modules.Directories[0])
assert.Equal(t, configDir, cliOpts.Env.InstancesEnabled)
}

func TestGetCliOpts_modules_directory(t *testing.T) {
work_dir, err := os.Getwd()
require.NoError(t, err)
work_dir = filepath.Join(work_dir, "testdata/modules_cfg")

tests := []struct {
name string
config string
modules_dir config.FieldStringArrayType
cfg_path string
}{
{
name: "Single string relative path",
config: "tt-modules1",
modules_dir: []string{filepath.Join(work_dir, "modules-dir")},
cfg_path: "tt-modules1.yaml",
},
{
name: "Single entry list",
config: "tt-modules2",
modules_dir: []string{filepath.Join(work_dir, "modules-dir")},
cfg_path: "tt-modules2.yml",
},
{
name: "Multiple entries list",
config: "tt-modules3.",
modules_dir: []string{
filepath.Join(work_dir, "modules-dir"),
"/ext/path/modules",
filepath.Join(work_dir, "local_modules"),
},
cfg_path: "tt-modules3.yaml",
},
{
name: "Empty list = default value",
config: "tt-modules4.yaml",
modules_dir: []string{filepath.Join(work_dir, "modules")},
cfg_path: "tt-modules4.yml",
},
{
name: "Single string absolute path",
config: "tt-modules5.yml",
modules_dir: []string{"/ext/path/modules"},
cfg_path: "tt-modules5.yaml",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockRepo := newMockRepository()
config := filepath.Join(work_dir, tt.config)
opts, cfg, err := GetCliOpts(config, &mockRepo)
require.NoError(t, err)
require.NotNil(t, opts.Modules)
require.Equal(t, tt.modules_dir, opts.Modules.Directories)
require.Equal(t, filepath.Join(work_dir, tt.cfg_path), cfg)
})
}
}
3 changes: 3 additions & 0 deletions cli/configure/testdata/modules_cfg/tt-modules1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Config with single string with relative path to modules directory.
modules:
directory: modules-dir
4 changes: 4 additions & 0 deletions cli/configure/testdata/modules_cfg/tt-modules2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Config with single list entry of relative path to modules directory.
modules:
directory:
- modules-dir
6 changes: 6 additions & 0 deletions cli/configure/testdata/modules_cfg/tt-modules3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Multiple directory items.
modules:
directory:
- modules-dir
- /ext/path/modules
- local_modules
3 changes: 3 additions & 0 deletions cli/configure/testdata/modules_cfg/tt-modules4.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Empty config value, to use default.
modules:
directory:
3 changes: 3 additions & 0 deletions cli/configure/testdata/modules_cfg/tt-modules5.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Config with single string with absolute path to modules directory.
modules:
directory: /ext/path/modules
3 changes: 2 additions & 1 deletion cli/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,12 @@ func generateTtEnv(configPath string, sourceCfg configData) error {

directoriesToCreate := []string{
cfg.Env.InstancesEnabled,
cfg.Modules.Directory,
cfg.Env.IncludeDir,
cfg.Env.BinDir,
cfg.Repo.Install,
}
// FIXME: Need select only internal directories https://github.com/tarantool/tt/issues/1014
directoriesToCreate = append(directoriesToCreate, cfg.Modules.Directories...)
for _, templatesPathOpts := range cfg.Templates {
directoriesToCreate = append(directoriesToCreate, templatesPathOpts.Path)
}
Expand Down
2 changes: 1 addition & 1 deletion cli/init/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func checkDefaultEnv(t *testing.T, configName string, instancesEnabled string) {
assert.Equal(t, "var/run", cfg.App.RunDir)
assert.Equal(t, "var/log", cfg.App.LogDir)
assert.Equal(t, "bin", cfg.Env.BinDir)
assert.Equal(t, "modules", cfg.Modules.Directory)
assert.Equal(t, config.FieldStringArrayType{"modules"}, cfg.Modules.Directories)
assert.Equal(t, "distfiles", cfg.Repo.Install)
assert.Equal(t, "include", cfg.Env.IncludeDir)
assert.Equal(t, "templates", cfg.Templates[0].Path)
Expand Down
2 changes: 1 addition & 1 deletion cli/init/templates/tt.yaml.default
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
modules:
# Directory where the external modules are stored.
directory: {{ .Modules.Directory }}
directory: {{ .Modules.Directories }}

env:
# Restart instance on failure.
Expand Down
3 changes: 2 additions & 1 deletion cli/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ func getExternalModulesDir(cmdCtx *cmdcontext.CmdCtx, cliOpts *config.CliOpts) (
// 1. If a directory field is specified;
// 2. Specified path exists;
// 3. Path points to not a directory.
modulesDir := cliOpts.Modules.Directory
// FIXME: Add working with a list https://github.com/tarantool/tt/issues/1014
modulesDir := cliOpts.Modules.Directories[0]
if info, err := os.Stat(modulesDir); err == nil {
// TODO: Add warning in next patches, discussion
// what if the file exists, but access is denied, etc.
Expand Down
35 changes: 19 additions & 16 deletions cli/pack/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func ttEnvironmentFilters(packCtx *PackCtx, cliOpts *config.CliOpts) []func(
cliOpts.Env.InstancesEnabled, cliOpts.Env.BinDir)
}
if cliOpts.Modules != nil {
envPaths = append(envPaths, cliOpts.Modules.Directory)
envPaths = append(envPaths, cliOpts.Modules.Directories...)
}
if cliOpts.Repo != nil {
envPaths = append(envPaths, cliOpts.Repo.Install)
Expand Down Expand Up @@ -204,24 +204,27 @@ func updateEnvPath(basePath string, packCtx *PackCtx, cliOpts *config.CliOpts) (
// copyEnvModules copies tt modules.
func copyEnvModules(bundleEnvPath string, packCtx *PackCtx, cliOpts, newOpts *config.CliOpts) {
if packCtx.WithoutModules || packCtx.CartridgeCompat || cliOpts.Modules == nil ||
cliOpts.Modules.Directory == "" {
len(cliOpts.Modules.Directories) == 0 {
return
}

if !util.IsDir(cliOpts.Modules.Directory) {
log.Debugf("Skip copying modules from %q: does not exist or not a directory",
cliOpts.Modules.Directory)
} else {
dir, err := os.Open(cliOpts.Modules.Directory)
if err != nil {
log.Warnf("cannot open %q for reading: %s", cliOpts.Modules.Directory, err)
}
if files, _ := dir.Readdir(1); len(files) == 0 {
return // No modules.
}
if err := copy.Copy(cliOpts.Modules.Directory,
util.JoinPaths(bundleEnvPath, newOpts.Modules.Directory)); err != nil {
log.Warnf("Failed to copy modules from %q: %s", cliOpts.Modules.Directory, err)
for _, directory := range cliOpts.Modules.Directories {
if !util.IsDir(directory) {
log.Debugf("Skip copying modules from %q: does not exist or not a directory",
directory)
} else {
dir, err := os.Open(directory)
if err != nil {
log.Warnf("cannot open %q for reading: %s", directory, err)
}
if files, _ := dir.Readdir(1); len(files) == 0 {
return // No modules.
}
// FIXME: Add working with a list https://github.com/tarantool/tt/issues/1014
if err := copy.Copy(directory,
util.JoinPaths(bundleEnvPath, newOpts.Modules.Directories[0])); err != nil {
log.Warnf("Failed to copy modules from %q: %s", directory, err)
}
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions cli/pack/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func Test_createNewOpts(t *testing.T) {
RunDir: "var/run",
},
Modules: &config.ModulesOpts{
Directory: "modules",
Directories: []string{"modules"},
},
Repo: &config.RepoOpts{
Rocks: "",
Expand Down Expand Up @@ -280,7 +280,7 @@ func Test_createNewOpts(t *testing.T) {
RunDir: "var/run",
},
Modules: &config.ModulesOpts{
Directory: "modules",
Directories: []string{"modules"},
},
Repo: &config.RepoOpts{
Rocks: "",
Expand Down Expand Up @@ -317,7 +317,7 @@ func Test_createNewOpts(t *testing.T) {
RunDir: "/var/run/tarantool/bundle",
},
Modules: &config.ModulesOpts{
Directory: "modules",
Directories: []string{"modules"},
},
Repo: &config.RepoOpts{
Rocks: "",
Expand Down Expand Up @@ -360,7 +360,7 @@ func Test_createNewOpts(t *testing.T) {
RunDir: "var/run",
},
Modules: &config.ModulesOpts{
Directory: "modules",
Directories: []string{"modules"},
},
Repo: &config.RepoOpts{
Rocks: "",
Expand Down
Loading

0 comments on commit f083b8d

Please sign in to comment.