Skip to content

Commit a62ee65

Browse files
committed
refactor: extract common loader/saver interfaces
Move FunctionLoader, FunctionSaver, and FunctionLoaderSaver interfaces along with standardLoaderSaver implementation into new cmd/common package to enable reuse across commands. Add tests and a common testing factory function which will be used in the upcoming tests for config ci subcommand too. Issue #3256 Signed-off-by: Stanislav Jakuschevskij <sjakusch@redhat.com>
1 parent 8e83a41 commit a62ee65

File tree

8 files changed

+152
-49
lines changed

8 files changed

+152
-49
lines changed

cmd/common/common.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package common
2+
3+
import (
4+
"fmt"
5+
6+
fn "knative.dev/func/pkg/functions"
7+
)
8+
9+
// DefaultLoaderSaver implements FunctionLoaderSaver composite interface
10+
var DefaultLoaderSaver FunctionLoaderSaver = standardLoaderSaver{}
11+
12+
// FunctionLoader loads a function from a filesystem path.
13+
type FunctionLoader interface {
14+
Load(path string) (fn.Function, error)
15+
}
16+
17+
// FunctionSaver persists a function to storage.
18+
type FunctionSaver interface {
19+
Save(f fn.Function) error
20+
}
21+
22+
// FunctionLoaderSaver combines loading and saving capabilities for functions.
23+
type FunctionLoaderSaver interface {
24+
FunctionLoader
25+
FunctionSaver
26+
}
27+
28+
type standardLoaderSaver struct{}
29+
30+
// Load creates and validates a function from the given filesystem path.
31+
func (s standardLoaderSaver) Load(path string) (fn.Function, error) {
32+
f, err := fn.NewFunction(path)
33+
if err != nil {
34+
return fn.Function{}, fmt.Errorf("failed to create new function (path: %q): %w", path, err)
35+
}
36+
37+
if !f.Initialized() {
38+
return fn.Function{}, fn.NewErrNotInitialized(f.Root)
39+
}
40+
41+
return f, nil
42+
}
43+
44+
// Save writes the function configuration to disk.
45+
func (s standardLoaderSaver) Save(f fn.Function) error {
46+
return f.Write()
47+
}

cmd/common/common_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package common_test
2+
3+
import (
4+
"testing"
5+
6+
"gotest.tools/v3/assert"
7+
"knative.dev/func/cmd/common"
8+
cmdTest "knative.dev/func/cmd/testing"
9+
fn "knative.dev/func/pkg/functions"
10+
fnTest "knative.dev/func/pkg/testing"
11+
)
12+
13+
func TestDefaultLoaderSaver_SuccessfulLoad(t *testing.T) {
14+
existingFunc := cmdTest.CreateFuncInTempDir(t, "ls-func")
15+
16+
actualFunc, err := common.DefaultLoaderSaver.Load(existingFunc.Root)
17+
18+
assert.NilError(t, err)
19+
assert.Equal(t, existingFunc.Name, actualFunc.Name)
20+
}
21+
22+
func TestDefaultLoaderSaver_GenericFuncCreateError_WhenFuncPathInvalid(t *testing.T) {
23+
_, err := common.DefaultLoaderSaver.Load("/non-existing-path")
24+
25+
assert.ErrorContains(t, err, "failed to create new function")
26+
}
27+
28+
func TestDefaultLoaderSaver_IsNotInitializedError_WhenNoFuncAtPath(t *testing.T) {
29+
expectedErrMsg := fn.NewErrNotInitialized(fnTest.Cwd()).Error()
30+
31+
_, err := common.DefaultLoaderSaver.Load(fnTest.Cwd())
32+
33+
assert.Error(t, err, expectedErrMsg)
34+
}
35+
36+
func TestDefaultLoaderSaver_SuccessfulSave(t *testing.T) {
37+
existingFunc := cmdTest.CreateFuncInTempDir(t, "")
38+
name := "environment"
39+
value := "test"
40+
existingFunc.Run.Envs.Add(name, value)
41+
42+
saveErr := common.DefaultLoaderSaver.Save(existingFunc)
43+
actualFunc, loadErr := common.DefaultLoaderSaver.Load(existingFunc.Root)
44+
45+
assert.NilError(t, saveErr)
46+
assert.NilError(t, loadErr)
47+
assert.Equal(t, actualFunc.Run.Envs.Slice()[0], "environment=test")
48+
}
49+
50+
func TestDefaultLoaderSaver_ForwardsSaveError(t *testing.T) {
51+
err := common.DefaultLoaderSaver.Save(fn.Function{})
52+
53+
assert.Error(t, err, "function root path is required")
54+
}

