diff --git a/packer/cache.go b/packer/cache.go index b2be017fe..d0bd4f4c8 100644 --- a/packer/cache.go +++ b/packer/cache.go @@ -5,27 +5,29 @@ import ( "path/filepath" ) -var DefaultCacheDir = "packer_cache" - // CachePath returns an absolute path to a cache file or directory // -// When the directory is not absolute, CachePath will try to get -// current working directory to be able to return a full path. -// CachePath tries to create the resulting path if it doesn't exist. -// -// CachePath can error in case it cannot find the cwd. +// When the directory is not absolute, CachePath will try to make a +// a cache depending on the operating system. // -// ex: +// TODO: Update this with better examples +// NOTE: cache directory will change depending on operating system dependent +// For Windows: // PACKER_CACHE_DIR="" CacheDir() => "./packer_cache/ // PACKER_CACHE_DIR="" CacheDir("foo") => "./packer_cache/foo // PACKER_CACHE_DIR="bar" CacheDir("foo") => "./bar/foo // PACKER_CACHE_DIR="/home/there" CacheDir("foo", "bar") => "/home/there/foo/bar +// For Unix: +// PACKER_CACHE_DIR="", XDG_CONFIG_HOME="", Default_config CacheDir() => "$HOME/cache/packer" +// PACKER_CACHE_DIR="", XDG_CONFIG_HOME="", Default_config CacheDir("foo") => "$HOME/cache/packer/foo" +// PACKER_CACHE_DIR="bar", XDG_CONFIG_HOME="", Default_config CacheDir("foo") => "./bar/foo +// PACKER_CACHE_DIR="/home/there", XDG_CONFIG_HOME="", Default_config CacheDir("foo", "bar") => "/home/there/foo/bar func CachePath(paths ...string) (path string, err error) { defer func() { // create the dir based on return path if it doesn't exist os.MkdirAll(filepath.Dir(path), os.ModePerm) }() - cacheDir := DefaultCacheDir + cacheDir := getDefaultCacheDir() if cd := os.Getenv("PACKER_CACHE_DIR"); cd != "" { cacheDir = cd } diff --git a/packer/cache_config_unix.go b/packer/cache_config_unix.go new file mode 100644 index 000000000..51d3318c3 --- /dev/null +++ b/packer/cache_config_unix.go @@ -0,0 +1,21 @@ +// +build darwin freebsd linux netbsd openbsd solaris + +package packer + +import ( + "os" + "path/filepath" +) + +func getDefaultCacheDir() string { + var defaultConfigFileDir string + + if xdgCacheHome := os.Getenv("XDG_CACHE_HOME"); xdgCacheHome != "" { + return filepath.Join(xdgCacheHome, "packer") + } + + homeDir := os.Getenv("HOME") + defaultConfigFileDir = filepath.Join(homeDir, ".cache", "packer") + + return defaultConfigFileDir +} diff --git a/packer/cache_config_unix_test.go b/packer/cache_config_unix_test.go new file mode 100644 index 000000000..4473419b1 --- /dev/null +++ b/packer/cache_config_unix_test.go @@ -0,0 +1,108 @@ +// +build darwin freebsd linux netbsd openbsd solaris + +package packer + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func TestCachePath(t *testing.T) { + // temporary directories for env vars + xdgCacheHomeTempDir, err := ioutil.TempDir(os.TempDir(), "*") + if err != nil { + t.Fatalf("Failed to create temp test directory: failing test: %v", err) + } + defer os.RemoveAll(xdgCacheHomeTempDir) + packerCacheTempDir, err := ioutil.TempDir(os.TempDir(), "*") + if err != nil { + t.Fatalf("Failed to create temp test directory: failing test: %v", err) + } + defer os.RemoveAll(packerCacheTempDir) + + // reset env + packerCacheDir := os.Getenv("PACKER_CACHE_DIR") + os.Setenv("PACKER_CACHE_DIR", "") + defer func() { + os.Setenv("PACKER_CACHE_DIR", packerCacheDir) + }() + + xdgCacheHomeDir := os.Getenv("XDG_CACHE_HOME") + os.Setenv("XDG_CACHE_HOME", "") + defer func() { + os.Setenv("XDG_CACHE_HOME", xdgCacheHomeDir) + }() + + type args struct { + paths []string + } + tests := []struct { + name string + args args + env map[string]string + want string + wantErr bool + }{ + { + "base", + args{}, + nil, + filepath.Join(os.Getenv("HOME"), ".cache", "packer"), + false, + }, + { + "base and path", + args{[]string{"a", "b"}}, + nil, + filepath.Join(os.Getenv("HOME"), ".cache", "packer", "a", "b"), + false, + }, + { + "env PACKER_CACHE_DIR and path", + args{[]string{"a", "b"}}, + map[string]string{"PACKER_CACHE_DIR": packerCacheTempDir}, + filepath.Join(packerCacheTempDir, "a", "b"), + false, + }, + { + "env XDG_CACHE_HOME and path", + args{[]string{"a", "b"}}, + map[string]string{"XDG_CACHE_HOME": xdgCacheHomeTempDir}, + filepath.Join(xdgCacheHomeTempDir, "packer", "a", "b"), + false, + }, + { + "env PACKER_CACHE_DIR, XDG_CACHE_HOME, and path", + args{[]string{"a", "b"}}, + map[string]string{ + "XDG_CACHE_HOME": xdgCacheHomeTempDir, + "PACKER_CACHE_DIR": packerCacheTempDir, + }, + filepath.Join(packerCacheTempDir, "a", "b"), + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for k, v := range tt.env { + os.Setenv(k, v) + } + got, err := CachePath(tt.args.paths...) + if (err != nil) != tt.wantErr { + t.Errorf("CachePath() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("CachePath() = %v, want %v", got, tt.want) + } + resetTestEnv() + }) + } +} + +func resetTestEnv() { + os.Setenv("PACKER_CACHE_DIR", "") + os.Setenv("XDG_CACHE_HOME", "") +} diff --git a/packer/cache_config_windows.go b/packer/cache_config_windows.go new file mode 100644 index 000000000..d6ca1f85d --- /dev/null +++ b/packer/cache_config_windows.go @@ -0,0 +1,11 @@ +// +build windows + +package packer + +const ( + defaultConfigFile = "packer_cache" +) + +func getDefaultCacheDir() string { + return defaultConfigFile +} diff --git a/packer/cache_test.go b/packer/cache_config_windows_test.go similarity index 98% rename from packer/cache_test.go rename to packer/cache_config_windows_test.go index 3075acbd6..da052cab2 100644 --- a/packer/cache_test.go +++ b/packer/cache_config_windows_test.go @@ -1,3 +1,5 @@ +// +build windows + package packer import ( diff --git a/pathing/config_file.go b/pathing/config_file.go index ee2ef52b7..a21a52d2e 100644 --- a/pathing/config_file.go +++ b/pathing/config_file.go @@ -70,22 +70,6 @@ func configFile() (string, error) { return filepath.Join(dir, defaultConfigFile), nil } -func configDir() (string, error) { - var dir string - if cd := os.Getenv("PACKER_CONFIG_DIR"); cd != "" { - log.Printf("Detected config directory from env var: %s", cd) - dir = cd - } else { - homedir, err := homeDir() - if err != nil { - return "", err - } - dir = homedir - } - - return filepath.Join(dir, defaultConfigDir), nil -} - // Given a path, check to see if it's using ~ to reference a user directory. // If so, then replace that component with the requested user directory. // In "~/", "~" gets replaced by current user's home dir. diff --git a/pathing/config_file_unix.go b/pathing/config_file_unix.go index 71f1f2669..8dd47caca 100644 --- a/pathing/config_file_unix.go +++ b/pathing/config_file_unix.go @@ -2,7 +2,48 @@ package pathing +import ( + "errors" + "log" + "os" + "path/filepath" +) + const ( defaultConfigFile = ".packerconfig" defaultConfigDir = ".packer.d" ) + +func configDir() (path string, err error) { + + if cd := os.Getenv("PACKER_CONFIG_DIR"); cd != "" { + log.Printf("Detected config directory from env var: %s", cd) + return filepath.Join(cd, defaultConfigDir), nil + } + + var dir string + homedir := os.Getenv("HOME") + + if homedir == "" { + return "", errors.New("No $HOME environment variable found, required to set Config Directory") + } + + if hasDefaultConfigFileLocation(homedir) { + dir = filepath.Join(homedir, defaultConfigDir) + log.Printf("Old default config directory found: %s", dir) + } else if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" { + log.Printf("Detected xdg config directory from env var: %s", xdgConfigHome) + dir = filepath.Join(xdgConfigHome, "packer") + } else { + dir = filepath.Join(homedir, ".config", "packer") + } + + return dir, nil +} + +func hasDefaultConfigFileLocation(homedir string) bool { + if _, err := os.Stat(filepath.Join(homedir, defaultConfigDir)); err != nil { + return false + } + return true +} diff --git a/pathing/config_file_unix_test.go b/pathing/config_file_unix_test.go new file mode 100644 index 000000000..52ee01a79 --- /dev/null +++ b/pathing/config_file_unix_test.go @@ -0,0 +1,141 @@ +// +build darwin freebsd linux netbsd openbsd solaris + +package pathing + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func TestConfigPath(t *testing.T) { + // temporary directories for env vars + xdgConfigHomeTempDir, err := ioutil.TempDir(os.TempDir(), "*") + if err != nil { + t.Fatalf("Failed to create temp test directory: failing test: %v", err) + } + defer os.RemoveAll(xdgConfigHomeTempDir) + + packerConfigTempDir, err := ioutil.TempDir(os.TempDir(), "*") + if err != nil { + t.Fatalf("Failed to create temp test directory: failing test: %v", err) + } + defer os.RemoveAll(packerConfigTempDir) + + homeTempDir, err := ioutil.TempDir(os.TempDir(), "*") + if err != nil { + t.Fatalf("Failed to create temp test directory: failing test: %v", err) + } + defer os.RemoveAll(homeTempDir) + + homeDirDefaultConfigTempDir, err := ioutil.TempDir(os.TempDir(), "*") + if err != nil { + t.Fatalf("Failed to create temp test directory: failing test: %v", err) + } + + err = os.Mkdir(filepath.Join(homeDirDefaultConfigTempDir, defaultConfigDir), 0755) + if err != nil { + t.Fatalf("Failed to create temp test file: failing test: %v", err) + } + defer os.RemoveAll(homeDirDefaultConfigTempDir) + + // reset env + packerConfigDir := os.Getenv("PACKER_CONFIG_DIR") + os.Setenv("PACKER_CONFIG_DIR", "") + defer func() { + os.Setenv("PACKER_CONFIG_DIR", packerConfigDir) + }() + + xdgConfigHomeDir := os.Getenv("XDG_CONFIG_HOME") + os.Setenv("XDG_CONFIG_HOME", "") + defer func() { + os.Setenv("XDG_CONFIG_HOME", xdgConfigHomeDir) + }() + + homeDir := os.Getenv("HOME") + os.Setenv("HOME", "") + defer func() { + os.Setenv("HOME", homeDir) + }() + + tests := []struct { + name string + env map[string]string + want string + wantErr bool + }{ + { + "no HOME env var", + nil, + "", + true, + }, + { + "base", + map[string]string{"HOME": homeTempDir}, + filepath.Join(homeTempDir, ".config", "packer"), + false, + }, + { + "XDG_CONFIG_HOME set without default file", + map[string]string{ + "XDG_CONFIG_HOME": xdgConfigHomeTempDir, + "HOME": homeTempDir, + }, + filepath.Join(xdgConfigHomeTempDir, "packer"), + false, + }, + { + "env PACKER_CONFIG_DIR", + map[string]string{"PACKER_CONFIG_DIR": packerConfigTempDir}, + filepath.Join(packerConfigTempDir, defaultConfigDir), + false, + }, + { + "env PACKER_CONFIG_DIR, XDG_CONFIG_HOME", + map[string]string{ + "XDG_CONFIG_HOME": xdgConfigHomeTempDir, + "PACKER_CONFIG_DIR": packerConfigTempDir, + }, + filepath.Join(packerConfigTempDir, defaultConfigDir), + false, + }, + { + "Old Default Config Found", + map[string]string{"HOME": homeDirDefaultConfigTempDir}, + filepath.Join(homeDirDefaultConfigTempDir, defaultConfigDir), + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for k, v := range tt.env { + os.Setenv(k, v) + } + got, err := ConfigDir() + if (err != nil) != tt.wantErr { + t.Errorf( + "Name: %v, ConfigPath() error = %v, wantErr %v", + tt.name, + err, + tt.wantErr) + return + } + if got != tt.want { + t.Errorf( + "Name: %v, ConfigPath() = %v, want %v", + tt.name, + got, + tt.want) + } + resetTestEnv() + }) + } +} + +func resetTestEnv() { + os.Setenv("PACKER_CONFIG_DIR", "") + os.Setenv("XDG_CONFIG_HOME", "") + os.Setenv("HOME", "") +} diff --git a/pathing/config_file_windows.go b/pathing/config_file_windows.go index 138dd9dcd..d7b27365a 100644 --- a/pathing/config_file_windows.go +++ b/pathing/config_file_windows.go @@ -2,7 +2,28 @@ package pathing +import ( + "log" + "os" + "path/filepath" +) + const ( defaultConfigFile = "packer.config" defaultConfigDir = "packer.d" ) + +func configDir() (path string, err error) { + + if cd := os.Getenv("PACKER_CONFIG_DIR"); cd != "" { + log.Printf("Detected config directory from env var: %s", cd) + return filepath.Join(cd, defaultConfigDir), nil + } + + homedir, err := homeDir() + if err != nil { + return "", err + } + + return filepath.Join(homedir, defaultConfigDir), nil +} diff --git a/pathing/config_file_windows_test.go b/pathing/config_file_windows_test.go new file mode 100644 index 000000000..c2b64ecdc --- /dev/null +++ b/pathing/config_file_windows_test.go @@ -0,0 +1,79 @@ +// +build windows + +package pathing + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func TestConfigPath(t *testing.T) { + // temporary directories for env vars + packerConfigTempDir, err := ioutil.TempDir(os.TempDir(), "*") + if err != nil { + t.Fatalf("Failed to create temp test directory: failing test: %v", err) + } + defer os.RemoveAll(packerConfigTempDir) + + // reset env + packerConfigDir := os.Getenv("PACKER_CONFIG_DIR") + os.Setenv("PACKER_CONFIG_DIR", "") + defer func() { + os.Setenv("PACKER_CONFIG_DIR", packerConfigDir) + }() + + homedir, err := homeDir() + if err != nil { + t.Fatalf("err: %s", err) + } + + tests := []struct { + name string + env map[string]string + want string + wantErr bool + }{ + { + "base", + nil, + filepath.Join(homedir, defaultConfigDir), + false, + }, + { + "env PACKER_CONFIG_DIR", + map[string]string{"PACKER_CONFIG_DIR": packerConfigTempDir}, + filepath.Join(packerConfigTempDir, defaultConfigDir), + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for k, v := range tt.env { + os.Setenv(k, v) + } + got, err := ConfigDir() + if (err != nil) != tt.wantErr { + t.Errorf( + "Name: %v, ConfigPath() error = %v, wantErr %v", + tt.name, + err, + tt.wantErr) + return + } + if got != tt.want { + t.Errorf( + "Name: %v, ConfigPath() = %v, want %v", + tt.name, + got, + tt.want) + } + resetTestEnv() + }) + } +} + +func resetTestEnv() { + os.Setenv("PACKER_CONFIG_DIR", "") +}