Skip to content

Commit

Permalink
modules: find module with TT_CLI_MODULES_PATH
Browse files Browse the repository at this point in the history
Closes #1013

@TarantoolBot document
Title: Use environment variable to find modules.

The TT_CLI_MODULES_PATH environment variable specifies a list of
module's paths separated by a colon “:”.
It can be set externally from tt and must contain a list of
folders where modules are located.
The logic of working with this variable is similar to the PATH
system variable.

Lists of modules are combined into one common list:

- The modules declared in the configuration file tt.yaml come first
- then the modules defined via the environment variable are added.
Any duplicates must be removed, while maintaining the original
order of module declarations.

**Usage example**
Set variable:
```sh
export TT_CLI_MODULES_PATH=/ext/path/modules:${TT_CLI_MODULES_PATH}
```
  • Loading branch information
dmyger committed Dec 6, 2024
1 parent 232230a commit 95a94bb
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added

- `tt.yaml`: allows to specify a list of modules directories.
- Environment variable TT_CLI_MODULES_PATH can be used to specify
an extra path with modules.

### Changed

Expand Down
74 changes: 52 additions & 22 deletions cli/modules/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@ func fillSubCommandsInfo(cmd *cobra.Command, modulesInfo *ModulesInfo) {
// GetModulesInfo collects information about available modules (both external and internal).
func GetModulesInfo(cmdCtx *cmdcontext.CmdCtx, rootCmd *cobra.Command,
cliOpts *config.CliOpts) (ModulesInfo, error) {
modulesDir, err := getExternalModulesDir(cmdCtx, cliOpts)
modulesDirs, err := getConfigModulesDirs(cmdCtx, cliOpts)
if err != nil {
return nil, err
}

externalModules, err := getExternalModules(modulesDir)
modulesEnvDirs, err := getEnvironmentModulesDirs()
if err != nil {
return nil, err
}
modulesDirs = append(modulesDirs, modulesEnvDirs...)

externalModules, err := getExternalModules(modulesDirs)
if err != nil {
return nil, fmt.Errorf(
"failed to get available external modules information: %s", err)
Expand All @@ -72,45 +78,69 @@ func GetModulesInfo(cmdCtx *cmdcontext.CmdCtx, rootCmd *cobra.Command,
return modulesInfo, nil
}

// getExternalModulesDir returns the directory where external modules are located.
func getExternalModulesDir(cmdCtx *cmdcontext.CmdCtx, cliOpts *config.CliOpts) (string, error) {
// collectDirectoriesList checks list to ensure that all items is directories.
func collectDirectoriesList(paths []string) ([]string, error) {
dirs := make([]string, 0, len(paths))
// We return an error only if the following conditions are met:
// 1. If a directory field is specified;
// 2. Specified path exists;
// 3. Path points to not a directory.
for _, dir := range paths {
if info, err := os.Stat(dir); err == nil {
// TODO: Add warning in next patches, discussion
// what if the file exists, but access is denied, etc.
// FIXME: resolve this question while prepare list:
// https://github.com/tarantool/tt/issues/1014
if !info.IsDir() {
return dirs, fmt.Errorf("specified path in configuration file is not a directory")
}
dirs = append(dirs, dir)
}
}

return dirs, nil
}

// getConfigModulesDirs returns from configuration the list of directories,
// where external modules are located.
func getConfigModulesDirs(cmdCtx *cmdcontext.CmdCtx, cliOpts *config.CliOpts) ([]string, error) {
// Configuration file not detected - ignore and work on.
// TODO: Add warning in next patches, discussion
// what if the file exists, but access is denied, etc.
if _, err := os.Stat(cmdCtx.Cli.ConfigPath); err != nil {
if !os.IsNotExist(err) {
return "", fmt.Errorf("failed to get access to configuration file: %s", err)
return []string{}, fmt.Errorf("failed to get access to configuration file: %s", err)
}

return "", nil
return []string{}, nil
}

// Unspecified `modules` field is not considered an error.
if cliOpts.Modules == nil {
return "", nil
return []string{}, nil
}

// We return an error only if the following conditions are met:
// 1. If a directory field is specified;
// 2. Specified path exists;
// 3. Path points to not a 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.
if !info.IsDir() {
return "", fmt.Errorf("specified path in configuration file is not a directory")
}
}
return collectDirectoriesList(cliOpts.Modules.Directories)
}

return modulesDir, nil
func getEnvironmentModulesDirs() ([]string, error) {
env_var := os.Getenv("TT_CLI_MODULES_PATH")
if env_var == "" {
return []string{}, nil
}
paths := strings.Split(env_var, ":")
return collectDirectoriesList(paths)
}

// getExternalModules returns map of available modules by
// parsing the contents of the path folder.
func getExternalModules(path string) (map[string]string, error) {
func getExternalModules(paths []string) (map[string]string, error) {
modules := make(map[string]string)
if len(paths) == 0 {
return modules, nil
}
// FIXME: Work with list at https://github.com/tarantool/tt/issues/1014
path := paths[0]

// If the directory doesn't exist, it is not an error.
// TODO: Add warning in next patches, discussion
Expand Down
2 changes: 2 additions & 0 deletions cli/modules/modules_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// FIXME: Create new tests https://github.com/tarantool/tt/issues/1039

package modules

import (
Expand Down

0 comments on commit 95a94bb

Please sign in to comment.