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 }