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

Fixes #5404: Skaffold configs downloaded from a url can define remote config dependencies #5405

Merged
merged 7 commits into from
Feb 19, 2021

Conversation

gsquared94
Copy link
Contributor

Fixes #5404

This PR makes two changes:

  • Fix the config parsing logic that was erroneously checking that a dependency config file exists on disk even if it's a URL.
  • Add missing check that multiple configs in the same file cannot have the same name.

@gsquared94 gsquared94 requested a review from a team as a code owner February 17, 2021 04:13
@google-cla google-cla bot added the cla: yes label Feb 17, 2021
@gsquared94 gsquared94 changed the title fix: #5404 Fixes #5404: Skaffold configs downloaded from a url can define remote configs Feb 17, 2021
@gsquared94 gsquared94 requested a review from nkubala February 17, 2021 04:15
@gsquared94 gsquared94 changed the title Fixes #5404: Skaffold configs downloaded from a url can define remote configs Fixes #5404: Skaffold configs downloaded from a url can define remote config dependencies Feb 17, 2021
@codecov
Copy link

codecov bot commented Feb 17, 2021

Codecov Report

Merging #5405 (bbaf14f) into master (73d60cb) will decrease coverage by 0.14%.
The diff coverage is 75.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5405      +/-   ##
==========================================
- Coverage   71.57%   71.43%   -0.15%     
==========================================
  Files         397      399       +2     
  Lines       14434    14516      +82     
