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

Split templating from file IO operations #45

Merged
merged 9 commits into from
Nov 21, 2023

Conversation

atanasdinov
Copy link
Contributor

  • Initially aimed to add perms to fileio functions in order to drop unnecessary and prone to missing additional Chmod() calls
  • Led to splitting up the initial WriteFile implementation to two separate parts WriteFile for non-templated files and WriteTemplate for templated ones
  • WriteFile at that point was just a wrapper around os.WriteFile and WriteTemplate was doing too many things for a single function
  • As a result, the template package was extracted and reused where applicable and WriteFile was removed. This decouples templating from IO ops and further simplifies combustion components structure and testing

@jdob
Copy link
Contributor

jdob commented Nov 17, 2023

This is interesting because you trade programmatic convenience for pure decoupling. Admittedly, part of why I say that is because I still really don't like Go error handling, so having to recreate the two-step "apply template, write file" and handle the error each time feels cumbersome for the amount of components we're going to have that use that model on.

@atanasdinov
Copy link
Contributor Author

I'm not sure if I'll be able to pull it off (since we have so many moving pieces) but my goal is try and decouple the file handling as much as possible from the rest of the business logic. This way we should be able to confirm all modifications to scripts and templates are properly executed (with the necessary tests in place) and only then write these results to the FS (regardless if this is the BuildDir, the CombustionDir or anything else).

@@ -10,7 +10,6 @@ import (

const (
scriptsDir = "scripts"
scriptMode = 0o744
Copy link
Contributor

Choose a reason for hiding this comment

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

Glad you addressed this now. After the user stuff landed (where I ended up having a separate constant for the perms in that file) I was going to revisit this.

@@ -70,7 +70,7 @@ func TestWriteModifyScript(t *testing.T) {

stats, err := os.Stat(expectedFilename)
require.NoError(t, err)
assert.Equal(t, fs.FileMode(modifyScriptMode), stats.Mode())
assert.Equal(t, fs.FileMode(0o744), stats.Mode())
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think hardcoding the permissions is the right move here. What the test is ensuring is that the correct variable was used when it was run, so IMO we should be testing that the right variable was used regardless of what it actually contains. This bleeds abstractions from fileio into the raw test by hardcoding into here what's set in fileio. We simply care here that it was set with (what we call) executable params, not the specifics of what those are. The test for what those are should live in fileio.

if err != nil {
return "", fmt.Errorf("applying GRUB guestfish snippet: %w", err)
return "", fmt.Errorf("parsing guestfish template: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious why you took the "GRUB" portion out. The intention was that it provided more specific information on where/what the error was. If we reuse the generic "parsing guestfish template" error elsewhere, that'll make debugging (admittedly slightly) less descriptive.

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 used that because the name of the template variable is guestfishSnippet. No issues putting GRUB back in though, will fix.

pkg/build/raw.go Outdated
if err != nil {
return fmt.Errorf("writing modification script %s: %w", modifyScriptName, err)
return fmt.Errorf("parsing template: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to my other comment, I don't see the harm in including more information rather than less in an error message. The errors inside of template.Parse() don't give any details on what it was that failed, so including the name of the template being applied just makes our lives easier/quicker when it comes to debugging.

pkg/build/raw.go Outdated

filename := b.generateBuildDirFilename(modifyScriptName)
if err = os.WriteFile(filename, []byte(data), fileio.ExecutablePerms); err != nil {
return fmt.Errorf("writing modification script: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment here about the filename not being in the error. Even if it's in the wrapped error, I don't see the harm in including it here as well. I'm of the school of thought that more detail in error messages, even if it ends up being potentially redundant (which itself is contingent on os.WriteFile never removing that information), saves time while understanding an error message and debugging.

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 believe this is more of a style difference. I like keeping very simple wrappers and only including variables if / when absolutely necessary. No issues in bringing it back to what it used to be though, will fix.

@@ -56,7 +56,7 @@ func TestConfigureScripts(t *testing.T) {
fullEntryPath := filepath.Join(builder.context.CombustionDir, entry.Name())
stats, err := os.Stat(fullEntryPath)
require.NoError(t, err)
assert.Equal(t, fs.FileMode(scriptMode), stats.Mode())
assert.Equal(t, fs.FileMode(0o744), stats.Mode())
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here on the hardcoded permissions.

"text/template"
)

func Parse(name string, contents string, templateData any) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is definitely going to make development easier. With this change, I can slap a debugger on my test and look at the applied template rather than having to crack open the file.

@atanasdinov atanasdinov merged commit 9fd31c8 into suse-edge:main Nov 21, 2023
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants