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

Add template pull stack #668

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions commands/fetch_templates.go
Expand Up @@ -116,3 +116,27 @@ func moveTemplates(repoPath string, overwrite bool) ([]string, []string, error)

return existingLanguages, fetchedLanguages, nil
}

func pullTemplate(repository string) error {
LucasRoesler marked this conversation as resolved.
Show resolved Hide resolved
if _, err := os.Stat(repository); err != nil {
if !versioncontrol.IsGitRemote(repository) && !versioncontrol.IsPinnedGitRemote(repository) {
return fmt.Errorf("The repository URL must be a valid git repo uri")
}
}

repository, refName := versioncontrol.ParsePinnedRemote(repository)

if err := versioncontrol.GitCheckRefName.Invoke("", map[string]string{"refname": refName}); err != nil {
fmt.Printf("Invalid tag or branch name `%s`\n", refName)
fmt.Println("See https://git-scm.com/docs/git-check-ref-format for more details of the rules Git enforces on branch and reference names.")

return err
}

fmt.Printf("Fetch templates from repository: %s at %s\n", repository, refName)
if err := fetchTemplates(repository, refName, overwrite); err != nil {
return fmt.Errorf("error while fetching templates: %s", err)
}

return nil
}
24 changes: 1 addition & 23 deletions commands/template_pull.go
Expand Up @@ -4,9 +4,7 @@ package commands

import (
"fmt"
"os"

"github.com/openfaas/faas-cli/versioncontrol"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -44,28 +42,8 @@ func runTemplatePull(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
repository = args[0]
}
repository = getTemplateURL(repository, os.Getenv(templateURLEnvironment), DefaultTemplateRepository)

if _, err := os.Stat(repository); err != nil {
if !versioncontrol.IsGitRemote(repository) && !versioncontrol.IsPinnedGitRemote(repository) {
return fmt.Errorf("The repository URL must be a valid git repo uri")
}
}

repository, refName := versioncontrol.ParsePinnedRemote(repository)

if err := versioncontrol.GitCheckRefName.Invoke("", map[string]string{"refname": refName}); err != nil {
fmt.Printf("Invalid tag or branch name `%s`\n", refName)
fmt.Println("See https://git-scm.com/docs/git-check-ref-format for more details of the rules Git enforces on branch and reference names.")

return err
}

fmt.Printf("Fetch templates from repository: %s at %s\n", repository, refName)
if err := fetchTemplates(repository, refName, overwrite); err != nil {
return fmt.Errorf("error while fetching templates: %s", err)
}
return nil
return pullTemplate(repository)
}

