-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Summary: Add a cd step that allowers users to change the working directory for all subsequent steps. Differential Revision: D63702296
- Loading branch information
1 parent
1b645b8
commit 9fc997a
Showing
4 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
--- | ||
api_version: 2.0 | ||
uuid: 236f8a86-8034-43aa-a6e5-979337c375a4 | ||
name: change_directory_example | ||
description: | | ||
This TTP shows you how to use the change_directory action type | ||
to change the working directory for all future actions. If you specify cleanup | ||
as default, the directory will be reverted during the cleanup phase. This may | ||
be useful if any of your cleanups make assumptions about their working directory. | ||
args: | ||
- name: cd_destination | ||
description: this argument is where we will try to cd to | ||
default: /tmp | ||
steps: | ||
- name: "Initial directory" | ||
inline: | | ||
echo "Current working directory is: \"$(pwd)\"" | ||
- name: "cd" | ||
cd: {{.Args.cd_destination}} | ||
cleanup: default | ||
- name: "New directory" | ||
inline: | | ||
echo "Current working directory is: \"$(pwd)\"" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/* | ||
Copyright © 2024-present, Meta Platforms, Inc. and affiliates | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
*/ | ||
|
||
package blocks | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/facebookincubator/ttpforge/pkg/logging" | ||
"github.com/spf13/afero" | ||
) | ||
|
||
// ChangeDirectoryStep is a step that changes the current working directory | ||
type ChangeDirectoryStep struct { | ||
actionDefaults `yaml:",inline"` | ||
Cd string `yaml:"cd"` | ||
PreviousDir string | ||
PreviousCDStep *ChangeDirectoryStep | ||
FileSystem afero.Fs `yaml:"-,omitempty"` | ||
} | ||
|
||
// NewChangeDirectoryStep creates a new ChangeDirectoryStep instance with an initialized Act struct. | ||
func NewChangeDirectoryStep() *ChangeDirectoryStep { | ||
return &ChangeDirectoryStep{} | ||
} | ||
|
||
// IsNil checks if a ChangeDirectoryStep is considered empty or unitializied | ||
func (step *ChangeDirectoryStep) IsNil() bool { | ||
return step.Cd == "" | ||
} | ||
|
||
// Validate validates the ChangeDirectoryStep, checking for the necessary attributes and dependencies. | ||
// | ||
// **Returns:** | ||
// | ||
// error: error if validation fails, nil otherwise | ||
func (step *ChangeDirectoryStep) Validate(_ TTPExecutionContext) error { | ||
// If this has a parent cd step, hold off on validation until execute | ||
if step.PreviousCDStep != nil { | ||
return nil | ||
} | ||
|
||
// Check if cd is provided | ||
if step.Cd == "" { | ||
err := errors.New("cd must be provided") | ||
return err | ||
} | ||
|
||
// Check if cd is a valid directory | ||
fsys := step.FileSystem | ||
if fsys == nil { | ||
fsys = afero.NewOsFs() | ||
} | ||
|
||
exists, err := afero.DirExists(fsys, step.Cd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if !exists { | ||
return fmt.Errorf("directory \"%s\" does not exist", step.Cd) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Execute runs the ChangeDirectoryStep, changing the current working directory and returns an error if any occur. | ||
// | ||
// **Returns:** | ||
// | ||
// ActResult: the result of the action | ||
// error: error if execution fails, nil otherwise | ||
func (step *ChangeDirectoryStep) Execute(ctx TTPExecutionContext) (*ActResult, error) { | ||
// If this has a parent, then it's a cleanup step, so we need to grab the previous dir from it | ||
if step.PreviousCDStep != nil { | ||
if step.PreviousCDStep.PreviousDir == "" { | ||
return nil, fmt.Errorf("no previous directory found in parent cd step") | ||
} | ||
step.Cd = step.PreviousCDStep.PreviousDir | ||
} | ||
|
||
logging.L().Infof("Changing directory to %s", step.Cd) | ||
|
||
if step.Cd == "" { | ||
return nil, fmt.Errorf("empty cd value in Execute(...)") | ||
} | ||
|
||
// Set workdir to the current cd value and store the previous workdir | ||
step.PreviousDir = ctx.Vars.WorkDir | ||
ctx.Vars.WorkDir = step.Cd | ||
|
||
return &ActResult{}, nil | ||
} | ||
|
||
// GetDefaultCleanupAction sets the directory back to the previous directory | ||
func (step *ChangeDirectoryStep) GetDefaultCleanupAction() Action { | ||
return &ChangeDirectoryStep{ | ||
PreviousCDStep: step, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
Copyright © 2023-present, Meta Platforms, Inc. and affiliates | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
*/ | ||
|
||
package blocks | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/facebookincubator/ttpforge/pkg/testutils" | ||
"github.com/spf13/afero" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestChangeDirectoryExecute(t *testing.T) { | ||
|
||
testCases := []struct { | ||
name string | ||
description string | ||
step *ChangeDirectoryStep | ||
fsysContents map[string][]byte | ||
expectedError bool | ||
startingDir string | ||
}{ | ||
{ | ||
name: "Change directory to valid directory", | ||
description: "Change directory and expect successful change of workdir", | ||
step: &ChangeDirectoryStep{ | ||
Cd: "/tmp", | ||
}, | ||
fsysContents: map[string][]byte{ | ||
"/home/testuser/test": []byte("test"), | ||
"/tmp/test": []byte("test"), | ||
}, | ||
expectedError: false, | ||
startingDir: "/home/testuser/", | ||
}, | ||
{ | ||
name: "Change directory to invalid directory", | ||
description: "Try to change directory to invalid directory and expect error", | ||
step: &ChangeDirectoryStep{ | ||
Cd: "/doesntexist", | ||
}, | ||
fsysContents: map[string][]byte{ | ||
"/home/testuser/test": []byte("test"), | ||
"/tmp/test": []byte("test"), | ||
}, | ||
expectedError: true, | ||
startingDir: "/home/testuser/", | ||
}, | ||
{ | ||
name: "Change directory with no given directory", | ||
description: "Try to change directory to no directory and expect error", | ||
step: &ChangeDirectoryStep{ | ||
Cd: "", | ||
}, | ||
fsysContents: map[string][]byte{ | ||
"/home/testuser/test": []byte("test"), | ||
"/tmp/test": []byte("test"), | ||
}, | ||
expectedError: true, | ||
startingDir: "/home/testuser/", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
// Prep filesystem | ||
if tc.fsysContents != nil { | ||
fsys, err := testutils.MakeAferoTestFs(tc.fsysContents) | ||
require.NoError(t, err) | ||
tc.step.FileSystem = fsys | ||
} else { | ||
tc.step.FileSystem = afero.NewMemMapFs() | ||
} | ||
|
||
// validate and check error | ||
execCtx := NewTTPExecutionContext() | ||
execCtx.Vars.WorkDir = tc.startingDir | ||
err := tc.step.Validate(execCtx) | ||
|
||
if tc.expectedError { | ||
require.Error(t, err) | ||
return | ||
} | ||
require.NoError(t, err) | ||
|
||
// execute and check error | ||
_, err = tc.step.Execute(execCtx) | ||
|
||
if tc.expectedError { | ||
require.Error(t, err) | ||
return | ||
} | ||
require.NoError(t, err) | ||
|
||
// check current working directory | ||
assert.Equal(t, tc.step.Cd, execCtx.Vars.WorkDir) | ||
|
||
// cleanup and check error | ||
err = tc.step.GetDefaultCleanupAction().Validate(execCtx) | ||
require.NoError(t, err) | ||
_, err = tc.step.GetDefaultCleanupAction().Execute(execCtx) | ||
require.NoError(t, err) | ||
|
||
// expect working directory to be rolled back to starting directory | ||
assert.Equal(t, tc.startingDir, execCtx.Vars.WorkDir) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters