Skip to content
Snippets Groups Projects
Commit 385da08e authored by Jessica Yuen's avatar Jessica Yuen
Browse files

Support renaming of envs to parent & child directories

Currently, there are limitations around the file system we are using
that does not easily allow renaming of `us-west/prod` to `us-west`, or
vice versa - `us-west` to `us-west/prod`.

This commit will handle the logic to allow for that by moving the file
contents.
parent f6fc32a1
No related branches found
No related tags found
No related merge requests found
......@@ -47,6 +47,17 @@ const (
specFilename = "spec.json"
)
var envPaths = []string{
// metadata Dir.wh
metadataDirName,
// environment base override file
envFileName,
// params file
paramsFileName,
// spec file
specFilename,
}
// Environment represents all fields of a ksonnet environment
type Environment struct {
Path string
......@@ -292,22 +303,36 @@ func (m *manager) SetEnvironment(name string, desired *Environment) error {
if err != nil {
return err
}
if exists {
return fmt.Errorf("Failed to create environment, directory exists at path: %s", string(pathNew))
}
// Need to first create subdirectories that don't exist
intermediatePath := path.Dir(string(pathNew))
log.Debugf("Moving directory at path '%s' to '%s'", string(pathOld), string(pathNew))
err = m.appFS.MkdirAll(intermediatePath, defaultFolderPermissions)
if err != nil {
return err
}
// finally, move the directory
err = m.appFS.Rename(string(pathOld), string(pathNew))
if err != nil {
log.Debugf("Failed to move path '%s' to '%s", string(pathOld), string(pathNew))
return err
// we know that the desired path is not an environment from
// the check earlier. This is an intermediate directory.
// We need to move the file contents.
m.tryMvEnvDir(pathOld, pathNew)
} else if filepath.HasPrefix(string(pathNew), string(pathOld)) {
// the new directory is a child of the old directory --
// rename won't work.
err = m.appFS.MkdirAll(string(pathNew), defaultFolderPermissions)
if err != nil {
return err
}
m.tryMvEnvDir(pathOld, pathNew)
} else {
// Need to first create subdirectories that don't exist
intermediatePath := path.Dir(string(pathNew))
log.Debugf("Moving directory at path '%s' to '%s'", string(pathOld), string(pathNew))
err = m.appFS.MkdirAll(intermediatePath, defaultFolderPermissions)
if err != nil {
return err
}
// finally, move the directory
err = m.appFS.Rename(string(pathOld), string(pathNew))
if err != nil {
log.Debugf("Failed to move path '%s' to '%s", string(pathOld), string(pathNew))
return err
}
}
// clean up any empty parent directory paths
err = m.cleanEmptyParentDirs(name)
if err != nil {
......@@ -414,6 +439,34 @@ func (m *manager) SetEnvironmentParams(env, component string, params param.Param
return nil
}
func (m *manager) tryMvEnvDir(dirPathOld, dirPathNew AbsPath) error {
// first ensure none of these paths exists in the new directory
for _, p := range envPaths {
path := string(appendToAbsPath(dirPathNew, p))
if exists, err := afero.Exists(m.appFS, path); err != nil {
return err
} else if exists {
return fmt.Errorf("%s already exists", path)
}
}
// note: afero and go does not provide simple ways to move the
// contents. We'll have to rename them individually.
for _, p := range envPaths {
err := m.appFS.Rename(string(appendToAbsPath(dirPathOld, p)), string(appendToAbsPath(dirPathNew, p)))
if err != nil {
return err
}
}
// clean up the old directory if it is empty
if empty, err := afero.IsEmpty(m.appFS, string(dirPathOld)); err != nil {
return err
} else if empty {
return m.appFS.RemoveAll(string(dirPathOld))
}
return nil
}
func (m *manager) cleanEmptyParentDirs(name string) error {
// clean up any empty parent directory paths
log.Debug("Removing empty parent directories, if any")
......
......@@ -36,8 +36,13 @@ const (
var mockAPIServer = "http://example.com"
var mockNamespace = "some-namespace"
var mockEnvs = []string{defaultEnvName, mockEnvName, mockEnvName2, mockEnvName3}
func mockEnvironments(t *testing.T, appName string) *manager {
return mockEnvironmentsWith(t, appName, mockEnvs)
}
func mockEnvironmentsWith(t *testing.T, appName string, envNames []string) *manager {
spec, err := parseClusterSpec(fmt.Sprintf("file:%s", blankSwagger), testFS)
if err != nil {
t.Fatalf("Failed to parse cluster spec: %v", err)
......@@ -50,7 +55,6 @@ func mockEnvironments(t *testing.T, appName string) *manager {
t.Fatalf("Failed to init cluster spec: %v", err)
}
envNames := []string{defaultEnvName, mockEnvName, mockEnvName2, mockEnvName3}
for _, env := range envNames {
envPath := appendToAbsPath(m.environmentsPath, env)
testFS.Mkdir(string(envPath), defaultFolderPermissions)
......@@ -226,6 +230,36 @@ func TestSetEnvironment(t *testing.T) {
if envSpec.Namespace != set.Namespace {
t.Fatalf("Expected namespace to be set to '%s', got: '%s'", set.Namespace, envSpec.Namespace)
}
tests := []struct {
appName string
nameOld string
nameNew string
}{
// Test changing the name of an env 'us-west' to 'us-west/dev'
{
"test-set-to-child",
"us-west",
"us-west/dev",
},
// Test changing the name of an env 'us-west/dev' to 'us-west'
{
"test-set-to-parent",
"us-west/dev",
"us-west",
},
}
for _, v := range tests {
m = mockEnvironmentsWith(t, v.appName, []string{v.nameOld})
err = m.SetEnvironment(v.nameOld, &Environment{Name: v.nameNew})
if err != nil {
t.Fatalf("Could not set '%s', got:\n %s", v.nameOld, err)
}
// Ensure new env directory is created
expectedPath := appendToAbsPath(AbsPath(v.appName), environmentsDir, v.nameNew)
testDirExists(t, string(expectedPath))
}
}
func TestGenerateOverrideData(t *testing.T) {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment