github.com/tonto/cli@v0.0.0-20180104210444-aec958fa47db/funcfile.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	yaml "gopkg.in/yaml.v2"
    12  )
    13  
    14  var (
    15  	validFuncfileNames = [...]string{
    16  		"func.yaml",
    17  		"func.yml",
    18  		"func.json",
    19  	}
    20  )
    21  
    22  type inputMap struct {
    23  	Body interface{}
    24  }
    25  type outputMap struct {
    26  	Body interface{}
    27  }
    28  
    29  type fftest struct {
    30  	Name   string     `yaml:"name,omitempty" json:"name,omitempty"`
    31  	Input  *inputMap  `yaml:"input,omitempty" json:"input,omitempty"`
    32  	Output *outputMap `yaml:"outoutput,omitempty" json:"output,omitempty"`
    33  	Err    *string    `yaml:"err,omitempty" json:"err,omitempty"`
    34  	// Env    map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
    35  }
    36  
    37  type InputVar struct {
    38  	Name     string `yaml:"name" json:"name"`
    39  	Required bool   `yaml:"required" json:"required"`
    40  }
    41  type Expects struct {
    42  	Config []InputVar `yaml:"config" json:"config"`
    43  }
    44  
    45  type funcfile struct {
    46  	Name string `yaml:"name,omitempty" json:"name,omitempty"`
    47  
    48  	// Build params
    49  	Version    string   `yaml:"version,omitempty" json:"version,omitempty"`
    50  	Runtime    string   `yaml:"runtime,omitempty" json:"runtime,omitempty"`
    51  	Entrypoint string   `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty"`
    52  	Cmd        string   `yaml:"cmd,omitempty" json:"cmd,omitempty"`
    53  	Build      []string `yaml:"build,omitempty" json:"build,omitempty"`
    54  	Tests      []fftest `yaml:"tests,omitempty" json:"tests,omitempty"`
    55  	BuildImage string   `yaml:"build_image,omitempty" json:"build_image,omitempty"` // Image to use as base for building
    56  	RunImage   string   `yaml:"run_image,omitempty" json:"run_image,omitempty"`     // Image to use for running
    57  
    58  	// Route params
    59  	// TODO embed models.Route
    60  	Type        string              `yaml:"type,omitempty" json:"type,omitempty"`
    61  	Memory      uint64              `yaml:"memory,omitempty" json:"memory,omitempty"`
    62  	Format      string              `yaml:"format,omitempty" json:"format,omitempty"`
    63  	Timeout     *int32              `yaml:"timeout,omitempty" json:"timeout,omitempty"`
    64  	Path        string              `yaml:"path,omitempty" json:"path,omitempty"`
    65  	Config      map[string]string   `yaml:"config,omitempty" json:"config,omitempty"`
    66  	Headers     map[string][]string `yaml:"headers,omitempty" json:"headers,omitempty"`
    67  	IDLETimeout *int32              `yaml:"idle_timeout,omitempty" json:"idle_timeout,omitempty"`
    68  
    69  	// Run/test
    70  	Expects Expects `yaml:"expects,omitempty" json:"expects,omitempty"`
    71  }
    72  
    73  func (ff *funcfile) ImageName() string {
    74  	fname := ff.Name
    75  	if !strings.Contains(fname, "/") {
    76  		// then we'll prefix FN_REGISTRY
    77  		reg := os.Getenv(envFnRegistry)
    78  		if reg != "" {
    79  			if reg[len(reg)-1] != '/' {
    80  				reg += "/"
    81  			}
    82  			fname = fmt.Sprintf("%s%s", reg, fname)
    83  		}
    84  	}
    85  	if ff.Version != "" {
    86  		fname = fmt.Sprintf("%s:%s", fname, ff.Version)
    87  	}
    88  	return fname
    89  }
    90  
    91  func (ff *funcfile) RuntimeTag() (runtime, tag string) {
    92  	if ff.Runtime == "" {
    93  		return "", ""
    94  	}
    95  
    96  	rt := ff.Runtime
    97  	tagpos := strings.Index(rt, ":")
    98  	if tagpos == -1 {
    99  		return rt, ""
   100  	}
   101  
   102  	return rt[:tagpos], rt[tagpos+1:]
   103  }
   104  
   105  // findFuncfile for a func.yaml/json/yml file in path
   106  func findFuncfile(path string) (string, error) {
   107  	for _, fn := range validFuncfileNames {
   108  		fullfn := filepath.Join(path, fn)
   109  		if exists(fullfn) {
   110  			return fullfn, nil
   111  		}
   112  	}
   113  	return "", newNotFoundError("could not find function file")
   114  }
   115  func findAndParseFuncfile(path string) (fpath string, ff *funcfile, err error) {
   116  	fpath, err = findFuncfile(path)
   117  	if err != nil {
   118  		return "", nil, err
   119  	}
   120  	ff, err = parseFuncfile(fpath)
   121  	if err != nil {
   122  		return "", nil, err
   123  	}
   124  	return fpath, ff, err
   125  }
   126  
   127  func loadFuncfile() (string, *funcfile, error) {
   128  	return findAndParseFuncfile(".")
   129  }
   130  
   131  func parseFuncfile(path string) (*funcfile, error) {
   132  	ext := filepath.Ext(path)
   133  	switch ext {
   134  	case ".json":
   135  		return decodeFuncfileJSON(path)
   136  	case ".yaml", ".yml":
   137  		return decodeFuncfileYAML(path)
   138  	}
   139  	return nil, errUnexpectedFileFormat
   140  }
   141  
   142  func storeFuncfile(path string, ff *funcfile) error {
   143  	ext := filepath.Ext(path)
   144  	switch ext {
   145  	case ".json":
   146  		return encodeFuncfileJSON(path, ff)
   147  	case ".yaml", ".yml":
   148  		return encodeFuncfileYAML(path, ff)
   149  	}
   150  	return errUnexpectedFileFormat
   151  }
   152  
   153  func decodeFuncfileJSON(path string) (*funcfile, error) {
   154  	f, err := os.Open(path)
   155  	if err != nil {
   156  		return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err)
   157  	}
   158  	ff := &funcfile{}
   159  	// ff.Route = &fnmodels.Route{}
   160  	err = json.NewDecoder(f).Decode(ff)
   161  	// ff := fff.MakeFuncFile()
   162  	return ff, err
   163  }
   164  
   165  func decodeFuncfileYAML(path string) (*funcfile, error) {
   166  	b, err := ioutil.ReadFile(path)
   167  	if err != nil {
   168  		return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err)
   169  	}
   170  	ff := &funcfile{}
   171  	err = yaml.Unmarshal(b, ff)
   172  	// ff := fff.MakeFuncFile()
   173  	return ff, err
   174  }
   175  
   176  func encodeFuncfileJSON(path string, ff *funcfile) error {
   177  	f, err := os.Open(path)
   178  	if err != nil {
   179  		return fmt.Errorf("could not open %s for encoding. Error: %v", path, err)
   180  	}
   181  	return json.NewEncoder(f).Encode(ff)
   182  }
   183  
   184  func encodeFuncfileYAML(path string, ff *funcfile) error {
   185  	b, err := yaml.Marshal(ff)
   186  	if err != nil {
   187  		return fmt.Errorf("could not encode function file. Error: %v", err)
   188  	}
   189  	return ioutil.WriteFile(path, b, os.FileMode(0644))
   190  }
   191  
   192  func isFuncfile(path string, info os.FileInfo) bool {
   193  	if info.IsDir() {
   194  		return false
   195  	}
   196  
   197  	basefn := filepath.Base(path)
   198  	for _, fn := range validFuncfileNames {
   199  		if basefn == fn {
   200  			return true
   201  		}
   202  	}
   203  	return false
   204  }