==========================================
+ Hits        10331    10369      +38     
- Misses       3335     3380      +45     
+ Partials      768      767       -1     
Impacted Files Coverage Δ
cmd/skaffold/app/cmd/parse_config.go 78.33% <75.00%> (+6.90%) ⬆️
pkg/skaffold/schema/versions.go 82.41% <0.00%> (ø)
pkg/skaffold/schema/v2beta12/config.go 20.58% <0.00%> (ø)
pkg/skaffold/schema/v2beta12/upgrade.go 100.00% <0.00%> (ø)
pkg/skaffold/util/tar.go 56.00% <0.00%> (+5.33%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 73d60cb...bbaf14f. Read the comment docs.

return nil, fmt.Errorf("skaffold config named %q found in multiple files: %q and %q", config.Metadata.Name, prevConfig, cfgOpts.file)
prev, found := r.configNameToFile[config.Metadata.Name]
if found {
sl := strings.SplitN(prev, "@", 2) // map value is always formatted as `file_name@config_index`
Copy link
Member

Choose a reason for hiding this comment

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

This overloading of configNameToFile seems error-prone (@ is a valid character in file names). It also seems unnecessary since it's entirely our code: we could just have the map take stringstruct { name, index }. But more to the point, we don't seem to use this index anywhere else, so couldn't this search-for-duplicates just use a local variable to maintain its state?

And maybe this search-for-duplicates should be done separately?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

don't know what I was doing here 🤦🏽‍♂️. Removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't effectively maintain all info in a local variable since we also want to print out the file names that cause the conflict.

@@ -108,11 +108,18 @@ func getConfigs(cfgOpts configOpts, opts config.SkaffoldOptions, r *record) ([]*
func processEachConfig(config *latest.SkaffoldConfig, cfgOpts configOpts, opts config.SkaffoldOptions, r *record, index int) ([]*latest.SkaffoldConfig, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Would be helpful to describe what index represents here. (And it'sits)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed typo; added description

Comment on lines 115 to 120
if prevConfig != cfgOpts.file {
return nil, fmt.Errorf("skaffold config named %q found in multiple files: %q and %q", config.Metadata.Name, prevConfig, cfgOpts.file)
}
if prevIndex != fmt.Sprint(index) {
return nil, fmt.Errorf("multiple skaffold configs named %q found in file %q", config.Metadata.Name, cfgOpts.file)
}
Copy link
Member

Choose a reason for hiding this comment

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

Can duplicate references not be handled by ParseConfigAndUpgrade()? So all we'd need is a set of seen config names?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can't exactly be inside ParseConfigAndUpgrade() since that works with VersionedConfig which doesn't have any method like GetName. To make that change I'd have to modify every version of the skaffold config to satisfy that interface. But you're right in that I can do this check upfront. Moved.

if !util.IsURL(path) {
fi, err := os.Stat(path)
if err != nil {
if os.IsNotExist(errors.Unwrap(err)) {
Copy link
Member

Choose a reason for hiding this comment

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

The unwrap isn't correct: it's possible the ErrNoExist is actually a wrapped error. See the example at
https://golang.org/pkg/os/#IsNotExist

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a weird one. It was existing code so I didn't mess around with it. It's calculating that the FileNotFound error from ReadConfiguration is wrapped exactly once in ParseConfig. So it unwraps exactly once to get the underlying error. 🤦🏽‍♂️

I'll convert this to a concrete error type.

Copy link
Member

Choose a reason for hiding this comment

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

The code for IsNotExist notes:

New code should use errors.Is(err, os.ErrNotExist)

errors.Is() will unwrap the error sequentially.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh, got it. Fixed

return nil, fmt.Errorf("parsing dependencies for skaffold config %s: %w", cfgOpts.file, err)
}
if fi.IsDir() {
path = filepath.Join(path, "skaffold.yaml")
}
cfgOpts.isDependency = cfgOpts.isDependency || path != cfgOpts.file
Copy link
Member

Choose a reason for hiding this comment

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

This line is a bit puzzling and warrants a comment as I'm not sure how this situation can arise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added

Copy link
Member

Choose a reason for hiding this comment

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

What's previous and current? Oh! Is configOpts from the config that required d as a dependency? That was not clear.

Comment on lines 444 to 448
if test.err != "" {
t.CheckDeepEqual(errors.New(strings.ReplaceAll(test.err, "WORK_DIR", wd)), err, cmp.Comparer(errorsComparer))
} else {
t.CheckDeepEqual(test.expected, cfgs)
}
Copy link
Member

Choose a reason for hiding this comment

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

Might be easier to have an duplicateError type, and instead use a function to check the correctness of the error.

Better yet: create an error-code for this situation and test that error code. This should be reported upwards.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will make this change in fixing #5412

Copy link
Member

Choose a reason for hiding this comment

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

The tests are failing as you're getting windows path separators. Maybe you need to replace /\ too in the WORKDIR references.

@gsquared94 gsquared94 added this to the v1.21.0-rc milestone Feb 18, 2021
Copy link
Member

@briandealwis briandealwis left a comment

Choose a reason for hiding this comment

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

LGTM once the tests pass on Windows.

// validate that config names are unique if specified
seen := make(map[string]bool)
for _, cfg := range parsed {
cfgName := cfg.(*latest.SkaffoldConfig).Metadata.Name
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should have a ConfigName() method on VersionedConfig?

return nil, fmt.Errorf("parsing dependencies for skaffold config %s: %w", cfgOpts.file, err)
}
if fi.IsDir() {
path = filepath.Join(path, "skaffold.yaml")
}
cfgOpts.isDependency = cfgOpts.isDependency || path != cfgOpts.file
Copy link
Member

Choose a reason for hiding this comment

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

What's previous and current? Oh! Is configOpts from the config that required d as a dependency? That was not clear.

Comment on lines 444 to 448
if test.err != "" {
t.CheckDeepEqual(errors.New(strings.ReplaceAll(test.err, "WORK_DIR", wd)), err, cmp.Comparer(errorsComparer))
} else {
t.CheckDeepEqual(test.expected, cfgs)
}
Copy link
Member

Choose a reason for hiding this comment

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

The tests are failing as you're getting windows path separators. Maybe you need to replace /\ too in the WORKDIR references.

continue
}
if seen[cfgName] {
return nil, fmt.Errorf("multiple skaffold configs named %q found in file %q", cfgName, cfgOpts.file)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe seen should be a map of name → file? Then you could include at least two of the file nams.

@gsquared94 gsquared94 merged commit 5701c38 into GoogleContainerTools:master Feb 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Providing a url to -f flag doesn't work with remote configs
2 participants