From 8b6584ef7eea04014ac35534b55ee3c789490c67 Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Tue, 29 Sep 2026 18:11:23 +0100 Subject: [PATCH 1/5] Remove Knative generate code paths The generate command no longer supports the serving.knative.dev/v1 API version. Drop the Knative Serving generator, its env ordering helper, and references from the --api flag help text and examples. Functions should be generated with the openfaas.com CRDs instead. The schema/knative package is now unused and will be deleted separately. Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- commands/generate.go | 123 +------------------------------------------ 1 file changed, 1 insertion(+), 122 deletions(-) diff --git a/commands/generate.go b/commands/generate.go index e5a2e4c5..4d9c6c62 100644 --- a/commands/generate.go +++ b/commands/generate.go @@ -14,7 +14,6 @@ import ( "github.com/openfaas/faas-cli/proxy" "github.com/openfaas/faas-cli/schema" - knativev1 "github.com/openfaas/faas-cli/schema/knative/v1" openfaasv1 "github.com/openfaas/faas-cli/schema/openfaas/v1" "github.com/openfaas/faas-cli/util" "github.com/openfaas/go-sdk/stack" @@ -45,7 +44,7 @@ func init() { generateCmd.Flags().StringVar(&fromStore, "from-store", "", "generate using a store image") generateCmd.Flags().StringVar(&name, "name", "", "for use with --from-store, override the name for the Function CR") - generateCmd.Flags().StringVar(&api, "api", defaultAPIVersion, "CRD API version e.g openfaas.com/v1, serving.knative.dev/v1") + generateCmd.Flags().StringVar(&api, "api", defaultAPIVersion, "CRD API version e.g openfaas.com/v1") generateCmd.Flags().StringVarP(&crdFunctionNamespace, "namespace", "n", "openfaas-fn", "Kubernetes namespace for functions") generateCmd.Flags().Var(&tagFormat, "tag", "Override latest tag on function Docker image, accepts 'digest', 'latest', 'sha', 'branch', 'describe'") generateCmd.Flags().BoolVar(&envsubst, "envsubst", true, "Substitute environment variables in stack.yaml file") @@ -62,7 +61,6 @@ var generateCmd = &cobra.Command{ Long: `The generate command creates kubernetes CRD YAML file for functions`, Example: ` faas-cli generate --api=openfaas.com/v1 --yaml stack.yaml | kubectl apply -f - faas-cli generate --api=openfaas.com/v1 -f stack.yaml - faas-cli generate --api=serving.knative.dev/v1 -f stack.yaml faas-cli generate --api=openfaas.com/v1 --namespace openfaas-fn -f stack.yaml faas-cli generate --api=openfaas.com/v1 -f stack.yaml --tag branch -n openfaas-fn`, PreRunE: preRunGenerate, @@ -209,10 +207,6 @@ func generateCRDYAML(services stack.Services, format schema.BuildFormat, apiVers if len(services.Functions) > 0 { - if apiVersion == knativev1.APIVersionLatest { - return generateknativev1ServingServiceCRDYAML(services, format, api, crdFunctionNamespace) - } - orderedNames := generateFunctionOrder(services.Functions) for _, name := range orderedNames { @@ -274,103 +268,6 @@ func generateCRDYAML(services stack.Services, format schema.BuildFormat, apiVers return objectsString, nil } -func generateknativev1ServingServiceCRDYAML(services stack.Services, format schema.BuildFormat, apiVersion, namespace string) (string, error) { - crds := []knativev1.ServingServiceCRD{} - - orderedNames := generateFunctionOrder(services.Functions) - - for _, name := range orderedNames { - - function := services.Functions[name] - - fileEnvironment, err := readFiles(function.EnvironmentFile) - if err != nil { - return "", err - } - - //combine all environment variables - allEnvironment, envErr := compileEnvironment([]string{}, function.Environment, fileEnvironment) - if envErr != nil { - return "", envErr - } - - env := orderknativeEnv(allEnvironment) - - var annotations map[string]string - - if function.Annotations != nil { - annotations = *function.Annotations - } - - branch, version, err := builder.GetImageTagValues(tagFormat, function.Handler) - if err != nil { - return "", err - } - - imageName := schema.BuildImageName(format, function.Image, version, branch) - - crd := knativev1.ServingServiceCRD{ - Metadata: schema.Metadata{ - Name: name, - Namespace: namespace, - Annotations: annotations, - }, - APIVersion: apiVersion, - Kind: "Service", - - Spec: knativev1.ServingServiceSpec{ - ServingServiceSpecTemplate: knativev1.ServingServiceSpecTemplate{ - Template: knativev1.ServingServiceSpecTemplateSpec{ - Containers: []knativev1.ServingSpecContainersContainerSpec{}, - }, - }, - }, - } - - crd.Spec.Template.Containers = append(crd.Spec.Template.Containers, knativev1.ServingSpecContainersContainerSpec{ - Image: imageName, - Env: env, - }) - - var mounts []knativev1.VolumeMount - var volumes []knativev1.Volume - - for _, secret := range function.Secrets { - mounts = append(mounts, knativev1.VolumeMount{ - MountPath: "/var/openfaas/secrets/" + secret, - ReadOnly: true, - Name: secret, - }) - volumes = append(volumes, knativev1.Volume{ - Name: secret, - Secret: knativev1.Secret{ - SecretName: secret, - }, - }) - } - - crd.Spec.Template.Volumes = volumes - crd.Spec.Template.Containers[0].VolumeMounts = mounts - - crds = append(crds, crd) - } - - var objectsString string - for _, crd := range crds { - - var buff bytes.Buffer - yamlEncoder := yaml.NewEncoder(&buff) - yamlEncoder.SetIndent(2) // this is what you're looking for - if err := yamlEncoder.Encode(&crd); err != nil { - return "", err - } - - objectsString += "---\n" + string(buff.Bytes()) - } - - return objectsString, nil -} - func generateFunctionOrder(functions map[string]stack.Function) []string { var functionNames []string @@ -383,21 +280,3 @@ func generateFunctionOrder(functions map[string]stack.Function) []string { return functionNames } - -func orderknativeEnv(environment map[string]string) []knativev1.EnvPair { - - var orderedEnvironment []string - var envVars []knativev1.EnvPair - - for k := range environment { - orderedEnvironment = append(orderedEnvironment, k) - } - - sort.Strings(orderedEnvironment) - - for _, envVar := range orderedEnvironment { - envVars = append(envVars, knativev1.EnvPair{Name: envVar, Value: environment[envVar]}) - } - - return envVars -} From 43e5f0d539767c3e95c0b72ea1a077318b3228a8 Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Tue, 29 Sep 2026 18:11:31 +0100 Subject: [PATCH 2/5] Delete Knative Serving schema Remove the schema/knative/v1 package containing the ServingService CRD types, now that the generate command no longer emits Knative Serving resources. Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- schema/knative/v1/serving.go | 56 ------------------------------------ 1 file changed, 56 deletions(-) delete mode 100644 schema/knative/v1/serving.go diff --git a/schema/knative/v1/serving.go b/schema/knative/v1/serving.go deleted file mode 100644 index be0cfea2..00000000 --- a/schema/knative/v1/serving.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) OpenFaaS Author(s) 2019. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -package v1 - -import "github.com/openfaas/faas-cli/schema" - -const APIVersionLatest = "serving.knative.dev/v1" - -// ServingServiceCRD root level YAML definition for the object -type ServingServiceCRD struct { - //APIVersion CRD API version - APIVersion string `yaml:"apiVersion"` - //Kind kind of the object - Kind string `yaml:"kind"` - Metadata schema.Metadata `yaml:"metadata,omitempty"` - Spec ServingServiceSpec `yaml:"spec"` -} - -type ServingServiceSpec struct { - ServingServiceSpecTemplate `yaml:"template"` -} - -type ServingServiceSpecTemplateSpec struct { - Containers []ServingSpecContainersContainerSpec `yaml:"containers"` - Volumes []Volume `yaml:"volumes,omitempty"` -} -type ServingServiceSpecTemplate struct { - Template ServingServiceSpecTemplateSpec `yaml:"spec"` -} - -type ServingSpecContainersContainerSpec struct { - Image string `yaml:"image"` - Env []EnvPair `yaml:"env,omitempty"` - VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty"` -} - -type VolumeMount struct { - Name string `yaml:"name"` - MountPath string `yaml:"mountPath"` - ReadOnly bool `yaml:"readOnly"` -} - -type Volume struct { - Name string `yaml:"name"` - Secret Secret `yaml:"secret"` -} - -type Secret struct { - SecretName string `yaml:"secretName"` -} - -type EnvPair struct { - Name string `yaml:"name"` - Value string `yaml:"value"` -} From 28a49d3b8929e09675a47aa766a0feec56b88a22 Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Tue, 29 Sep 2026 18:15:33 +0100 Subject: [PATCH 3/5] Validate the --api flag for generate Only openfaas.com/v1 and openfaas.com/v2alpha2 are supported by the generate command. Any other API version was previously accepted and stamped straight into the generated CRD, producing YAML that would not apply. Reject unknown values in preRunGenerate with the list of supported versions. Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- commands/generate.go | 13 +++++++++-- commands/generate_test.go | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/commands/generate.go b/commands/generate.go index 4d9c6c62..a7d79c0e 100644 --- a/commands/generate.go +++ b/commands/generate.go @@ -8,6 +8,7 @@ import ( "fmt" "os" "sort" + "strings" "github.com/openfaas/faas-cli/builder" v2 "github.com/openfaas/faas-cli/schema/store/v2" @@ -28,6 +29,8 @@ const ( defaultAPIVersion = "openfaas.com/v1" ) +var apiVersions = []string{"openfaas.com/v1", "openfaas.com/v2alpha2"} + var ( api string name string @@ -44,7 +47,7 @@ func init() { generateCmd.Flags().StringVar(&fromStore, "from-store", "", "generate using a store image") generateCmd.Flags().StringVar(&name, "name", "", "for use with --from-store, override the name for the Function CR") - generateCmd.Flags().StringVar(&api, "api", defaultAPIVersion, "CRD API version e.g openfaas.com/v1") + generateCmd.Flags().StringVar(&api, "api", defaultAPIVersion, "CRD API version e.g openfaas.com/v1, openfaas.com/v2alpha2") generateCmd.Flags().StringVarP(&crdFunctionNamespace, "namespace", "n", "openfaas-fn", "Kubernetes namespace for functions") generateCmd.Flags().Var(&tagFormat, "tag", "Override latest tag on function Docker image, accepts 'digest', 'latest', 'sha', 'branch', 'describe'") generateCmd.Flags().BoolVar(&envsubst, "envsubst", true, "Substitute environment variables in stack.yaml file") @@ -72,7 +75,13 @@ func preRunGenerate(cmd *cobra.Command, args []string) error { return fmt.Errorf("you must supply the API version with the --api flag") } - return nil + for _, version := range apiVersions { + if api == version { + return nil + } + } + + return fmt.Errorf("unsupported API version %q, must be one of: %s", api, strings.Join(apiVersions, ", ")) } func filterStoreItem(items []v2.StoreFunction, fromStore string) (*v2.StoreFunction, error) { diff --git a/commands/generate_test.go b/commands/generate_test.go index 68b9b2cb..5527e20b 100644 --- a/commands/generate_test.go +++ b/commands/generate_test.go @@ -539,3 +539,52 @@ func Test_generateFunctionOrder(t *testing.T) { } } + +func Test_preRunGenerate_API(t *testing.T) { + testcases := []struct { + Name string + API string + Expected bool + }{ + { + Name: "Empty API version", + API: "", + Expected: false, + }, + { + Name: "OpenFaaS v1 API version", + API: "openfaas.com/v1", + Expected: true, + }, + { + Name: "OpenFaaS v2alpha2 API version", + API: "openfaas.com/v2alpha2", + Expected: true, + }, + { + Name: "Knative API version", + API: "serving.knative.dev/v1", + Expected: false, + }, + { + Name: "Unknown API version", + API: "openfaas.com/v2", + Expected: false, + }, + } + + for _, testcase := range testcases { + api = testcase.API + err := preRunGenerate(generateCmd, []string{}) + + if testcase.Expected && err != nil { + t.Fatalf("%s: expected success, got error: %s", testcase.Name, err) + } + + if !testcase.Expected && err == nil { + t.Fatalf("%s: expected error, got nil", testcase.Name) + } + } + + api = defaultAPIVersion +} From 15e2deda90e3bae1def66f634a17ef15c8b149a5 Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Tue, 29 Sep 2026 18:19:12 +0100 Subject: [PATCH 4/5] Add stack.yaml output for generate --from-store Scaffolding a stack.yaml for a store function is a common starting point for overriding a pre-built deployment, such as adding OAuth environment variables and secrets before running deploy. Add an --output flag accepting stack.yaml (or its stack alias), which emits an OpenFaaS stack.yaml for the store function instead of a Function CR. The generated file carries the store image, merged labels, annotations and environment, and sets skip_build so the pre-built image is used without a rebuild. The flag is only valid alongside --from-store, and is rejected otherwise. Output types are declared locally so empty values are omitted, keeping the scaffold minimal for editing afterwards. Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- commands/generate.go | 84 +++++++++++++++++++++++++++++++++++++- commands/generate_test.go | 85 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 1 deletion(-) diff --git a/commands/generate.go b/commands/generate.go index a7d79c0e..85b3f712 100644 --- a/commands/generate.go +++ b/commands/generate.go @@ -38,6 +38,7 @@ var ( crdFunctionNamespace string fromStore string desiredArch string + outputFormat string annotationArgs []string labelArgs []string ) @@ -46,6 +47,7 @@ func init() { generateCmd.Flags().StringVar(&fromStore, "from-store", "", "generate using a store image") generateCmd.Flags().StringVar(&name, "name", "", "for use with --from-store, override the name for the Function CR") + generateCmd.Flags().StringVar(&outputFormat, "output", "", "output format e.g stack.yaml, for use with --from-store to generate an OpenFaaS stack.yaml") generateCmd.Flags().StringVar(&api, "api", defaultAPIVersion, "CRD API version e.g openfaas.com/v1, openfaas.com/v2alpha2") generateCmd.Flags().StringVarP(&crdFunctionNamespace, "namespace", "n", "openfaas-fn", "Kubernetes namespace for functions") @@ -65,12 +67,17 @@ var generateCmd = &cobra.Command{ Example: ` faas-cli generate --api=openfaas.com/v1 --yaml stack.yaml | kubectl apply -f - faas-cli generate --api=openfaas.com/v1 -f stack.yaml faas-cli generate --api=openfaas.com/v1 --namespace openfaas-fn -f stack.yaml - faas-cli generate --api=openfaas.com/v1 -f stack.yaml --tag branch -n openfaas-fn`, + faas-cli generate --api=openfaas.com/v1 -f stack.yaml --tag branch -n openfaas-fn + faas-cli generate --from-store nodeinfo --output stack.yaml > stack.yaml`, PreRunE: preRunGenerate, RunE: runGenerate, } func preRunGenerate(cmd *cobra.Command, args []string) error { + if isStackOutput(outputFormat) { + return nil + } + if len(api) == 0 { return fmt.Errorf("you must supply the API version with the --api flag") } @@ -84,6 +91,10 @@ func preRunGenerate(cmd *cobra.Command, args []string) error { return fmt.Errorf("unsupported API version %q, must be one of: %s", api, strings.Join(apiVersions, ", ")) } +func isStackOutput(format string) bool { + return format == "stack.yaml" || format == "stack" +} + func filterStoreItem(items []v2.StoreFunction, fromStore string) (*v2.StoreFunction, error) { var item *v2.StoreFunction @@ -103,6 +114,10 @@ func filterStoreItem(items []v2.StoreFunction, fromStore string) (*v2.StoreFunct func runGenerate(cmd *cobra.Command, args []string) error { + if isStackOutput(outputFormat) && fromStore == "" { + return fmt.Errorf("--output %s can only be used with --from-store", outputFormat) + } + desiredArch, _ := cmd.Flags().GetString("arch") var services stack.Services @@ -197,6 +212,16 @@ Alternatively, to generate a definition for store functions, use "--from-store"` os.Exit(1) } + if isStackOutput(outputFormat) { + stackYAML, err := generateStackYAML(services, gateway) + if err != nil { + return err + } + + fmt.Println(stackYAML) + return nil + } + objectsString, err := generateCRDYAML(services, tagFormat, api, crdFunctionNamespace, builder.NewFunctionMetadataSourceLive()) if err != nil { @@ -209,6 +234,63 @@ Alternatively, to generate a definition for store functions, use "--from-store"` return nil } +// stackYAML is the output format for --output stack.yaml, mirroring an +// OpenFaaS stack.yaml with only the values known from the store item +type stackYAML struct { + Version string `yaml:"version"` + Provider stack.Provider `yaml:"provider"` + Functions map[string]stackYAMLFn `yaml:"functions"` +} + +type stackYAMLFn struct { + Image string `yaml:"image"` + SkipBuild bool `yaml:"skip_build"` + Environment map[string]string `yaml:"environment,omitempty"` + Labels *map[string]string `yaml:"labels,omitempty"` + Annotations *map[string]string `yaml:"annotations,omitempty"` +} + +// generateStackYAML generates an OpenFaaS stack.yaml for functions, with +// skip_build set so the pre-built image is deployed without a rebuild +func generateStackYAML(services stack.Services, gateway string) (string, error) { + functions := make(map[string]stackYAMLFn, len(services.Functions)) + for name, function := range services.Functions { + if function.Labels != nil && len(*function.Labels) == 0 { + function.Labels = nil + } + + if function.Annotations != nil && len(*function.Annotations) == 0 { + function.Annotations = nil + } + + functions[name] = stackYAMLFn{ + Image: function.Image, + SkipBuild: true, + Environment: function.Environment, + Labels: function.Labels, + Annotations: function.Annotations, + } + } + + output := stackYAML{ + Version: "1.0", + Provider: stack.Provider{ + Name: "openfaas", + GatewayURL: gateway, + }, + Functions: functions, + } + + var buff bytes.Buffer + yamlEncoder := yaml.NewEncoder(&buff) + yamlEncoder.SetIndent(2) + if err := yamlEncoder.Encode(&output); err != nil { + return "", err + } + + return buff.String(), nil +} + // generateCRDYAML generates CRD YAML for functions func generateCRDYAML(services stack.Services, format schema.BuildFormat, apiVersion, namespace string, metadataSource builder.FunctionMetadataSource) (string, error) { diff --git a/commands/generate_test.go b/commands/generate_test.go index 5527e20b..00bb76ee 100644 --- a/commands/generate_test.go +++ b/commands/generate_test.go @@ -3,6 +3,7 @@ package commands import ( + "strings" "testing" v2 "github.com/openfaas/faas-cli/schema/store/v2" @@ -588,3 +589,87 @@ func Test_preRunGenerate_API(t *testing.T) { api = defaultAPIVersion } + +func Test_generateStackYAML(t *testing.T) { + labels := map[string]string{"colour": "blue"} + annotations := map[string]string{"topic": "oauth"} + + services := stack.Services{ + Functions: map[string]stack.Function{ + "nodeinfo": { + Name: "nodeinfo", + Image: "ghcr.io/openfaas/nodeinfo:latest", + Environment: map[string]string{"fprocess": "node index.js"}, + Labels: &labels, + Annotations: &annotations, + }, + }, + } + + generatedYAML, err := generateStackYAML(services, "https://gateway.example.com") + if err != nil { + t.Fatalf("failed to generate stack YAML: %s", err) + } + + parsed, err := stack.ParseYAMLData([]byte(generatedYAML), "", "", true) + if err != nil { + t.Fatalf("failed to parse generated stack YAML: %s", err) + } + if parsed == nil { + t.Fatal("generated stack YAML was empty") + } + + if parsed.Version != "1.0" { + t.Errorf("want version 1.0, got %q", parsed.Version) + } + + if parsed.Provider.Name != "openfaas" { + t.Errorf("want provider name openfaas, got %q", parsed.Provider.Name) + } + + if parsed.Provider.GatewayURL != "https://gateway.example.com" { + t.Errorf("want gateway https://gateway.example.com, got %q", parsed.Provider.GatewayURL) + } + + function, ok := parsed.Functions["nodeinfo"] + if !ok { + t.Fatal("want function nodeinfo in generated stack YAML") + } + + if !function.SkipBuild { + t.Error("want skip_build to be true") + } + + if function.Image != "ghcr.io/openfaas/nodeinfo:latest" { + t.Errorf("want image ghcr.io/openfaas/nodeinfo:latest, got %q", function.Image) + } + + if function.Environment["fprocess"] != "node index.js" { + t.Errorf("want fprocess node index.js, got %q", function.Environment["fprocess"]) + } + + if function.Labels == nil || (*function.Labels)["colour"] != "blue" { + t.Errorf("want label colour: blue, got %v", function.Labels) + } + + if function.Annotations == nil || (*function.Annotations)["topic"] != "oauth" { + t.Errorf("want annotation topic: oauth, got %v", function.Annotations) + } +} + +func Test_runGenerate_StackOutputRequiresFromStore(t *testing.T) { + outputFormat = "stack.yaml" + fromStore = "" + + err := runGenerate(generateCmd, []string{}) + if err == nil { + t.Fatal("expected error when --output stack.yaml is used without --from-store, got nil") + } + + if !strings.Contains(err.Error(), "--from-store") { + t.Errorf("expected error to mention --from-store, got: %s", err) + } + + outputFormat = "" + fromStore = "" +} From 59846c685f0b8b26b08908a290bcb036338c3bff Mon Sep 17 00:00:00 2001 From: "Alex Ellis (OpenFaaS Ltd)" Date: Tue, 29 Sep 2026 18:29:25 +0100 Subject: [PATCH 5/5] Validate the --output flag in PreRunE Move the check that --output stack.yaml requires --from-store out of runGenerate and into preRunGenerate, where the rest of the flag validation lives. Unknown output formats are now rejected rather than silently ignored, and the --name help no longer refers to a Function CR, which is not what stack output generates. Signed-off-by: Alex Ellis (OpenFaaS Ltd) --- commands/generate.go | 14 ++++++---- commands/generate_test.go | 59 ++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/commands/generate.go b/commands/generate.go index 85b3f712..98179712 100644 --- a/commands/generate.go +++ b/commands/generate.go @@ -46,7 +46,7 @@ var ( func init() { generateCmd.Flags().StringVar(&fromStore, "from-store", "", "generate using a store image") - generateCmd.Flags().StringVar(&name, "name", "", "for use with --from-store, override the name for the Function CR") + generateCmd.Flags().StringVar(&name, "name", "", "for use with --from-store, override the name for the generated function") generateCmd.Flags().StringVar(&outputFormat, "output", "", "output format e.g stack.yaml, for use with --from-store to generate an OpenFaaS stack.yaml") generateCmd.Flags().StringVar(&api, "api", defaultAPIVersion, "CRD API version e.g openfaas.com/v1, openfaas.com/v2alpha2") @@ -74,7 +74,15 @@ var generateCmd = &cobra.Command{ } func preRunGenerate(cmd *cobra.Command, args []string) error { + if outputFormat != "" && !isStackOutput(outputFormat) { + return fmt.Errorf("unsupported output format %q, must be one of: stack.yaml, stack", outputFormat) + } + if isStackOutput(outputFormat) { + if len(fromStore) == 0 { + return fmt.Errorf("--output %s can only be used with --from-store", outputFormat) + } + return nil } @@ -114,10 +122,6 @@ func filterStoreItem(items []v2.StoreFunction, fromStore string) (*v2.StoreFunct func runGenerate(cmd *cobra.Command, args []string) error { - if isStackOutput(outputFormat) && fromStore == "" { - return fmt.Errorf("--output %s can only be used with --from-store", outputFormat) - } - desiredArch, _ := cmd.Flags().GetString("arch") var services stack.Services diff --git a/commands/generate_test.go b/commands/generate_test.go index 00bb76ee..a667e029 100644 --- a/commands/generate_test.go +++ b/commands/generate_test.go @@ -3,7 +3,6 @@ package commands import ( - "strings" "testing" v2 "github.com/openfaas/faas-cli/schema/store/v2" @@ -657,17 +656,57 @@ func Test_generateStackYAML(t *testing.T) { } } -func Test_runGenerate_StackOutputRequiresFromStore(t *testing.T) { - outputFormat = "stack.yaml" - fromStore = "" - - err := runGenerate(generateCmd, []string{}) - if err == nil { - t.Fatal("expected error when --output stack.yaml is used without --from-store, got nil") +func Test_preRunGenerate_Output(t *testing.T) { + testcases := []struct { + Name string + Output string + FromStore string + ExpectError bool + }{ + { + Name: "stack.yaml output with --from-store", + Output: "stack.yaml", + FromStore: "nodeinfo", + ExpectError: false, + }, + { + Name: "stack alias with --from-store", + Output: "stack", + FromStore: "nodeinfo", + ExpectError: false, + }, + { + Name: "stack.yaml output without --from-store", + Output: "stack.yaml", + FromStore: "", + ExpectError: true, + }, + { + Name: "unknown output format", + Output: "json", + FromStore: "", + ExpectError: true, + }, + { + Name: "no output flag", + Output: "", + FromStore: "", + ExpectError: false, + }, } - if !strings.Contains(err.Error(), "--from-store") { - t.Errorf("expected error to mention --from-store, got: %s", err) + for _, testcase := range testcases { + outputFormat = testcase.Output + fromStore = testcase.FromStore + + err := preRunGenerate(generateCmd, []string{}) + if testcase.ExpectError && err == nil { + t.Fatalf("%s: expected error, got nil", testcase.Name) + } + + if !testcase.ExpectError && err != nil { + t.Fatalf("%s: expected success, got error: %s", testcase.Name, err) + } } outputFormat = ""