diff --git a/CNAME b/CNAME deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cmd/cli/config/set.go b/cmd/cli/config/set.go index 28655c19ca..30ededb9a1 100644 --- a/cmd/cli/config/set.go +++ b/cmd/cli/config/set.go @@ -72,7 +72,7 @@ func newSetCmd() *cobra.Command { value = args[1:] } - return setConfig(cmd, configPath, key, value...) + return setConfig(cmd, rawConfig, configPath, key, value...) }, // Provide auto completion for arguments to the `set` command ValidArgsFunction: setAutoComplete, @@ -82,7 +82,7 @@ func newSetCmd() *cobra.Command { return setCmd } -func setConfig(cmd *cobra.Command, cfgFilePath, key string, value ...string) error { +func setConfig(cmd *cobra.Command, cfg *config.Config, cfgFilePath, key string, value ...string) error { cmd.Printf("Writing config to %s", cfgFilePath) v := viper.New() v.SetConfigFile(cfgFilePath) @@ -95,7 +95,15 @@ func setConfig(cmd *cobra.Command, cfgFilePath, key string, value ...string) err if err != nil { return err } - v.Set(key, parsed) + if key == types.DataDirKey { + dataDirPath := config.AbsPathSilent(parsed.(string)) + if err = config.ValidatePath(dataDirPath); err != nil { + return err + } + v.Set(key, dataDirPath) + } else { + v.Set(key, parsed) + } if err := v.WriteConfigAs(cfgFilePath); err != nil { return err } diff --git a/pkg/config/config.go b/pkg/config/config.go index 64627eb2ea..0d04a00f7d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/mitchellh/go-homedir" "github.com/mitchellh/mapstructure" "github.com/rs/zerolog/log" "github.com/samber/lo" @@ -15,6 +16,7 @@ import ( "github.com/bacalhau-project/bacalhau/pkg/bacerrors" "github.com/bacalhau-project/bacalhau/pkg/config/types" + "github.com/bacalhau-project/bacalhau/pkg/models" "github.com/bacalhau-project/bacalhau/pkg/util/idgen" ) @@ -22,6 +24,8 @@ const ( environmentVariablePrefix = "BACALHAU" inferConfigTypes = true DefaultFileName = "config.yaml" + + errComponent = "config" ) var ( @@ -116,23 +120,13 @@ func New(opts ...Option) (*Config, error) { // To absolute paths for better logging. This is best effort and will not return an error if it fails. for i, path := range c.paths { - if !filepath.IsAbs(path) { - absPath, err := filepath.Abs(path) - if err != nil { - log.Debug().Msgf("failed to resolve absolute path for %s: %v", path, err) - } else { - c.paths[i] = absPath - } - } + c.paths[i] = AbsPathSilent(path) } // merge the config files in the order they were passed. for _, path := range c.paths { - if err := c.Merge(path); err != nil { - if os.IsNotExist(err) { - return nil, fmt.Errorf("the specified configuration file %q doesn't exist", path) - } - return nil, fmt.Errorf("opening config file %q: %w", path, err) + if err := c.merge(path); err != nil { + return nil, err } } @@ -181,6 +175,10 @@ func New(opts ...Option) (*Config, error) { c.values[types.ComputeEnabledKey] = true } } + case types.DataDirKey: + // Handle relative paths for data-dir flag + path := AbsPathSilent(flag.Value.String()) + c.base.Set(types.DataDirKey, path) case "default.publisher.deprecated": // allow the deprecated --default-publisher flag to bind to related fields in the config. for _, key := range []string{ @@ -202,10 +200,26 @@ func New(opts ...Option) (*Config, error) { } // merge the passed values last as they take highest precedence + // allow the user to set datadir as relative paths and resolve them to absolute paths for name, value := range c.values { + if name == types.DataDirKey { + if val, ok := value.(string); ok { + value = AbsPathSilent(val) + } + } c.base.Set(name, value) } + // allow the users to set datadir to a path like ~/.bacalhau or ~/something/idk/whatever + // and expand the path for them + if expandedPath, err := homedir.Expand(c.base.GetString(types.DataDirKey)); err == nil { + c.base.Set(types.DataDirKey, expandedPath) + } + + if err := ValidatePath(c.base.GetString(types.DataDirKey)); err != nil { + return nil, err + } + // if no config file was provided, we look for a config.yaml under the resolved data directory, // and if it exists, we create and return a new config with the resolved path. // we attempt this last to ensure the data-dir is resolved correctly from all config sources. @@ -255,12 +269,15 @@ func (c *Config) Load(path string) error { return nil } -// Merge merges a new configuration file specified by `path` with the existing config. -// Merge returns an error if the file cannot be read -func (c *Config) Merge(path string) error { +// merge merges a new configuration file specified by `path` with the existing config. +// merge returns an error if the file cannot be read +func (c *Config) merge(path string) error { c.base.SetConfigFile(path) if err := c.base.MergeInConfig(); err != nil { - return err + if os.IsNotExist(err) { + return fmt.Errorf("the specified configuration file %q doesn't exist", path) + } + return fmt.Errorf("opening config file %q: %w", path, err) } return nil } @@ -285,6 +302,9 @@ func (c *Config) Unmarshal(out interface{}) error { if err := c.base.Unmarshal(&out, DecoderHook); err != nil { return err } + if v, ok := out.(models.Validatable); ok { + return v.Validate() + } return nil } @@ -317,6 +337,44 @@ func GenerateNodeID(ctx context.Context, nodeNameProviderType string) (string, e return nodeName, nil } +func AbsPathSilent(path string) string { + expandedPath, err := homedir.Expand(path) + if err != nil { + log.Debug().Msgf("failed to expand path %s: %v", path, err) + return path + } + absPath, err := filepath.Abs(expandedPath) + if err != nil { + log.Debug().Msgf("failed to resolve absolute path for %s: %v", path, err) + return path + } + return absPath +} + +func ValidatePath(path string) error { + if path == "" { + return bacerrors.New("data dir path is empty"). + WithHint("Provide a valid path for the data directory"). + WithComponent(errComponent). + WithCode(bacerrors.ValidationError) + } + if strings.Contains(path, "$") { + return bacerrors.New("data dir path %q contains a '$' character", path). + WithHint("Note that environment variables are not expanded will be used as-is"). + WithComponent(errComponent). + WithCode(bacerrors.ValidationError) + } + + if !filepath.IsAbs(path) { + return bacerrors.New("data dir path %q is not an absolute path", path). + WithHint("Use an absolute path for the data directory"). + WithComponent(errComponent). + WithCode(bacerrors.ValidationError) + } + + return nil +} + // checkFlagConfigConflicts checks for conflicts between cli flags and config values. // e.g. bacalhau serve --config=api.host=0.0.0.0 --api-host=0.0.0.0 should be rejected. func checkFlagConfigConflicts(flags map[string][]*pflag.Flag, cfgValues map[string]any) error { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index ca7ce120fb..252e9868db 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -3,6 +3,7 @@ package config_test import ( + "fmt" "os" "path/filepath" "testing" @@ -15,14 +16,185 @@ import ( "github.com/bacalhau-project/bacalhau/pkg/config/types" ) +func TestConfigDataDirPath(t *testing.T) { + workingDir, err := os.Getwd() + require.NoError(t, err) + homeDir := t.TempDir() + t.Setenv("HOME", homeDir) + + testCases := []struct { + name string + dataDir string + setMethod string // "cli", "config", "env", "file", "default" + isValid bool + expected string + errorContains string + }{ + // CLI flag cases + { + name: "CLI: Valid relative path", + dataDir: "relative/path", + setMethod: "cli", + isValid: true, + expected: filepath.Join(workingDir, "relative/path"), + }, + { + name: "CLI: Valid tilde path", + dataDir: "~/some/path", + setMethod: "cli", + isValid: true, + expected: filepath.Join(homeDir, "some/path"), + }, + { + name: "CLI: Valid absolute path", + dataDir: "/absolute/path", + setMethod: "cli", + isValid: true, + expected: "/absolute/path", + }, + // Config value cases + { + name: "Config: Valid relative path", + dataDir: "relative/path", + setMethod: "config", + isValid: true, + expected: filepath.Join(workingDir, "relative/path"), + }, + { + name: "Config: Valid tilde path", + dataDir: "~/some/path", + setMethod: "config", + isValid: true, + expected: filepath.Join(homeDir, "some/path"), + }, + { + name: "Config: Valid absolute path", + dataDir: "/absolute/path", + setMethod: "config", + isValid: true, + expected: "/absolute/path", + }, + // Environment variable cases + { + name: "Env: Invalid relative path", + dataDir: "relative/path", + setMethod: "env", + isValid: false, + errorContains: "not an absolute path", + }, + { + name: "Env: Valid tilde path", + dataDir: "~/some/path", + setMethod: "env", + isValid: true, + expected: filepath.Join(homeDir, "some/path"), + }, + { + name: "Env: Valid absolute path", + dataDir: "/absolute/path", + setMethod: "env", + isValid: true, + expected: "/absolute/path", + }, + // Config file cases + { + name: "File: Invalid relative path", + dataDir: "relative/path", + setMethod: "file", + isValid: false, + errorContains: "not an absolute path", + }, + { + name: "File: Valid tilde path", + dataDir: "~/some/path", + setMethod: "file", + isValid: true, + expected: filepath.Join(homeDir, "some/path"), + }, + { + name: "File: Valid absolute path", + dataDir: "/absolute/path", + setMethod: "file", + isValid: true, + expected: "/absolute/path", + }, + // Default value cases + { + name: "Default: Invalid relative path", + dataDir: "relative/path", + setMethod: "default", + isValid: false, + errorContains: "not an absolute path", + }, + { + name: "Default: Valid tilde path", + dataDir: "~/some/path", + setMethod: "default", + isValid: true, + expected: filepath.Join(homeDir, "some/path"), + }, + { + name: "Default: Valid absolute path", + dataDir: "/absolute/path", + setMethod: "default", + isValid: true, + expected: "/absolute/path", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var cfg *config.Config + + switch tc.setMethod { + case "cli": + flags := make(map[string][]*pflag.Flag) + flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) + flagSet.String(types.DataDirKey, tc.dataDir, "data directory") + flag := flagSet.Lookup(types.DataDirKey) + flag.Changed = true + flags[types.DataDirKey] = []*pflag.Flag{flag} + cfg, err = config.New(config.WithFlags(flags)) + case "config": + cfg, err = config.New(config.WithValues(map[string]interface{}{types.DataDirKey: tc.dataDir})) + case "env": + t.Setenv("BACALHAU_DATADIR", tc.dataDir) + cfg, err = config.New(config.WithEnvironmentVariables(map[string][]string{ + types.DataDirKey: {"BACALHAU_DATADIR"}, + })) + case "file": + tempFile, innerErr := os.CreateTemp("", "config*.yaml") + require.NoError(t, innerErr) + defer os.Remove(tempFile.Name()) + _, innerErr = tempFile.WriteString(fmt.Sprintf("datadir: %s\n", tc.dataDir)) + require.NoError(t, innerErr) + tempFile.Close() + cfg, err = config.New(config.WithPaths(tempFile.Name())) + case "default": + cfg, err = config.New(config.WithDefault(types.Bacalhau{DataDir: tc.dataDir})) + } + + if tc.isValid { + require.NoError(t, err) + var actual types.Bacalhau + assert.NoError(t, cfg.Unmarshal(&actual)) + assert.Equal(t, tc.expected, actual.DataDir) + } else { + require.Error(t, err) + assert.Contains(t, err.Error(), tc.errorContains) + } + }) + } +} + func TestConfigWithValueOverrides(t *testing.T) { - overrideRepo := "overrideRepo" + overrideRepo := "/overrideRepo" overrideName := "puuid" overrideClientAddress := "1.1.1.1" overrideClientPort := 1234 defaultConfig := types.Bacalhau{ - DataDir: "defaultRepo", + DataDir: "/defaultRepo", API: types.API{ Host: "0.0.0.0", Port: 1234, @@ -59,10 +231,7 @@ type TestConfig struct { StringValue string IntValue int BoolValue bool -} - -func (c TestConfig) Validate() error { - return nil + DataDir string } func TestNew(t *testing.T) { @@ -71,6 +240,7 @@ func TestNew(t *testing.T) { StringValue: "default", IntValue: 42, BoolValue: true, + DataDir: "/data/dir", })) require.NoError(t, err) @@ -92,6 +262,7 @@ func TestNew(t *testing.T) { stringValue: "from_file" intValue: 100 boolValue: false +datadir: "/data/dir" `) require.NoError(t, err) tempFile.Close() @@ -134,6 +305,7 @@ boolValue: false StringValue: "default", IntValue: 42, BoolValue: true, + DataDir: "/data/dir", }), config.WithFlags(flags), ) @@ -149,14 +321,9 @@ boolValue: false }) t.Run("With Environment Variables", func(t *testing.T) { - os.Setenv("BACALHAU_STRING_VALUE", "from_env") - os.Setenv("BACALHAU_INT_VALUE", "300") - os.Setenv("BACALHAU_BOOL_VALUE", "true") - defer func() { - os.Unsetenv("BACALHAU_STRING_VALUE") - os.Unsetenv("BACALHAU_INT_VALUE") - os.Unsetenv("BACALHAU_BOOL_VALUE") - }() + t.Setenv("BACALHAU_STRING_VALUE", "from_env") + t.Setenv("BACALHAU_INT_VALUE", "300") + t.Setenv("BACALHAU_BOOL_VALUE", "true") envVars := map[string][]string{ "stringValue": {"BACALHAU_STRING_VALUE"}, @@ -169,6 +336,7 @@ boolValue: false StringValue: "default", IntValue: 42, BoolValue: false, + DataDir: "/data/dir", }), config.WithEnvironmentVariables(envVars), ) @@ -188,6 +356,7 @@ boolValue: false "stringValue": "from_values", "intValue": 400, "boolValue": false, + "datadir": "/data/dir", } cfg, err := config.New( @@ -228,6 +397,7 @@ boolValue: true StringValue: "default", IntValue: 42, BoolValue: false, + DataDir: "/data/dir", }), ) require.NoError(t, err) @@ -252,6 +422,7 @@ func TestMerge(t *testing.T) { _, err = tempFile1.WriteString(` stringValue: "first" intValue: 100 +datadir: "/dir1" `) require.NoError(t, err) tempFile1.Close() @@ -263,6 +434,7 @@ intValue: 100 _, err = tempFile2.WriteString(` intValue: 200 boolValue: true +datadir: "/dir2" `) require.NoError(t, err) tempFile2.Close() @@ -273,13 +445,10 @@ boolValue: true IntValue: 42, BoolValue: false, }), - config.WithPaths(tempFile1.Name()), + config.WithPaths(tempFile1.Name(), tempFile2.Name()), ) require.NoError(t, err) - err = cfg.Merge(tempFile2.Name()) - require.NoError(t, err) - var testCfg TestConfig err = cfg.Unmarshal(&testCfg) require.NoError(t, err) @@ -287,6 +456,7 @@ boolValue: true assert.Equal(t, "first", testCfg.StringValue) assert.Equal(t, 200, testCfg.IntValue) assert.True(t, testCfg.BoolValue) + assert.Equal(t, "/dir2", testCfg.DataDir) } func TestConfigurationPrecedence(t *testing.T) { @@ -304,14 +474,9 @@ boolValue: false tempFile.Close() // Set up environment variables - os.Setenv("BACALHAU_STRING_VALUE", "from_env") - os.Setenv("BACALHAU_INT_VALUE", "200") - os.Setenv("BACALHAU_BOOL_VALUE", "true") - defer func() { - os.Unsetenv("BACALHAU_STRING_VALUE") - os.Unsetenv("BACALHAU_INT_VALUE") - os.Unsetenv("BACALHAU_BOOL_VALUE") - }() + t.Setenv("BACALHAU_STRING_VALUE", "from_env") + t.Setenv("BACALHAU_INT_VALUE", "200") + t.Setenv("BACALHAU_BOOL_VALUE", "true") // Set up explicit values values := map[string]interface{}{ @@ -326,6 +491,7 @@ boolValue: false StringValue: "default", IntValue: 50, BoolValue: false, + DataDir: "/data/dir", }), config.WithPaths(tempFile.Name()), config.WithEnvironmentVariables(map[string][]string{ @@ -367,6 +533,7 @@ boolValue: false StringValue: "default", IntValue: 50, BoolValue: false, + DataDir: "/data/dir", }), config.WithPaths(tempFile.Name()), config.WithEnvironmentVariables(map[string][]string{ @@ -391,6 +558,7 @@ boolValue: false StringValue: "default", IntValue: 50, BoolValue: false, + DataDir: "/data/dir", }), config.WithPaths(tempFile.Name()), config.WithEnvironmentVariables(map[string][]string{ @@ -418,6 +586,7 @@ boolValue: false StringValue: "default", IntValue: 50, BoolValue: true, + DataDir: "/data/dir", }), config.WithPaths(tempFile.Name()), ) @@ -436,6 +605,7 @@ boolValue: false StringValue: "default", IntValue: 50, BoolValue: true, + DataDir: "/data/dir", }), ) require.NoError(t, err) @@ -461,6 +631,7 @@ func TestFlagConfigConflicts(t *testing.T) { values := map[string]any{ "boolValue": true, + "datadir": "/data/dir", } cfg, err := config.New( diff --git a/pkg/config/types/default_config.go b/pkg/config/types/default_config.go index 9a4f6d5e91..a9ae9d7235 100644 --- a/pkg/config/types/default_config.go +++ b/pkg/config/types/default_config.go @@ -130,17 +130,12 @@ const defaultBacalhauDir = ".bacalhau" // DefaultDataDir determines the appropriate default directory for storing repository data. // Priority order: -// 1. If the environment variable BACALHAU_DIR is set and non-empty, use it. -// 2. User's home directory with .bacalhau appended. -// 3. If all above fail, use .bacalhau in the current directory. +// 1. User's home directory with .bacalhau appended. func DefaultDataDir() string { - if repoDir, set := os.LookupEnv("BACALHAU_DIR"); set && repoDir != "" { - return repoDir + if userHome, err := os.UserHomeDir(); err == nil && userHome != "" { + if expandedUserHome, err := filepath.Abs(userHome); err == nil { + return filepath.Join(expandedUserHome, defaultBacalhauDir) + } } - - if userHome, err := os.UserHomeDir(); err == nil { - return filepath.Join(userHome, defaultBacalhauDir) - } - - return defaultBacalhauDir + return "" } diff --git a/pkg/config/types/paths.go b/pkg/config/types/paths.go index 2fff193073..99d7e95499 100644 --- a/pkg/config/types/paths.go +++ b/pkg/config/types/paths.go @@ -7,11 +7,11 @@ import ( const UserKeyFileName = "user_id.pem" -func (c Bacalhau) UserKeyPath() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) UserKeyPath() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } - path := filepath.Join(c.DataDir, UserKeyFileName) + path := filepath.Join(b.DataDir, UserKeyFileName) if exists, err := fileExists(path); err != nil { return "", fmt.Errorf("checking if user key exists: %w", err) } else if exists { @@ -25,20 +25,20 @@ func (c Bacalhau) UserKeyPath() (string, error) { const AuthTokensFileName = "tokens.json" -func (c Bacalhau) AuthTokensPath() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) AuthTokensPath() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } - return filepath.Join(c.DataDir, AuthTokensFileName), nil + return filepath.Join(b.DataDir, AuthTokensFileName), nil } const OrchestratorDirName = "orchestrator" -func (c Bacalhau) OrchestratorDir() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) OrchestratorDir() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } - path := filepath.Join(c.DataDir, OrchestratorDirName) + path := filepath.Join(b.DataDir, OrchestratorDirName) if err := ensureDir(path); err != nil { return "", fmt.Errorf("getting orchestrator path: %w", err) } @@ -47,24 +47,24 @@ func (c Bacalhau) OrchestratorDir() (string, error) { const JobStoreFileName = "state_boltdb.db" -func (c Bacalhau) JobStoreFilePath() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) JobStoreFilePath() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } // make sure the parent dir exists first - if _, err := c.OrchestratorDir(); err != nil { + if _, err := b.OrchestratorDir(); err != nil { return "", fmt.Errorf("getting job store path: %w", err) } - return filepath.Join(c.DataDir, OrchestratorDirName, JobStoreFileName), nil + return filepath.Join(b.DataDir, OrchestratorDirName, JobStoreFileName), nil } const NetworkTransportDirName = "nats-store" -func (c Bacalhau) NetworkTransportDir() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) NetworkTransportDir() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } - path := filepath.Join(c.DataDir, OrchestratorDirName, NetworkTransportDirName) + path := filepath.Join(b.DataDir, OrchestratorDirName, NetworkTransportDirName) if err := ensureDir(path); err != nil { return "", fmt.Errorf("getting network transport path: %w", err) } @@ -73,11 +73,11 @@ func (c Bacalhau) NetworkTransportDir() (string, error) { const ComputeDirName = "compute" -func (c Bacalhau) ComputeDir() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) ComputeDir() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } - path := filepath.Join(c.DataDir, ComputeDirName) + path := filepath.Join(b.DataDir, ComputeDirName) if err := ensureDir(path); err != nil { return "", fmt.Errorf("getting compute path: %w", err) } @@ -86,11 +86,11 @@ func (c Bacalhau) ComputeDir() (string, error) { const ExecutionDirName = "executions" -func (c Bacalhau) ExecutionDir() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) ExecutionDir() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } - path := filepath.Join(c.DataDir, ComputeDirName, ExecutionDirName) + path := filepath.Join(b.DataDir, ComputeDirName, ExecutionDirName) if err := ensureDir(path); err != nil { return "", fmt.Errorf("getting executions path: %w", err) } @@ -99,11 +99,11 @@ func (c Bacalhau) ExecutionDir() (string, error) { const PluginsDirName = "plugins" -func (c Bacalhau) PluginsDir() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) PluginsDir() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } - path := filepath.Join(c.DataDir, PluginsDirName) + path := filepath.Join(b.DataDir, PluginsDirName) if err := ensureDir(path); err != nil { return "", fmt.Errorf("getting plugins path: %w", err) } @@ -112,12 +112,12 @@ func (c Bacalhau) PluginsDir() (string, error) { const ExecutionStoreFileName = "state_boltdb.db" -func (c Bacalhau) ExecutionStoreFilePath() (string, error) { - if c.DataDir == "" { +func (b Bacalhau) ExecutionStoreFilePath() (string, error) { + if b.DataDir == "" { return "", fmt.Errorf("data dir not set") } - if _, err := c.ComputeDir(); err != nil { + if _, err := b.ComputeDir(); err != nil { return "", fmt.Errorf("getting execution store path: %w", err) } - return filepath.Join(c.DataDir, ComputeDirName, ExecutionStoreFileName), nil + return filepath.Join(b.DataDir, ComputeDirName, ExecutionStoreFileName), nil } diff --git a/pkg/repo/fs.go b/pkg/repo/fs.go index 3eb2f37323..4e7724bcbe 100644 --- a/pkg/repo/fs.go +++ b/pkg/repo/fs.go @@ -2,6 +2,7 @@ package repo import ( "context" + "errors" "fmt" "os" "path/filepath" @@ -9,6 +10,8 @@ import ( "github.com/mitchellh/go-homedir" "github.com/rs/zerolog/log" + "github.com/bacalhau-project/bacalhau/pkg/bacerrors" + "github.com/bacalhau-project/bacalhau/pkg/config" "github.com/bacalhau-project/bacalhau/pkg/config_legacy" legacy_types "github.com/bacalhau-project/bacalhau/pkg/config_legacy/types" "github.com/bacalhau-project/bacalhau/pkg/telemetry" @@ -92,7 +95,11 @@ func (fsr *FsRepo) Init() error { // 0755: Owner can read, write, execute. Others can read and execute. if err := os.MkdirAll(fsr.path, repoPermission); err != nil && !os.IsExist(err) { - return err + return bacerrors.New("failed to initialize the bacalhau repo at %q: %s", fsr.path, errors.Unwrap(err)). + WithHint("The data dir you've configured bacalhau to use is invalid\n"+ + "\tIf provided, ensure the --data-dir/--repo flag contains a valid path\n"+ + "\tIf present, ensure the config file provided by the --config flag contains a valid DataDir field path\n"+ + "\tIf present, ensure the config file in %s contains a valid DataDir field path", filepath.Join(fsr.path, config.DefaultFileName)) } // TODO this should be a part of the config.