Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lockdown /tekton/step folders to their own steps. #4352

Merged
merged 1 commit into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions cmd/entrypoint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ var (
breakpointOnFailure = flag.Bool("breakpoint_on_failure", false, "If specified, expect steps to not skip on failure")
onError = flag.String("on_error", "", "Set to \"continue\" to ignore an error and continue when a container terminates with a non-zero exit code."+
" Set to \"stopAndFail\" to declare a failure with a step error and stop executing the rest of the steps.")
stepMetadataDir = flag.String("step_metadata_dir", "", "If specified, create directory to store the step metadata e.g. /tekton/steps/<step-name>/")
stepMetadataDirLink = flag.String("step_metadata_dir_link", "", "creates a symbolic link to the specified step_metadata_dir e.g. /tekton/steps/<step-index>/")
stepMetadataDir = flag.String("step_metadata_dir", "", "If specified, create directory to store the step metadata e.g. /tekton/steps/<step-name>/")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: it looks like the "=" lost its alignment?

Copy link
Member Author

@wlynch wlynch Dec 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷 gofmt is doing this, so I'm not going to fight it. My guess is the string concat above is resetting the alignment, and stepMetadataDirLink just happened to be the right length to align this block with the flags above.

)

const (
Expand Down Expand Up @@ -114,7 +113,6 @@ func main() {
BreakpointOnFailure: *breakpointOnFailure,
OnError: *onError,
StepMetadataDir: *stepMetadataDir,
StepMetadataDirLink: *stepMetadataDirLink,
}

// Copy any creds injected by the controller into the $HOME directory of the current
Expand Down
31 changes: 7 additions & 24 deletions cmd/entrypoint/post_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"log"
"os"
"path/filepath"

"github.com/tektoncd/pipeline/pkg/entrypoint"
)
Expand All @@ -13,11 +14,16 @@ type realPostWriter struct{}
var _ entrypoint.PostWriter = (*realPostWriter)(nil)

// Write creates a file and writes content to that file if content is specified
// assumption here is the underlying directory structure already exists
func (*realPostWriter) Write(file string, content string) {
if file == "" {
return
}

// Create directory if it doesn't already exist
if err := os.MkdirAll(filepath.Dir(file), os.ModePerm); err != nil {
log.Fatalf("Error creating parent directory of %q: %v", file, err)
}

Comment on lines +21 to +26
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add tests for this new behaviour?
I tried removing this and unit tests in cmd/entrypoint/post_writer_test.go still work fine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

f, err := os.Create(file)
if err != nil {
log.Fatalf("Creating %q: %v", file, err)
Expand All @@ -29,26 +35,3 @@ func (*realPostWriter) Write(file string, content string) {
}
}
}

// CreateDirWithSymlink creates the specified directory and a symbolic link to that directory
func (*realPostWriter) CreateDirWithSymlink(source, link string) {
if source == "" {
return
}
if err := os.MkdirAll(source, 0770); err != nil {
log.Fatalf("Creating directory %q: %v", source, err)
}

if link == "" {
return
}
// create a symlink if it does not exist
if _, err := os.Stat(link); os.IsNotExist(err) {
// check if a source exist before creating a symbolic link
if _, err := os.Stat(source); err == nil {
if err := os.Symlink(source, link); err != nil {
log.Fatalf("Creating a symlink %q: %v", link, err)
}
}
}
}
45 changes: 9 additions & 36 deletions cmd/entrypoint/post_writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ package main
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)

func TestRealPostWriter_WriteFileContent(t *testing.T) {
testdir, err := ioutil.TempDir("", "post-writer")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(testdir)
tests := []struct {
name, file, content string
}{{
Expand All @@ -18,6 +24,9 @@ func TestRealPostWriter_WriteFileContent(t *testing.T) {
}, {
name: "create an empty file",
file: "sample.txt",
}, {
name: "create an empty file in new subdirectory",
file: filepath.Join(testdir, "dir", "sample.txt"),
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -39,39 +48,3 @@ func TestRealPostWriter_WriteFileContent(t *testing.T) {
})
}
}

func TestRealPostWriter_CreateStepPath(t *testing.T) {
tests := []struct {
name, source, link string
}{{
name: "Create a path with a file",
source: "sample.txt",
link: "0",
}, {
name: "Create a path without specifying any path",
}, {
name: "Create a sym link without specifying any link path",
source: "sample.txt",
}, {
name: "Create a sym link without specifying any source",
link: "0.txt",
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rw := realPostWriter{}
rw.CreateDirWithSymlink(tt.source, tt.link)
if tt.source != "" {
defer os.Remove(tt.source)
if _, err := os.Stat(tt.source); err != nil {
t.Fatalf("Failed to create a file %q", tt.source)
}
}
if tt.source != "" && tt.link != "" {
defer os.Remove(tt.link)
if _, err := os.Stat(tt.link); err != nil {
t.Fatalf("Failed to create a sym link %q", tt.link)
}
}
})
}
}
46 changes: 46 additions & 0 deletions cmd/entrypoint/subcommands/step_init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package subcommands