cmd/config.go

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,12 @@ import (
77
"github.com/ory/viper"
88
"github.com/spf13/cobra"
99

10+
"knative.dev/func/cmd/common"
1011
"knative.dev/func/pkg/config"
1112
fn "knative.dev/func/pkg/functions"
1213
)
1314

14-
type functionLoader interface {
15-
Load(path string) (fn.Function, error)
16-
}
17-
18-
type functionSaver interface {
19-
Save(f fn.Function) error
20-
}
21-
22-
type functionLoaderSaver interface {
23-
functionLoader
24-
functionSaver
25-
}
26-
27-
type standardLoaderSaver struct{}
28-
29-
func (s standardLoaderSaver) Load(path string) (fn.Function, error) {
30-
f, err := fn.NewFunction(path)
31-
if err != nil {
32-
return fn.Function{}, fmt.Errorf("failed to create new function (path: %q): %w", path, err)
33-
}
34-
if !f.Initialized() {
35-
return fn.Function{}, fn.NewErrNotInitialized(f.Root)
36-
}
37-
return f, nil
38-
}
39-
40-
func (s standardLoaderSaver) Save(f fn.Function) error {
41-
return f.Write()
42-
}
43-
44-
var defaultLoaderSaver standardLoaderSaver
45-
46-
func NewConfigCmd(loadSaver functionLoaderSaver, newClient ClientFactory) *cobra.Command {
15+
func NewConfigCmd(loadSaver common.FunctionLoaderSaver, newClient ClientFactory) *cobra.Command {
4716
cmd := &cobra.Command{
4817
Use: "config",
4918
Short: "Configure a function",
@@ -75,7 +44,7 @@ or from the directory specified with --path.
7544

7645
func runConfigCmd(cmd *cobra.Command, args []string) (err error) {
7746

78-
function, err := initConfigCommand(defaultLoaderSaver)
47+
function, err := initConfigCommand(common.DefaultLoaderSaver)
7948
if err != nil {
8049
return
8150
}
@@ -117,7 +86,7 @@ func runConfigCmd(cmd *cobra.Command, args []string) (err error) {
11786
case "Environment variables":
11887
err = runAddEnvsPrompt(cmd.Context(), function)
11988
case "Labels":
120-
err = runAddLabelsPrompt(cmd.Context(), function, defaultLoaderSaver)
89+
err = runAddLabelsPrompt(cmd.Context(), function, common.DefaultLoaderSaver)
12190
case "Git":
12291
err = runConfigGitSetCmd(cmd, NewClient)
12392
}
@@ -128,7 +97,7 @@ func runConfigCmd(cmd *cobra.Command, args []string) (err error) {
12897
case "Environment variables":
12998
err = runRemoveEnvsPrompt(function)
13099
case "Labels":
131-
err = runRemoveLabelsPrompt(function, defaultLoaderSaver)
100+
err = runRemoveLabelsPrompt(function, common.DefaultLoaderSaver)
132101
case "Git":
133102
err = runConfigGitRemoveCmd(cmd, NewClient)
134103
}
@@ -163,7 +132,7 @@ func newConfigCmdConfig() configCmdConfig {
163132
}
164133
}
165134

166-
func initConfigCommand(loader functionLoader) (fn.Function, error) {
135+
func initConfigCommand(loader common.FunctionLoader) (fn.Function, error) {
167136
config := newConfigCmdConfig()
168137

169138
function, err := loader.Load(config.Path)

cmd/config_envs.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import (
1313
"github.com/ory/viper"
1414
"github.com/spf13/cobra"
1515

16+
"knative.dev/func/cmd/common"
1617
"knative.dev/func/pkg/config"
1718
fn "knative.dev/func/pkg/functions"
1819
"knative.dev/func/pkg/k8s"
1920
"knative.dev/func/pkg/utils"
2021
)
2122

22-
func NewConfigEnvsCmd(loadSaver functionLoaderSaver) *cobra.Command {
23+
func NewConfigEnvsCmd(loadSaver common.FunctionLoaderSaver) *cobra.Command {
2324
cmd := &cobra.Command{
2425
Use: "envs",
2526
Short: "List and manage configured environment variable for a function",
@@ -64,7 +65,7 @@ the current directory or from the directory specified with --path.
6465
return cmd
6566
}
6667

67-
func NewConfigEnvsAddCmd(loadSaver functionLoaderSaver) *cobra.Command {
68+
func NewConfigEnvsAddCmd(loadSaver common.FunctionLoaderSaver) *cobra.Command {
6869
cmd := &cobra.Command{
6970
Use: "add",
7071
Short: "Add environment variable to the function configuration",
@@ -139,7 +140,7 @@ set environment variable from a secret
139140
return cmd
140141
}
141142

142-
func NewConfigEnvsRemoveCmd(loadSaver functionLoaderSaver) *cobra.Command {
143+
func NewConfigEnvsRemoveCmd(loadSaver common.FunctionLoaderSaver) *cobra.Command {
143144
cmd := &cobra.Command{
144145
Use: "remove",
145146
Short: "Remove environment variable from the function configuration",

cmd/config_labels.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import (
1111
"github.com/ory/viper"
1212
"github.com/spf13/cobra"
1313

14+
"knative.dev/func/cmd/common"
1415
"knative.dev/func/pkg/config"
1516
fn "knative.dev/func/pkg/functions"
1617
"knative.dev/func/pkg/utils"
1718
)
1819

19-
func NewConfigLabelsCmd(loaderSaver functionLoaderSaver) *cobra.Command {
20+
func NewConfigLabelsCmd(loaderSaver common.FunctionLoaderSaver) *cobra.Command {
2021
var configLabelsCmd = &cobra.Command{
2122
Use: "labels",
2223
Short: "List and manage configured labels for a function",
@@ -184,7 +185,7 @@ func listLabels(f fn.Function, w io.Writer, outputFormat Format) error {
184185
}
185186
}
186187

187-
func runAddLabelsPrompt(_ context.Context, f fn.Function, saver functionSaver) (err error) {
188+
func runAddLabelsPrompt(_ context.Context, f fn.Function, saver common.FunctionSaver) (err error) {
188189

189190
insertToIndex := 0
190191

@@ -317,7 +318,7 @@ func runAddLabelsPrompt(_ context.Context, f fn.Function, saver functionSaver) (
317318
return
318319
}
319320

320-
func runRemoveLabelsPrompt(f fn.Function, saver functionSaver) (err error) {
321+
func runRemoveLabelsPrompt(f fn.Function, saver common.FunctionSaver) (err error) {
321322
if len(f.Deploy.Labels) == 0 {
322323
fmt.Println("There aren't any configured labels")
323324
return

cmd/config_volumes.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/AlecAivazis/survey/v2"
99
"github.com/spf13/cobra"
1010

11+
"knative.dev/func/cmd/common"
1112
"knative.dev/func/pkg/config"
1213
fn "knative.dev/func/pkg/functions"
1314
"knative.dev/func/pkg/k8s"
@@ -26,7 +27,7 @@ the current directory or from the directory specified with --path.
2627
SuggestFor: []string{"vol", "volums", "vols"},
2728
PreRunE: bindEnv("path", "verbose"),
2829
RunE: func(cmd *cobra.Command, args []string) (err error) {
29-
function, err := initConfigCommand(defaultLoaderSaver)
30+
function, err := initConfigCommand(common.DefaultLoaderSaver)
3031
if err != nil {
3132
return
3233
}
@@ -85,7 +86,7 @@ For non-interactive usage, use flags to specify the volume type and configuratio
8586
SuggestFor: []string{"ad", "create", "insert", "append"},
8687
PreRunE: bindEnv("path", "verbose", "type", "source", "mount-path", "read-only", "size", "medium"),
8788
RunE: func(cmd *cobra.Command, args []string) (err error) {
88-
function, err := initConfigCommand(defaultLoaderSaver)
89+
function, err := initConfigCommand(common.DefaultLoaderSaver)
8990
if err != nil {
9091
return
9192
}
@@ -129,7 +130,7 @@ For non-interactive usage, use the --mount-path flag to specify which volume to
129130
SuggestFor: []string{"del", "delete", "rmeove"},
130131
PreRunE: bindEnv("path", "verbose", "mount-path"),
131132
RunE: func(cmd *cobra.Command, args []string) (err error) {
132-
function, err := initConfigCommand(defaultLoaderSaver)
133+
function, err := initConfigCommand(common.DefaultLoaderSaver)
133134
if err != nil {
134135
return
135136
}

cmd/root.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"k8s.io/apimachinery/pkg/util/sets"
1616
"knative.dev/client/pkg/util"
1717

18+
"knative.dev/func/cmd/common"
1819
"knative.dev/func/cmd/templates"
1920
"knative.dev/func/pkg/config"
2021
fn "knative.dev/func/pkg/functions"
@@ -100,7 +101,7 @@ Learn more about Knative at: https://knative.dev`, cfg.Name),
100101
{
101102
Header: "System Commands:",
102103
Commands: []*cobra.Command{
103-
NewConfigCmd(defaultLoaderSaver, newClient),
104+
NewConfigCmd(common.DefaultLoaderSaver, newClient),
104105
NewLanguagesCmd(newClient),
105106
NewTemplatesCmd(newClient),
106107
NewRepositoryCmd(newClient),
@@ -315,7 +316,7 @@ func mergeEnvs(envs []fn.Env, envToUpdate *util.OrderedMap, envToRemove []string
315316
return envs, counter, nil
316317
}
317318

318-
// addConfirmFlag ensures common text/wording when the --path flag is used
319+
// addConfirmFlag ensures common text/wording when the --confirm flag is used
319320
func addConfirmFlag(cmd *cobra.Command, dflt bool) {
320321
cmd.Flags().BoolP("confirm", "c", dflt, "Prompt to confirm options interactively ($FUNC_CONFIRM)")
321322
}
@@ -325,7 +326,7 @@ func addPathFlag(cmd *cobra.Command) {
325326
cmd.Flags().StringP("path", "p", "", "Path to the function. Default is current directory ($FUNC_PATH)")
326327
}
327328

328-
// addVerboseFlag ensures common text/wording when the --path flag is used
329+
// addVerboseFlag ensures common text/wording when the --verbose flag is used
329330
func addVerboseFlag(cmd *cobra.Command, dflt bool) {
330331
cmd.Flags().BoolP("verbose", "v", dflt, "Print verbose logs ($FUNC_VERBOSE)")
331332
}

cmd/testing/factory.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package testing
2+
3+
import (
4+
"testing"
5+
6+
"gotest.tools/v3/assert"
7+
fn "knative.dev/func/pkg/functions"
8+
fnTest "knative.dev/func/pkg/testing"
9+
)
10+
11+
// CreateFuncInTempDir creates and initializes a Go function in a temporary
12+
// directory for testing.
13+
func CreateFuncInTempDir(t *testing.T, fnName string) fn.Function {
14+
t.Helper()
15+
16+
var name string
17+
if fnName == "" {
18+
name = "go-func"
19+
}
20+
21+
result, err := fn.New().Init(fn.Function{
22+
Name: name,
23+
Runtime: "go",
24+
Root: fnTest.FromTempDirectory(t),
25+
})
26+
assert.NilError(t, err)
27+
28+
return result
29+
}

0 commit comments

Comments
 (0)