Add accumulateResources error tests for local files (#5225)

* Add accumulateResources error tests for local files.

Add tests demonstrating accumulateResources errors when the resource is
a local file. Works to address #4807.

* Improve readability
This commit is contained in:
Anna Song
2023-07-01 11:46:49 -07:00
committed by GitHub
parent 878cda7c55
commit da4e881007
3 changed files with 165 additions and 54 deletions

View File

@@ -17,6 +17,18 @@ import (
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
) )
const validResource = `
apiVersion: v1
kind: Service
metadata:
name: myService
spec:
selector:
backend: bungie
ports:
- port: 7002
`
func TestTargetMustHaveKustomizationFile(t *testing.T) { func TestTargetMustHaveKustomizationFile(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
th.WriteF("service.yaml", ` th.WriteF("service.yaml", `
@@ -63,17 +75,7 @@ func TestBaseMustHaveKustomizationFile(t *testing.T) {
resources: resources:
- base - base
`) `)
th.WriteF("base/service.yaml", ` th.WriteF("base/service.yaml", validResource)
apiVersion: v1
kind: Service
metadata:
name: myService
spec:
selector:
backend: bungie
ports:
- port: 7002
`)
err := th.RunWithErr(".", th.MakeDefaultOptions()) err := th.RunWithErr(".", th.MakeDefaultOptions())
if err == nil { if err == nil {
t.Fatalf("expected an error") t.Fatalf("expected an error")
@@ -167,12 +169,35 @@ spec:
func TestAccumulateResourcesErrors(t *testing.T) { func TestAccumulateResourcesErrors(t *testing.T) {
type testcase struct { type testcase struct {
name string name string
resource string resource string
// regex error message that is output when kustomize tries to isAbsolute bool
// accumulate resource as file and dir, respectively files map[string]string
// errFile, errDir are regex for the expected error message output
// when kustomize tries to accumulate resource as file and dir,
// respectively. The test substitutes occurrences of "%s" in the
// error strings with the absolute path where kustomize looks for it.
errFile, errDir string errFile, errDir string
} }
populateAbsolutePaths := func(tc testcase, dir string) testcase {
filePaths := make(map[string]string, len(tc.files)+1)
for file, content := range tc.files {
filePaths[filepath.Join(dir, file)] = content
}
resourcePath := filepath.Join(dir, tc.resource)
if tc.isAbsolute {
tc.resource = resourcePath
}
filePaths[filepath.Join(dir, "kustomization.yaml")] = fmt.Sprintf(`
resources:
- %s
`, tc.resource)
tc.files = filePaths
regPath := regexp.QuoteMeta(resourcePath)
tc.errFile = strings.ReplaceAll(tc.errFile, "%s", regPath)
tc.errDir = strings.ReplaceAll(tc.errDir, "%s", regPath)
return tc
}
buildError := func(tc testcase) string { buildError := func(tc testcase) string {
const ( const (
prefix = "accumulating resources" prefix = "accumulating resources"
@@ -196,39 +221,89 @@ func TestAccumulateResourcesErrors(t *testing.T) {
for _, test := range []testcase{ for _, test := range []testcase{
{ {
name: "remote file not considered repo", name: "remote file not considered repo",
// This url is too short to be considered a remote repo. // The example.com second-level domain is reserved and
resource: "https://raw.githubusercontent.com/kustomize", // safe to access, see RFC 2606.
// It is acceptable that the error for a remote file-like reference resource: "https://example.com/segments-too-few-to-be-repo",
// (that is not long enough to be considered a repo) does not // It's acceptable for the error output of a remote file-like
// indicate said reference is not a local directory. // resource to not indicate the resource's status as a
// Though it is possible for the remote file-like reference to be // local directory. Though it is possible for a remote file-like
// a local directory, it is very unlikely. // resource to be a local directory, it is very unlikely.
errFile: `HTTP Error: status code 400 \(Bad Request\)\z`, errFile: `HTTP Error: status code 404 \(Not Found\)\z`,
}, },
{ {
name: "remote file qualifies as repo", name: "remote file qualifies as repo",
resource: "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/kustomize/v5.0.0/examples/helloWorld/configMap", resource: "https://example.com/long/enough/to/have/org/and/repo",
// TODO(4788): This error message is technically wrong. Just // TODO(4788): This error message is technically wrong. Just
// because we fail to GET a reference does not mean the reference is // because we fail to GET a resource does not mean the resource is
// not a remote file. We should return the GET status code instead. // not a remote file. We should return the GET status code as well.
errFile: "URL is a git repository", errFile: "URL is a git repository",
errDir: `failed to run \S+/git fetch --depth=1 .+`, errDir: `failed to run \S+/git fetch --depth=1 .+`,
}, },
{
name: "local file qualifies as repo",
// The .example top level domain is reserved for example purposes,
// see RFC 2606.
resource: "package@v1.28.0.example/configs/base",
errFile: `evalsymlink failure on '%s' .+`,
errDir: `failed to run \S+/git fetch --depth=1 .+`,
},
{
name: "relative path does not exist",
resource: "file-or-directory",
errFile: `evalsymlink failure on '%s' .+`,
errDir: `must build at directory: not a valid directory: evalsymlink failure .+`,
},
{
name: "absolute path does not exist",
resource: "file-or-directory",
isAbsolute: true,
errFile: `evalsymlink failure on '%s' .+`,
errDir: `new root '%s' cannot be absolute`,
},
{
name: "relative file violates restrictions",
resource: "../base/resource.yaml",
files: map[string]string{
"../base/resource.yaml": validResource,
},
errFile: "security; file '%s' is not in or below .+",
// TODO(4348): Over-inclusion of directory error message when we
// know resource is file.
errDir: "must build at directory: '%s': file is not directory",
},
{
name: "absolute file violates restrictions",
resource: "../base/resource.yaml",
isAbsolute: true,
files: map[string]string{
"../base/resource.yaml": validResource,
},
errFile: "security; file '%s' is not in or below .+",
// TODO(4348): Over-inclusion of directory error message when we
// know resource is file.
errDir: `new root '%s' cannot be absolute`,
},
} { } {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
// should use real file system to indicate that we are creating // Should use real file system to indicate that we are creating
// new temporary directories on disk when we attempt to fetch repos // new temporary directories on disk when we attempt to fetch repos.
fSys, tmpDir := kusttest_test.CreateKustDir(t, fmt.Sprintf(` fs, tmpDir := kusttest_test.Setup(t)
resources: root := tmpDir.Join("root")
- %s require.NoError(t, fs.Mkdir(root))
`, test.resource))
test = populateAbsolutePaths(test, root)
for file, content := range test.files {
dir := filepath.Dir(file)
require.NoError(t, fs.MkdirAll(dir))
require.NoError(t, fs.WriteFile(file, []byte(content)))
}
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
_, err := b.Run(fSys, tmpDir.String()) _, err := b.Run(fs, root)
require.Regexp(t, buildError(test), err.Error()) require.Regexp(t, buildError(test), err.Error())
}) })
} }
// TODO(annasong): add tests that check accumulateResources errors for // TODO(annasong): add tests that check accumulateResources errors for
// - local files
// - repos // - repos
// - local directories // - local directories
// - files that yield malformed yaml errors // - files that yield malformed yaml errors

View File

@@ -31,6 +31,11 @@ func TestIsRemoteFile(t *testing.T) {
"https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/helloWorld/configMap.yaml", "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/helloWorld/configMap.yaml",
true, true,
}, },
"malformed https": {
// TODO(annasong): Maybe we want to fix this. Needs more research.
"https:/raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/helloWorld/configMap.yaml",
true,
},
"https dir": { "https dir": {
"https://github.com/kubernetes-sigs/kustomize//examples/helloWorld/", "https://github.com/kubernetes-sigs/kustomize//examples/helloWorld/",
true, true,
@@ -201,23 +206,44 @@ func TestNewEmptyLoader(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
func TestNewLoaderHTTP(t *testing.T) { func TestNewRemoteLoaderDoesNotExist(t *testing.T) {
t.Run("doesn't exist", func(t *testing.T) { _, err := makeLoader().New("https://example.com/org/repo")
_, err := makeLoader().New("https://google.com/project") require.ErrorContains(t, err, "fetch")
require.Error(t, err) }
})
// Though it is unlikely and we do not weigh this use case in our designs, func TestLoaderLocalScheme(t *testing.T) {
// it is possible for a reference that is considered a remote file to // It is unlikely but possible for a reference with a url scheme to
// actually be a directory // actually refer to a local file or directory.
t.Run("exists", func(t *testing.T) { t.Run("file", func(t *testing.T) {
const remoteFileLikeRoot = "https://domain"
require.True(t, IsRemoteFile(remoteFileLikeRoot))
fSys, dir := setupOnDisk(t) fSys, dir := setupOnDisk(t)
require.NoError(t, fSys.MkdirAll(dir.Join("https:/domain"))) parts := []string{
"ssh:",
"resource.yaml",
}
require.NoError(t, fSys.Mkdir(dir.Join(parts[0])))
const content = "resource config"
require.NoError(t, fSys.WriteFile(
dir.Join(filepath.Join(parts...)),
[]byte(content),
))
actualContent, err := newLoaderOrDie(RestrictionRootOnly,
fSys,
dir.String(),
).Load(strings.Join(parts, "//"))
require.NoError(t, err)
require.Equal(t, content, string(actualContent))
})
t.Run("directory", func(t *testing.T) {
fSys, dir := setupOnDisk(t)
parts := []string{
"https:",
"root",
}
require.NoError(t, fSys.MkdirAll(dir.Join(filepath.Join(parts...))))
ldr, err := newLoaderOrDie(RestrictionRootOnly, ldr, err := newLoaderOrDie(RestrictionRootOnly,
fSys, fSys,
dir.String(), dir.String(),
).New(remoteFileLikeRoot) ).New(strings.Join(parts, "//"))
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, ldr.Repo()) require.Empty(t, ldr.Repo())
}) })
@@ -635,6 +661,8 @@ func TestLoaderHTTP(t *testing.T) {
// setupOnDisk sets up a file system on disk and directory that is cleaned after // setupOnDisk sets up a file system on disk and directory that is cleaned after
// test completion. // test completion.
// TODO(annasong): Move all loader tests that require real file system into
// api/krusty.
func setupOnDisk(t *testing.T) (filesys.FileSystem, filesys.ConfirmedDir) { func setupOnDisk(t *testing.T) (filesys.FileSystem, filesys.ConfirmedDir) {
t.Helper() t.Helper()

View File

@@ -11,19 +11,27 @@ import (
"sigs.k8s.io/kustomize/kyaml/filesys" "sigs.k8s.io/kustomize/kyaml/filesys"
) )
// Setup sets up a file system on disk and directory that is cleaned after
// test completion.
func Setup(t *testing.T) (filesys.FileSystem, filesys.ConfirmedDir) {
t.Helper()
fSys := filesys.MakeFsOnDisk()
dir, err := filesys.NewTmpConfirmedDir()
require.NoError(t, err)
t.Cleanup(func() {
_ = fSys.RemoveAll(dir.String())
})
return fSys, dir
}
// CreateKustDir creates a file system on disk and a new directory // CreateKustDir creates a file system on disk and a new directory
// that holds a kustomization file with content. The directory is removed on // that holds a kustomization file with content. The directory is removed on
// test completion. // test completion.
func CreateKustDir(t *testing.T, content string) (filesys.FileSystem, filesys.ConfirmedDir) { func CreateKustDir(t *testing.T, content string) (filesys.FileSystem, filesys.ConfirmedDir) {
t.Helper() t.Helper()
fSys := filesys.MakeFsOnDisk() fSys, tmpDir := Setup(t)
tmpDir, err := filesys.NewTmpConfirmedDir()
require.NoError(t, err)
require.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(content))) require.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(content)))
t.Cleanup(func() {
require.NoError(t, fSys.RemoveAll(tmpDir.String()))
})
return fSys, tmpDir return fSys, tmpDir
} }