func pullDebugPrint(message string) {
Expand Down
111 changes: 111 additions & 0 deletions commands/template_pull_stack.go
@@ -0,0 +1,111 @@
package commands

import (
"fmt"
"io/ioutil"

"gopkg.in/yaml.v2"

"github.com/openfaas/faas-cli/stack"
"github.com/spf13/cobra"
)

var (
templateURL string
customRepoName string
)

func init() {
templatePullStackCmd.Flags().BoolVar(&overwrite, "overwrite", false, "Overwrite existing templates?")
templatePullStackCmd.Flags().BoolVar(&pullDebug, "debug", false, "Enable debug output")
templatePullStackCmd.PersistentFlags().StringVarP(&customRepoName, "repo", "r", "", "The custom name of the template repo")

templatePullCmd.AddCommand(templatePullStackCmd)
}

var templatePullStackCmd = &cobra.Command{
Use: `stack`,
Short: `Downloads templates specified in the function definition yaml file`,
Long: `Downloads templates specified in the function yaml file, in the current directory
`,
Example: `
faas-cli template pull stack
faas-cli template pull stack -f myfunction.yml
faas-cli template pull stack -r custom_repo_name
Copy link
Member

Choose a reason for hiding this comment

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

@martindekov

I get the option 1 uses stack.yml, option 2 uses a specific YAML file, but what does the final option do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if you have listed multiple repos in the yaml you can specify which one to be pulled

Copy link
Member

Choose a reason for hiding this comment

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

It seems like scope-creep, but I might have missed something. I think I'm ok with everything now but not convinced about this last feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So we always want to pull everything in the file? If that is the case there is no problem to remove it

`,
RunE: runTemplatePullStack,
}

func runTemplatePullStack(cmd *cobra.Command, args []string) error {
templatesConfig, err := loadTemplateConfig()
if err != nil {
return err
}
if len(customRepoName) > 0 {
return pullSpecificTemplate(templatesConfig, customRepoName, cmd)
}
return pullAllTemplates(templatesConfig, cmd)
}

func loadTemplateConfig() ([]stack.TemplateSource, error) {
stackConfig, err := readStackConfig()
if err != nil {
return nil, err
}
return stackConfig.StackConfig.TemplateConfigs, nil
}

func readStackConfig() (stack.Configuration, error) {
configField := stack.Configuration{}

configFieldBytes, err := ioutil.ReadFile(yamlFile)
if err != nil {
return configField, fmt.Errorf("Error while reading files %s", err.Error())
}
unmarshallErr := yaml.Unmarshal(configFieldBytes, &configField)
if unmarshallErr != nil {
return configField, fmt.Errorf("Error while reading configuration: %s", err.Error())
}
if len(configField.StackConfig.TemplateConfigs) == 0 {
return configField, fmt.Errorf("Error while reading configuration: no template repos currently configured")
}
return configField, nil
}

func pullAllTemplates(templateInfo []stack.TemplateSource, cmd *cobra.Command) error {
for _, val := range templateInfo {
fmt.Printf("Pulling template: %s from configuration file: %s\n", val.Name, yamlFile)
if len(val.Source) == 0 {
pullErr := runTemplateStorePull(cmd, []string{val.Name})
if pullErr != nil {
return pullErr
}
} else {
pullErr := pullTemplate(val.Source)
if pullErr != nil {
return pullErr
}
}
}
return nil
}

func findTemplate(templateInfo []stack.TemplateSource, customName string) (specificTemplate *stack.TemplateSource) {
for _, val := range templateInfo {
if val.Name == customName {
return &val
}
}
return nil
}

func pullSpecificTemplate(templateInfo []stack.TemplateSource, customName string, cmd *cobra.Command) error {
desiredTemplate := findTemplate(templateInfo, customName)
if desiredTemplate == nil {
return fmt.Errorf("Unable to find template repo with name: `%s`", customName)
}
if len(desiredTemplate.Source) == 0 {
return runTemplateStorePull(cmd, []string{desiredTemplate.Name})
}
return pullTemplate(desiredTemplate.Source)
}
168 changes: 168 additions & 0 deletions commands/template_pull_stack_test.go
@@ -0,0 +1,168 @@
package commands

import (
"reflect"
"testing"

"github.com/openfaas/faas-cli/stack"
)

func Test_findTemplate(t *testing.T) {
tests := []struct {
title string
desiredTemplate string
existingTemplates []stack.TemplateSource
expectedTemplate *stack.TemplateSource
}{
{
title: "Desired template is found",
desiredTemplate: "powershell",
existingTemplates: []stack.TemplateSource{
{Name: "powershell", Source: "exampleURL"},
{Name: "rust", Source: "exampleURL"},
},
expectedTemplate: &stack.TemplateSource{Name: "powershell", Source: "exampleURL"},
},
{
title: "Desired template is not found",
desiredTemplate: "golang",
existingTemplates: []stack.TemplateSource{
{Name: "powershell", Source: "exampleURL"},
{Name: "rust", Source: "exampleURL"},
},
expectedTemplate: nil,
},
}
for _, test := range tests {
t.Run(test.title, func(t *testing.T) {
result := findTemplate(test.existingTemplates, test.desiredTemplate)
if !reflect.DeepEqual(result, test.expectedTemplate) {
t.Errorf("Wanted template: `%s` got `%s`", test.expectedTemplate, result)
}
})
}
}

func Test_pullAllTemplates(t *testing.T) {
tests := []struct {
title string
existingTemplates []stack.TemplateSource
expectedError bool
}{
{
title: "Pull specific Template",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell", Source: "https://github.com/openfaas-incubator/powershell-http-template"},
{Name: "my_rust", Source: "https://github.com/openfaas-incubator/openfaas-rust-template"},
},
expectedError: false,
},
{
title: "Pull all templates",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell", Source: "https://github.com/openfaas-incubator/powershell-http-template"},
{Name: "my_rust", Source: "https://github.com/openfaas-incubator/openfaas-rust-template"},
},
expectedError: false,
},
{
title: "Pull custom template and template from store without source",
existingTemplates: []stack.TemplateSource{
{Name: "perl-alpine"},
{Name: "my_rust", Source: "https://github.com/openfaas-incubator/openfaas-rust-template"},
},
expectedError: false,
},
{
title: "Pull non-existant template",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell", Source: "invalidURL"},
{Name: "my_rust", Source: "https://github.com/openfaas-incubator/openfaas-rust-template"},
},
expectedError: true,
},
{
title: "Pull template with invalid URL",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell", Source: "invalidURL"},
},
expectedError: true,
},
{
title: "Pull template which does not exist in store",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell"},
},
expectedError: true,
},
}
for _, test := range tests {
t.Run(test.title, func(t *testing.T) {
actualError := pullAllTemplates(test.existingTemplates, templatePullStackCmd)
if actualError != nil && test.expectedError == false {
t.Errorf("Unexpected error: %s", actualError.Error())
}
})
}
}