import (
"log"
"os"
"path/filepath"
"strconv"
)

// StepInitCommand is the name of the /tekton/steps initialization command.
const StepInitCommand = "step-init"

var (
// root is the location of the Tekton root directory.
// Included as a global variable to allow overriding for tests.
tektonRoot = "/tekton"
)

// stepInit sets up the /tekton/steps directory for the pod.
// This expects the list of steps (in order matching the Task spec).
func stepInit(steps []string) error {
// Setup step directory symlinks - step data is written to a /tekton/run/<step>/status
// folder corresponding to each step - this is only mounted RW for the matching user step
// (and RO for all other steps).
// /tekton/steps provides a convenience symlink so that Tekton utilities to reference steps
// by name or index.
// NOTE: /tekton/steps may be removed in the future. Prefer using /tekton/run directly if
// possible.

// Create directory if it doesn't already exist
stepDir := filepath.Join(tektonRoot, "steps")
if err := os.MkdirAll(stepDir, os.ModePerm); err != nil {
log.Fatalf("Error creating steps directory %q: %v", stepDir, err)
}

for i, s := range steps {
run := filepath.Join(tektonRoot, "run", strconv.Itoa(i), "status")
if err := os.Symlink(run, filepath.Join(stepDir, s)); err != nil {
return err
}
if err := os.Symlink(run, filepath.Join(stepDir, strconv.Itoa(i))); err != nil {
return err
}
}
return nil
}
63 changes: 63 additions & 0 deletions cmd/entrypoint/subcommands/step_init_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package subcommands

import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)

func TestStepInit(t *testing.T) {
tmp, err := ioutil.TempDir("", "step-init-*")
if err != nil {
t.Fatalf("error creating temp directory: %v", err)
}
defer os.RemoveAll(tmp)

// Override tektonRoot for testing.
tektonRoot = tmp

// Create step directory so that symlinks can be successfully created.
// This is typically done by volume mounts, so it needs to be done manually
// in tests.
stepDir := filepath.Join(tmp, "steps")
if err := os.Mkdir(stepDir, os.ModePerm); err != nil {
t.Fatalf("error creating step directory: %v", err)
}

steps := []string{"a", "b"}
if err := stepInit(steps); err != nil {
t.Fatalf("stepInit: %v", err)
}

// Map of symlinks to expected /tekton/run folders.
// Expected format:
// Key: /tekton/steps/<key>
// Value: /tekton/run/<value>/status
wantLinks := map[string]string{
"a": "0",
"0": "0",
"b": "1",
"1": "1",
}

direntry, err := os.ReadDir(stepDir)
if err != nil {
t.Fatalf("os.ReadDir: %v", err)
}
for _, de := range direntry {
t.Run(de.Name(), func(t *testing.T) {
l, err := os.Readlink(filepath.Join(stepDir, de.Name()))
if err != nil {
t.Fatal(err)
}
want, ok := wantLinks[de.Name()]
if !ok {
t.Fatalf("unexpected symlink: %s", de.Name())
}
if wantDir := filepath.Join(tmp, "run", want, "status"); l != wantDir {
t.Errorf("want %s, got %s", wantDir, l)
}
})
}
}
5 changes: 5 additions & 0 deletions cmd/entrypoint/subcommands/subcommands.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ func Process(args []string) error {
}
return SubcommandSuccessful{message: fmt.Sprintf("Decoded script %s", src)}
}
case StepInitCommand:
if err := stepInit(args[1:]); err != nil {
return SubcommandError{subcommand: StepInitCommand, message: err.Error()}
}
return SubcommandSuccessful{message: "Setup /step directories"}
default:
}
return nil
Expand Down
15 changes: 15 additions & 0 deletions cmd/entrypoint/subcommands/subcommands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ func TestProcessSuccessfulSubcommands(t *testing.T) {
if _, ok := returnValue.(SubcommandSuccessful); !ok {
t.Errorf("unexpected return value from decode-script command: %v", returnValue)
}

t.Run(StepInitCommand, func(t *testing.T) {
wlynch marked this conversation as resolved.
Show resolved Hide resolved
tektonRoot = tmp

returnValue := Process([]string{StepInitCommand})
if _, ok := returnValue.(SubcommandSuccessful); !ok {
t.Errorf("unexpected return value from step-init command: %v", returnValue)
}

returnValue = Process([]string{StepInitCommand, "foo", "bar"})
if _, ok := returnValue.(SubcommandSuccessful); !ok {
t.Errorf("unexpected return value from step-init command w/ params: %v", returnValue)
}
})

}

// TestProcessIgnoresNonSubcommands checks that any input to Process which
Expand Down
Loading