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

Implement kustomize localize localization layer #4797

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
14 changes: 14 additions & 0 deletions api/internal/localizer/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package localizer

import (
"sigs.k8s.io/kustomize/kyaml/errors"
)

var (
ErrInvalidRoot = errors.Errorf("invalid root reference")
ErrLocalizeDirExists = errors.Errorf("'%s' localize directory already exists", LocalizeDir)
ErrNoRef = errors.Errorf("localize remote root missing ref query string parameter")
)
51 changes: 30 additions & 21 deletions api/internal/localizer/locloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/git"
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
Expand All @@ -21,7 +22,8 @@ type LocArgs struct {
// target; local copy if remote
Target filesys.ConfirmedDir

// directory that bounds target's local references, empty string if target is remote
// directory that bounds target's local references
// repo directory of local copy if target is remote
Scope filesys.ConfirmedDir

// localize destination
Expand Down Expand Up @@ -49,8 +51,7 @@ func NewLocLoader(targetArg string, scopeArg string, newDirArg string, fSys file
// check earlier to avoid cleanup
repoSpec, err := git.NewRepoSpecFromURL(targetArg)
if err == nil && repoSpec.Ref == "" {
return nil, LocArgs{},
errors.Errorf("localize remote root '%s' missing ref query string parameter", targetArg)
return nil, LocArgs{}, errors.Errorf("%w: '%s'", ErrNoRef, targetArg)
}

// for security, should enforce load restrictions
Expand All @@ -76,11 +77,12 @@ func NewLocLoader(targetArg string, scopeArg string, newDirArg string, fSys file
Scope: scope,
NewDir: newDir,
}
_, isRemote := ldr.Repo()
return &locLoader{
fSys: fSys,
args: &args,
Loader: ldr,
local: scope != "",
local: !isRemote,
}, args, nil
}

Expand All @@ -95,19 +97,18 @@ func (ll *locLoader) Load(path string) ([]byte, error) {
if filepath.IsAbs(path) {
return nil, errors.Errorf("absolute paths not yet supported in alpha: file path '%s' is absolute", path)
}
if ll.local {
if !loader.HasRemoteFileScheme(path) && ll.local {
abs := filepath.Join(ll.Root(), path)
dir, f, err := ll.fSys.CleanedAbs(abs)
if err != nil {
// should never happen
log.Fatalf(errors.WrapPrefixf(err, "cannot clean validated file path '%s'", abs).Error())
}
// target cannot reference newDir, as this load would've failed prior to localize;
// not a problem if remote because then reference could only be in newDir if repo copy,
// which will be cleaned, is inside newDir
if dir.HasPrefix(ll.args.NewDir) {
return nil, errors.Errorf(
"file path '%s' references into localize destination '%s'", dir.Join(f), ll.args.NewDir)
return nil, errors.Errorf("invalid reference '%s': file at '%s' references into localize destination '%s'",
path, dir.Join(f), ll.args.NewDir)
}
}
return content, nil
Expand All @@ -116,24 +117,32 @@ func (ll *locLoader) Load(path string) ([]byte, error) {
// New returns a Loader at path if path is a valid localize root.
// Otherwise, New returns an error.
func (ll *locLoader) New(path string) (ifc.Loader, error) {
repoSpec, err := git.NewRepoSpecFromURL(path)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note to self: check invalid if remote file passed in

if err == nil && repoSpec.Ref == "" {
return nil, errors.Errorf("localize remote root '%s' missing ref query string parameter", path)
}

ldr, err := ll.Loader.New(path)
// timeout indicates path is a root, not a file
if utils.IsErrTimeout(err) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note to self: Add TODO statement to refactor once appropriate errors added to ifc.Loader

if !hasRef(path) {
return nil, errors.Errorf("%w: '%s'", ErrNoRef, path)
}
return nil, err
}
// otherwise, invalid root; upper layer should try file
if err != nil {
return nil, errors.WrapPrefixf(err, "invalid root reference")
return nil, errors.Errorf("%w: %s", ErrInvalidRoot, err.Error())
}

var isRemote bool
if _, isRemote = ldr.Repo(); !isRemote {
if ll.local && !filesys.ConfirmedDir(ldr.Root()).HasPrefix(ll.args.Scope) {
return nil, errors.Errorf("root '%s' outside localize scope '%s'", ldr.Root(), ll.args.Scope)
_, isRemote := ldr.Repo()
if isRemote {
if !hasRef(path) {
return nil, errors.Errorf("%w: '%s'", ErrNoRef, path)
}
} else if ll.local {
if !filesys.ConfirmedDir(ldr.Root()).HasPrefix(ll.args.Scope) {
return nil, errors.Errorf("invalid reference '%s': root at '%s' outside localize scope '%s'",
path, ldr.Root(), ll.args.Scope)
}
if ll.local && filesys.ConfirmedDir(ldr.Root()).HasPrefix(ll.args.NewDir) {
return nil, errors.Errorf(
"root '%s' references into localize destination '%s'", ldr.Root(), ll.args.NewDir)
if filesys.ConfirmedDir(ldr.Root()).HasPrefix(ll.args.NewDir) {
return nil, errors.Errorf("invalid reference '%s': root at '%s' references into localize destination '%s'",
path, ldr.Root(), ll.args.NewDir)
}
}

Expand Down
35 changes: 27 additions & 8 deletions api/internal/localizer/locloader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,23 +257,42 @@ func TestNewFails(t *testing.T) {
req.NoError(err)
checkNewLocLoader(req, ldr, &args, "/alpha/beta/gamma", "/alpha", "/alpha/beta/gamma/newDir", fSys)

cases := map[string]string{
"outside scope": "../../../a",
"at dst": "newDir",
"ancestor": "../../beta",
"non-existent root": "delt",
"file": "delta/kustomization.yaml",
cases := map[string]*struct {
arg string

// whether error is specific to localize
localizeError bool
}{
"outside scope": {
"../../../a",
true,
},
"at dst": {
"newDir",
true,
},
"ancestor": {
arg: "../../beta",
},
"non-existent root": {
arg: "delt",
},
"file": {
arg: "delta/deployment.yaml",
},
}
for name, root := range cases {
root := root
t.Run(name, func(t *testing.T) {
fSys := makeMemoryFs(t)

ldr, _, err := lclzr.NewLocLoader("/alpha/beta/gamma", "alpha", "alpha/beta/gamma/newDir", fSys)
require.NoError(t, err)

_, err = ldr.New(root)
_, err = ldr.New(root.arg)
require.Error(t, err)
if !root.localizeError {
require.ErrorIs(t, err, lclzr.ErrInvalidRoot)
}
})
}
}
Expand Down
14 changes: 12 additions & 2 deletions api/internal/localizer/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import (
"sigs.k8s.io/kustomize/kyaml/filesys"
)

const LocalizeDir = "localized-files"

// establishScope returns the scope given localize arguments and targetLdr at targetArg
func establishScope(scopeArg string, targetArg string, targetLdr ifc.Loader, fSys filesys.FileSystem) (filesys.ConfirmedDir, error) {
if _, isRemote := targetLdr.Repo(); isRemote {
if repo, isRemote := targetLdr.Repo(); isRemote {
if scopeArg != "" {
return "", errors.Errorf("scope '%s' specified for remote localize target '%s'", scopeArg, targetArg)
}
return "", nil
return filesys.ConfirmedDir(repo), nil
}
// default scope
if scopeArg == "" {
Expand Down Expand Up @@ -80,6 +82,14 @@ func defaultNewDir(targetLdr ifc.Loader, spec *git.RepoSpec) string {
return strings.Join([]string{dstPrefix, targetDir}, "-")
}

func hasRef(path string) bool {
repoSpec, err := git.NewRepoSpecFromURL(path)
if err != nil {
log.Fatalf("%s: %s", "unable to parse validated root url", err.Error())
}
return repoSpec.Ref != ""
}

// urlBase is the url equivalent of filepath.Base
func urlBase(url string) string {
cleaned := strings.TrimRight(url, "/")
Expand Down