Skip to content

Commit

Permalink
Extract template package
Browse files Browse the repository at this point in the history
Signed-off-by: Atanas Dinov <[email protected]>
  • Loading branch information
atanasdinov committed Nov 17, 2023
1 parent 009afe5 commit 2bd2193
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 126 deletions.
15 changes: 4 additions & 11 deletions pkg/build/grub.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package build

import (
"bytes"
_ "embed"
"fmt"
"strings"
"text/template"

"github.com/suse-edge/edge-image-builder/pkg/template"
)

//go:embed scripts/grub/guestfish-snippet.tpl
Expand All @@ -19,24 +19,17 @@ func (b *Builder) generateGRUBGuestfishCommands() (string, error) {
return "", nil
}

tmpl, err := template.New("guestfish-snippet").Parse(guestfishSnippet)
if err != nil {
return "", fmt.Errorf("building template for GRUB guestfish snippet: %w", err)
}

argLine := strings.Join(b.imageConfig.OperatingSystem.KernelArgs, " ")
values := struct {
KernelArgs string
}{
KernelArgs: argLine,
}

var buff bytes.Buffer
err = tmpl.Execute(&buff, values)
snippet, err := template.Parse("guestfish-snippet", guestfishSnippet, values)
if err != nil {
return "", fmt.Errorf("applying GRUB guestfish snippet: %w", err)
return "", fmt.Errorf("parsing guestfish template: %w", err)
}

snippet := buff.String()
return snippet, nil
}
14 changes: 10 additions & 4 deletions pkg/build/raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package build
import (
_ "embed"
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/suse-edge/edge-image-builder/pkg/fileio"
"github.com/suse-edge/edge-image-builder/pkg/template"
)

