Skip to content

Improve skaffold init file traversal #3062

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

Merged
merged 9 commits into from
Oct 21, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
91 changes: 62 additions & 29 deletions pkg/skaffold/initializer/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"sort"
"strings"

"github.com/karrick/godirwalk"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"gopkg.in/AlecAivazis/survey.v1"
Expand Down Expand Up @@ -110,7 +111,7 @@ func DoInit(ctx context.Context, out io.Writer, c Config) error {
}
}

potentialConfigs, builderConfigs, err := walk(rootDir, c.Force, c.EnableJibInit, detectBuilders)
potentialConfigs, builderConfigs, err := walk(rootDir, c.Force, c.EnableJibInit)
if err != nil {
return err
}
Expand Down Expand Up @@ -242,7 +243,10 @@ func autoSelectBuilders(builderConfigs []InitBuilder, images []string) ([]builde
return pairs, builderConfigs, unresolvedImages
}

func detectBuilders(enableJibInit bool, path string) ([]InitBuilder, error) {
// detectBuilders checks if a path is a builder config, and if it is, returns the InitBuilders representing the
// configs. Also returns a boolean marking whether search completion for subdirectories (true = subdirectories should
// continue to be searched, false = subdirectories should not be searched for more builders)
func detectBuilders(enableJibInit bool, path string) ([]InitBuilder, bool) {
// TODO: Remove backwards compatibility if statement (not entire block)
if enableJibInit {
// Check for jib
Expand All @@ -251,19 +255,19 @@ func detectBuilders(enableJibInit bool, path string) ([]InitBuilder, error) {
for i := range builders {
results[i] = builders[i]
}
return results, filepath.SkipDir
return results, false
}
}

// Check for Dockerfile
if docker.ValidateDockerfileFunc(path) {
results := []InitBuilder{docker.Docker{File: path}}
return results, nil
return results, true
}

// TODO: Check for more builders

return nil, nil
return nil, true
}

func processCliArtifacts(artifacts []string) ([]builderImagePair, error) {
Expand Down Expand Up @@ -538,38 +542,67 @@ func printAnalyzeJSON(out io.Writer, skipBuild bool, pairs []builderImagePair, u
return err
}

func walk(dir string, force, enableJibInit bool, validateBuildFile func(bool, string) ([]InitBuilder, error)) ([]string, []InitBuilder, error) {
func walk(dir string, force, enableJibInit bool) ([]string, []InitBuilder, error) {
var potentialConfigs []string
var foundBuilders []InitBuilder
err := filepath.Walk(dir, func(path string, f os.FileInfo, e error) error {
if f.IsDir() && util.IsHiddenDir(f.Name()) {
logrus.Debugf("skip walking hidden dir %s", f.Name())
return filepath.SkipDir
}
if f.IsDir() || util.IsHiddenFile(f.Name()) {
return nil

var dirCallback func(path string, findBuilders bool) error
dirCallback = func(path string, findBuilders bool) error {
dirents, err := godirwalk.ReadDirents(path, nil)
if err != nil {
return err
}
if IsSkaffoldConfig(path) {
if !force {
return fmt.Errorf("pre-existing %s found", path)

var directories []*godirwalk.Dirent
findBuildersInDirectories := true
sort.Sort(dirents)

// Traverse files
for _, file := range dirents {
if util.IsHiddenFile(file.Name()) || util.IsHiddenDir(file.Name()) {
continue
}

// If we found a directory, keep track of it until we've gone through all the files first
if file.IsDir() {
directories = append(directories, file)
continue
}

filePath := filepath.Join(path, file.Name())
if IsSkaffoldConfig(filePath) {
if !force {
return fmt.Errorf("pre-existing %s found", filePath)
}
logrus.Debugf("%s is a valid skaffold configuration: continuing since --force=true", filePath)
continue
}

if IsSupportedKubernetesFileExtension(filePath) {
potentialConfigs = append(potentialConfigs, filePath)
continue
}

// try and parse build file
if findBuilders {
var builderConfigs []InitBuilder
builderConfigs, findBuildersInDirectories = detectBuilders(enableJibInit, filePath)
foundBuilders = append(foundBuilders, builderConfigs...)
}
logrus.Debugf("%s is a valid skaffold configuration: continuing since --force=true", path)
return nil
}
if IsSupportedKubernetesFileExtension(path) {
potentialConfigs = append(potentialConfigs, path)
return nil
}
// try and parse build file
if builderConfigs, err := validateBuildFile(enableJibInit, path); builderConfigs != nil {
for _, buildConfig := range builderConfigs {
logrus.Infof("existing builder found: %s", buildConfig.Describe())
foundBuilders = append(foundBuilders, buildConfig)

// Traverse into subdirectories
for _, dir := range directories {
err = dirCallback(filepath.Join(path, dir.Name()), findBuildersInDirectories)
if err != nil {
return err
}
return err
}

return nil
})
}

err := dirCallback(dir, true)
if err != nil {
return nil, nil, err
}
Expand Down
62 changes: 53 additions & 9 deletions pkg/skaffold/initializer/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ func TestWalk(t *testing.T) {
},
force: false,
expectedConfigs: []string{
"config/test.yaml",
"k8pod.yml",
"config/test.yaml",
},
expectedPaths: []string{
"Dockerfile",
Expand All @@ -166,8 +166,8 @@ func TestWalk(t *testing.T) {
force: false,
enableJibInit: true,
expectedConfigs: []string{
"config/test.yaml",
"k8pod.yml",
"config/test.yaml",
},
expectedPaths: []string{
"Dockerfile",
Expand All @@ -184,21 +184,44 @@ func TestWalk(t *testing.T) {
"k8pod.yml": emptyFile,
"gradle/build.gradle": emptyFile,
"gradle/subproject/build.gradle": emptyFile,
"maven/asubproject/pom.xml": emptyFile,
"maven/pom.xml": emptyFile,
"maven/subproject/pom.xml": emptyFile,
},
force: false,
enableJibInit: true,
expectedConfigs: []string{
"config/test.yaml",
"k8pod.yml",
"config/test.yaml",
},
expectedPaths: []string{
"gradle/build.gradle",
"maven/pom.xml",
},
shouldErr: false,
},
{
description: "multiple builders in same directory",
filesWithContents: map[string]string{
"build.gradle": emptyFile,
"ignored-builder/build.gradle": emptyFile,
"not-ignored-config/test.yaml": emptyFile,
"Dockerfile": emptyFile,
"k8pod.yml": emptyFile,
"pom.xml": emptyFile,
},
force: false,
enableJibInit: true,
expectedConfigs: []string{
"k8pod.yml",
"not-ignored-config/test.yaml",
},
expectedPaths: []string{
"Dockerfile",
"build.gradle",
"pom.xml",
},
shouldErr: false,
},
{
description: "should skip hidden dir",
filesWithContents: map[string]string{
Expand Down Expand Up @@ -234,8 +257,8 @@ deploy:
force: true,
enableJibInit: true,
expectedConfigs: []string{
"config/test.yaml",
"k8pod.yml",
"config/test.yaml",
},
expectedPaths: []string{
"Dockerfile",
Expand All @@ -244,7 +267,7 @@ deploy:
shouldErr: false,
},
{
description: "should error when skaffold.config present and force = false",
description: "should error when skaffold.config present and force = false",
filesWithContents: map[string]string{
"config/test.yaml": emptyFile,
"k8pod.yml": emptyFile,
Expand All @@ -253,6 +276,24 @@ deploy:
"Dockerfile": emptyFile,
"skaffold.yaml": `apiVersion: skaffold/v1beta6
kind: Config
deploy:
kustomize: {}`,
},
force: false,
enableJibInit: true,
expectedConfigs: nil,
expectedPaths: nil,
shouldErr: true,
},
{
description: "should error when skaffold.config present with jib config",
filesWithContents: map[string]string{
"config/test.yaml": emptyFile,
"k8pod.yml": emptyFile,
"README": emptyFile,
"pom.xml": emptyFile,
"skaffold.yaml": `apiVersion: skaffold/v1beta6
kind: Config
deploy:
kustomize: {}`,
},
Expand All @@ -265,15 +306,18 @@ deploy:
}
for _, test := range tests {
testutil.Run(t, test.description, func(t *testutil.T) {
tmpDir := t.NewTempDir().
WriteFiles(test.filesWithContents)
tmpDir := t.NewTempDir().WriteFiles(test.filesWithContents)

t.Override(&docker.ValidateDockerfileFunc, fakeValidateDockerfile)
t.Override(&jib.ValidateJibConfigFunc, fakeValidateJibConfig)

potentialConfigs, builders, err := walk(tmpDir.Root(), test.force, test.enableJibInit, detectBuilders)
potentialConfigs, builders, err := walk(tmpDir.Root(), test.force, test.enableJibInit)

t.CheckError(test.shouldErr, err)
if test.shouldErr {
return
}

t.CheckDeepEqual(tmpDir.Paths(test.expectedConfigs...), potentialConfigs)
t.CheckDeepEqual(len(test.expectedPaths), len(builders))
for i := range builders {
Expand Down