func Test_pullSpecificTemplate(t *testing.T) {
tests := []struct {
title string
desiredTemplate string
existingTemplates []stack.TemplateSource
expectedError bool
}{
{
title: "Pull custom named template",
desiredTemplate: "my_powershell",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell", Source: "https://github.com/openfaas-incubator/powershell-http-template"},
{Name: "my_rust", Source: "https://github.com/openfaas-incubator/openfaas-rust-template"},
},
expectedError: false,
},
{
title: "Pull missing template",
desiredTemplate: "my_perl",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell", Source: "https://github.com/openfaas-incubator/powershell-http-template"},
{Name: "my_rust", Source: "https://github.com/openfaas-incubator/openfaas-rust-template"},
},
expectedError: true,
},
{
title: "Pull custom template from store",
desiredTemplate: "perl-alpine",
existingTemplates: []stack.TemplateSource{
{Name: "perl-alpine"},
{Name: "my_rust", Source: "https://github.com/openfaas-incubator/openfaas-rust-template"},
},
expectedError: false,
},
{
title: "Pull specific template with invalid URL",
desiredTemplate: "my_powershell",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell", Source: "invalidURL"},
},
expectedError: true,
},
{
title: "Pull template missing from store",
desiredTemplate: "my_powershell",
existingTemplates: []stack.TemplateSource{
{Name: "my_powershell"},
},
expectedError: true,
},
}
for _, test := range tests {
t.Run(test.title, func(t *testing.T) {
actualError := pullSpecificTemplate(test.existingTemplates, test.desiredTemplate, templatePullStackCmd)
if actualError != nil && test.expectedError == false {
t.Errorf("Unexpected error: %s", actualError.Error())
}
})
}
}
2 changes: 1 addition & 1 deletion commands/template_pull_test.go
Expand Up @@ -60,7 +60,7 @@ func Test_templatePull(t *testing.T) {
faasCmd.SetArgs([]string{"template", "pull", localTemplateRepository, "--overwrite"})
err = faasCmd.Execute()
if err != nil {
fmt.Errorf("unexpected error while executing template pull with --overwrite: %s", err.Error())
t.Errorf("unexpected error while executing template pull with --overwrite: %s", err.Error())
}

str := buf.String()
Expand Down
13 changes: 13 additions & 0 deletions stack/schema.go
Expand Up @@ -61,6 +61,19 @@ type Function struct {
Namespace string `yaml:"namespace,omitempty"`
}

type Configuration struct {
StackConfig StackConfiguration `yaml:"configuration"`
}

type StackConfiguration struct {
TemplateConfigs []TemplateSource `yaml:"templates"`
}

type TemplateSource struct {
Name string `yaml:"name"`
Source string `yaml:"source,omitempty"`
}

// FunctionResources Memory and CPU
type FunctionResources struct {
Memory string `yaml:"memory"`
Expand Down