const (
Expand All @@ -15,7 +17,7 @@ const (
)

//go:embed scripts/modify-raw-image.sh.tpl
var modifyRawImageScript string
var modifyRawImageTemplate string

func (b *Builder) buildRawImage() error {
cmd := b.createRawImageCopyCommand()
Expand Down Expand Up @@ -66,10 +68,14 @@ func (b *Builder) writeModifyScript() error {
ConfigureGRUB: grubConfiguration,
}

filename := b.generateBuildDirFilename(modifyScriptName)
data, err := template.Parse(modifyScriptName, modifyRawImageTemplate, &values)
if err != nil {
return fmt.Errorf("parsing template: %w", err)
}

if err := fileio.WriteTemplate(filename, modifyRawImageScript, &values); err != nil {
return fmt.Errorf("writing modification script %s: %w", modifyScriptName, err)
filename := b.generateBuildDirFilename(modifyScriptName)
if err = os.WriteFile(filename, []byte(data), fileio.ExecutablePerms); err != nil {
return fmt.Errorf("writing modification script: %w", err)
}

return nil
Expand Down
32 changes: 1 addition & 31 deletions pkg/fileio/file_io.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,15 @@ import (
"fmt"
"io"
"os"
"text/template"
)

const (
// ExecutablePerms are Linux permissions (rwxr--r--) for executable files (scripts, binaries, etc.)
ExecutablePerms os.FileMode = 0o744
// NonExecutablePerms are Linux permissions (rw-r--r--) for non-executable files (configs, RPMs, etc.):
// NonExecutablePerms are Linux permissions (rw-r--r--) for non-executable files (configs, RPMs, etc.)
NonExecutablePerms os.FileMode = 0o644
)

func WriteTemplate(filename string, contents string, templateData any) error {
if templateData == nil {
return fmt.Errorf("template data not provided")
}

tmpl, err := template.New(filename).Parse(contents)
if err != nil {
return fmt.Errorf("parsing template: %w", err)
}

file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("creating file: %w", err)
}
defer func() {
_ = file.Close()
}()

if err = file.Chmod(ExecutablePerms); err != nil {
return fmt.Errorf("applying executable permissions: %w", err)
}

if err = tmpl.Execute(file, templateData); err != nil {
return fmt.Errorf("applying template: %w", err)
}

return nil
}

func CopyFile(src string, dest string, perms os.FileMode) error {
sourceFile, err := os.Open(src)
if err != nil {
Expand Down
80 changes: 0 additions & 80 deletions pkg/fileio/file_io_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,93 +2,13 @@ package fileio

import (
"fmt"
"io/fs"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestWriteTemplate(t *testing.T) {
const tmpDirPrefix = "eib-write-template-test-"

tmpDir, err := os.MkdirTemp("", tmpDirPrefix)
require.NoError(t, err)
defer os.RemoveAll(tmpDir)

tests := []struct {
name string
filename string
contents string
templateData any
expectedContents string
expectedErr string
}{
{
name: "Templated file is successfully written",
filename: "template",
contents: "{{.Foo}} and {{.Bar}}",
templateData: struct {
Foo string
Bar string
}{
Foo: "ooF",
Bar: "raB",
},
expectedContents: "ooF and raB",
},
{
name: "Templated file is not written due to missing data",
filename: "missing-data",
contents: "{{.Foo}} and {{.Bar}}",
expectedErr: "template data not provided",
},
{
name: "Templated file is not written due to invalid syntax",
filename: "invalid-syntax",
contents: "{{.Foo and ",
templateData: struct{}{},
expectedErr: fmt.Sprintf("parsing template: template: %s/invalid-syntax:1: unclosed action", tmpDir),
},
{
name: "Templated file is not written due to missing field",
filename: "invalid-data",
contents: "{{.Foo}} and {{.Bar}}",
templateData: struct {
Foo string
}{
Foo: "ooF",
},
expectedErr: fmt.Sprintf("applying template: template: %[1]s/invalid-data:1:15: "+
"executing \"%[1]s/invalid-data\" at <.Bar>: can't evaluate field Bar in type struct { Foo string }", tmpDir),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
filename := filepath.Join(tmpDir, test.filename)

err := WriteTemplate(filename, test.contents, test.templateData)

if test.expectedErr != "" {
assert.EqualError(t, err, test.expectedErr)
} else {
require.Nil(t, err)

contents, err := os.ReadFile(filename)
require.NoError(t, err)
assert.Equal(t, test.expectedContents, string(contents))

info, err := os.Stat(filename)
require.NoError(t, err)
assert.Equal(t, fs.FileMode(0o744), info.Mode())
}
})
}
}

func TestCopyFile(t *testing.T) {
const (
source = "file_io.go" // use the source code file as a valid input
Expand Down
25 changes: 25 additions & 0 deletions pkg/template/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package template

import (
"bytes"
"fmt"
"text/template"
)

func Parse(name string, contents string, templateData any) (string, error) {
if templateData == nil {
return "", fmt.Errorf("template data not provided")
}

tmpl, err := template.New(name).Parse(contents)
if err != nil {
return "", fmt.Errorf("parsing contents: %w", err)
}

var buff bytes.Buffer
if err = tmpl.Execute(&buff, templateData); err != nil {
return "", fmt.Errorf("applying template: %w", err)
}

return buff.String(), nil
}
73 changes: 73 additions & 0 deletions pkg/template/template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package template

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestParse(t *testing.T) {
tests := []struct {
name string
templateName string
contents string
templateData any
expectedOutput string
expectedErr string
}{
{
name: "Template is successfully processed",
templateName: "valid-template",
contents: "{{.Foo}} and {{.Bar}}",
templateData: struct {
Foo string
Bar string
}{
Foo: "ooF",
Bar: "raB",
},
expectedOutput: "ooF and raB",
},
{
name: "Templating fails due to missing data",
templateName: "missing-data",
contents: "{{.Foo}} and {{.Bar}}",
expectedErr: "template data not provided",
},
{
name: "Templating fails due to invalid syntax",
templateName: "invalid-syntax",
contents: "{{.Foo and ",
templateData: struct{}{},
expectedErr: "parsing contents: template: invalid-syntax:1: unclosed action",
},
{
name: "Templating fails due to missing field",
templateName: "invalid-data",
contents: "{{.Foo}} and {{.Bar}}",
templateData: struct {
Foo string
}{
Foo: "ooF",
},
expectedErr: "applying template: template: invalid-data:1:15: " +
"executing \"invalid-data\" at <.Bar>: can't evaluate field Bar in type struct { Foo string }",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {

data, err := Parse(test.templateName, test.contents, test.templateData)

if test.expectedErr != "" {
assert.EqualError(t, err, test.expectedErr)
assert.Equal(t, "", data)
} else {
require.Nil(t, err)
assert.Equal(t, test.expectedOutput, data)
}
})
}
}

0 comments on commit 2bd2193

Please sign in to comment.