mirror of https://github.com/knative/func.git
				
				
				
			
		
			
				
	
	
		
			171 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
| package function
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	ErrRepositoryNotFound        = errors.New("repository not found")
 | |
| 	ErrRepositoriesNotDefined    = errors.New("custom template repositories location not specified")
 | |
| 	ErrTemplatesNotFound         = errors.New("templates path (runtimes) not found")
 | |
| 	ErrRuntimeNotFound           = errors.New("runtime not found")
 | |
| 	ErrTemplateNotFound          = errors.New("template not found")
 | |
| 	ErrTemplateMissingRepository = errors.New("template name missing repository prefix")
 | |
| )
 | |
| 
 | |
| // Templates Manager
 | |
| type Templates struct {
 | |
| 	client *Client
 | |
| }
 | |
| 
 | |
| // newTemplates manager
 | |
| // Includes a back-reference to client (logic tree root) such
 | |
| // that the templates manager has full access to the API for
 | |
| // use in its implementations.
 | |
| func newTemplates(client *Client) *Templates {
 | |
| 	return &Templates{client: client}
 | |
| }
 | |
| 
 | |
| // List the full name of templates available for the runtime.
 | |
| // Full name is the optional repository prefix plus the template's repository
 | |
| // local name.  Default templates grouped first sans prefix.
 | |
| func (t *Templates) List(runtime string) ([]string, error) {
 | |
| 	names := []string{}
 | |
| 	extended := newSortedSet()
 | |
| 
 | |
| 	rr, err := t.client.Repositories().All()
 | |
| 	if err != nil {
 | |
| 		return []string{}, err
 | |
| 	}
 | |
| 
 | |
| 	for _, r := range rr {
 | |
| 		tt, err := r.Templates(runtime)
 | |
| 		if err != nil {
 | |
| 			return []string{}, err
 | |
| 		}
 | |
| 		for _, t := range tt {
 | |
| 			if r.Name == DefaultRepositoryName {
 | |
| 				names = append(names, t.Name)
 | |
| 			} else {
 | |
| 				extended.Add(t.Fullname())
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return append(names, extended.Items()...), nil
 | |
| }
 | |
| 
 | |
| // Template returns the named template in full form '[repo]/[name]' for the
 | |
| // specified runtime.
 | |
| // Templates from the default repository do not require the repo name prefix,
 | |
| // though it can be provided.
 | |
| func (t *Templates) Get(runtime, fullname string) (Template, error) {
 | |
| 	var (
 | |
| 		template Template
 | |
| 		repoName string
 | |
| 		tplName  string
 | |
| 		repo     Repository
 | |
| 		err      error
 | |
| 	)
 | |
| 
 | |
| 	// Split into repo and template names.
 | |
| 	// Defaults when unprefixed to DefaultRepository
 | |
| 	cc := strings.Split(fullname, "/")
 | |
| 	if len(cc) == 1 {
 | |
| 		repoName = DefaultRepositoryName
 | |
| 		tplName = fullname
 | |
| 	} else {
 | |
| 		repoName = cc[0]
 | |
| 		tplName = cc[1]
 | |
| 	}
 | |
| 
 | |
| 	// Get specified repository
 | |
| 	repo, err = t.client.Repositories().Get(repoName)
 | |
| 	if err != nil {
 | |
| 		return template, err
 | |
| 	}
 | |
| 
 | |
| 	return repo.Template(runtime, tplName)
 | |
| }
 | |
| 
 | |
| // Write a function's template to disk.
 | |
| // Returns a Function which may have been modified dependent on the content
 | |
| // of the template (which can define default Function fields, builders,
 | |
| // buildpacks, etc)
 | |
| func (t *Templates) Write(f Function) (Function, error) {
 | |
| 	// Templates require an initially valid Function to write
 | |
| 	// (has name, path, runtime etc)
 | |
| 	if err := f.Validate(); err != nil {
 | |
| 		return f, err
 | |
| 	}
 | |
| 
 | |
| 	// The Function's Template
 | |
| 	template, err := t.Get(f.Runtime, f.Template)
 | |
| 	if err != nil {
 | |
| 		return f, err
 | |
| 	}
 | |
| 
 | |
| 	// The Function's Template Repository
 | |
| 	repo, err := t.client.Repositories().Get(template.Repository)
 | |
| 	if err != nil {
 | |
| 		return f, err
 | |
| 	}
 | |
| 
 | |
| 	// Validate paths:  (repo/)[templates/]<runtime>/<template>
 | |
| 	templatesPath := repo.TemplatesPath
 | |
| 	if templatesPath == "" {
 | |
| 		templatesPath = "."
 | |
| 	}
 | |
| 	if _, err := repo.FS.Stat(templatesPath); err != nil {
 | |
| 		return f, ErrTemplatesNotFound
 | |
| 	}
 | |
| 	runtimePath := path.Join(templatesPath, template.Runtime)
 | |
| 	if _, err := repo.FS.Stat(runtimePath); err != nil {
 | |
| 		return f, ErrRuntimeNotFound
 | |
| 	}
 | |
| 	templatePath := path.Join(runtimePath, template.Name)
 | |
| 	if _, err := repo.FS.Stat(templatePath); err != nil {
 | |
| 		return f, ErrTemplateNotFound
 | |
| 	}
 | |
| 
 | |
| 	// Apply fields from the template onto the function itself (Denormalize).
 | |
| 	// The template is already the denormalized view of repo->runtime->template
 | |
| 	// so it's values are treated as defaults.
 | |
| 	// TODO: this begs the question: should the Template's manifest.yaml actually
 | |
| 	// be a partially-populated func.yaml?
 | |
| 	if f.Builder == "" { // as a special first case, this default comes from itself
 | |
| 		f.Builder = f.Builders["default"]
 | |
| 		if f.Builder == "" { // still nothing?  then use the template
 | |
| 			f.Builder = template.Builders["default"]
 | |
| 		}
 | |
| 	}
 | |
| 	if len(f.Builders) == 0 {
 | |
| 		f.Builders = template.Builders
 | |
| 	}
 | |
| 	if len(f.Buildpacks) == 0 {
 | |
| 		f.Buildpacks = template.Buildpacks
 | |
| 	}
 | |
| 	if len(f.BuildEnvs) == 0 {
 | |
| 		f.BuildEnvs = template.BuildEnvs
 | |
| 	}
 | |
| 	if f.HealthEndpoints.Liveness == "" {
 | |
| 		f.HealthEndpoints.Liveness = template.HealthEndpoints.Liveness
 | |
| 	}
 | |
| 	if f.HealthEndpoints.Readiness == "" {
 | |
| 		f.HealthEndpoints.Readiness = template.HealthEndpoints.Readiness
 | |
| 	}
 | |
| 	if f.Invocation.Format == "" {
 | |
| 		f.Invocation.Format = template.Invocation.Format
 | |
| 	}
 | |
| 
 | |
| 	// Copy the template files from the repo filesystem to the new Function's root
 | |
| 	// removing the manifest (if it exists; errors ignored)
 | |
| 	err = copy(templatePath, f.Root, repo.FS)              // copy everything
 | |
| 	_ = os.Remove(filepath.Join(f.Root, templateManifest)) // except the manifest
 | |
| 
 | |
| 	return f, err
 | |
| }
 |