github.com/devcamcar/cli@v0.0.0-20181107134215-706a05759d18/common/funcfile.go (about) 1 package common 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "github.com/fnproject/cli/config" 12 "github.com/spf13/viper" 13 14 yaml "gopkg.in/yaml.v2" 15 ) 16 17 var ( 18 validFuncfileNames = [...]string{ 19 "func.yaml", 20 "func.yml", 21 "func.json", 22 } 23 ) 24 25 // InputMap to be used within FFTest. 26 type InputMap struct { 27 Body interface{} 28 } 29 30 // OutputMap to be used within FFTest. 31 type OutputMap struct { 32 Body interface{} 33 } 34 35 // FFTest represents a test for a funcfile. 36 type FFTest struct { 37 Name string `yaml:"name,omitempty" json:"name,omitempty"` 38 Input *InputMap `yaml:"input,omitempty" json:"input,omitempty"` 39 Output *OutputMap `yaml:"outoutput,omitempty" json:"output,omitempty"` 40 Err *string `yaml:"err,omitempty" json:"err,omitempty"` 41 // Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"` 42 } 43 44 type inputVar struct { 45 Name string `yaml:"name" json:"name"` 46 Required bool `yaml:"required" json:"required"` 47 } 48 49 // Expects represents expected env vars in funcfile. 50 type Expects struct { 51 Config []inputVar `yaml:"config" json:"config"` 52 } 53 54 // FuncFile defines the internal structure of a func.yaml/json/yml 55 type FuncFile struct { 56 Name string `yaml:"name,omitempty" json:"name,omitempty"` 57 58 // Build params 59 Version string `yaml:"version,omitempty" json:"version,omitempty"` 60 Runtime string `yaml:"runtime,omitempty" json:"runtime,omitempty"` 61 Entrypoint string `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty"` 62 Cmd string `yaml:"cmd,omitempty" json:"cmd,omitempty"` 63 Build []string `yaml:"build,omitempty" json:"build,omitempty"` 64 Tests []FFTest `yaml:"tests,omitempty" json:"tests,omitempty"` 65 BuildImage string `yaml:"build_image,omitempty" json:"build_image,omitempty"` // Image to use as base for building 66 RunImage string `yaml:"run_image,omitempty" json:"run_image,omitempty"` // Image to use for running 67 ContentType string `yaml:"content_type,omitempty" json:"content_type,omitempty"` 68 69 // Route params 70 // TODO embed models.Route 71 Type string `yaml:"type,omitempty" json:"type,omitempty"` 72 Memory uint64 `yaml:"memory,omitempty" json:"memory,omitempty"` 73 Cpus string `yaml:"cpus,omitempty" json:"cpus,omitempty"` 74 Format string `yaml:"format,omitempty" json:"format,omitempty"` 75 Timeout *int32 `yaml:"timeout,omitempty" json:"timeout,omitempty"` 76 Path string `yaml:"path,omitempty" json:"path,omitempty"` 77 Config map[string]string `yaml:"config,omitempty" json:"config,omitempty"` 78 Headers map[string][]string `yaml:"headers,omitempty" json:"headers,omitempty"` 79 IDLETimeout *int32 `yaml:"idle_timeout,omitempty" json:"idle_timeout,omitempty"` 80 Annotations map[string]interface{} `yaml:"annotations,omitempty" json:"annotations,omitempty"` 81 82 // Run/test 83 Expects Expects `yaml:"expects,omitempty" json:"expects,omitempty"` 84 } 85 86 // FuncFileV20180708 defines the latest internal structure of a func.yaml/json/yml 87 type FuncFileV20180708 struct { 88 Schema_version int `yaml:"schema_version,omitempty" json:"schema_version,omitempty"` 89 90 Name string `yaml:"name,omitempty" json:"name,omitempty"` 91 Version string `yaml:"version,omitempty" json:"version,omitempty"` 92 Runtime string `yaml:"runtime,omitempty" json:"runtime,omitempty"` 93 Build_image string `yaml:"build_image,omitempty" json:"build_image,omitempty"` // Image to use as base for building 94 Run_image string `yaml:"run_image,omitempty" json:"run_image,omitempty"` // Image to use for running 95 Cmd string `yaml:"cmd,omitempty" json:"cmd,omitempty"` 96 Entrypoint string `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty"` 97 Content_type string `yaml:"content_type,omitempty" json:"content_type,omitempty"` 98 Format string `yaml:"format,omitempty" json:"format,omitempty"` 99 Type string `yaml:"type,omitempty" json:"type,omitempty"` 100 Memory uint64 `yaml:"memory,omitempty" json:"memory,omitempty"` 101 Timeout *int32 `yaml:"timeout,omitempty" json:"timeout,omitempty"` 102 IDLE_timeout *int32 `yaml:"idle_timeout,omitempty" json:"idle_timeout,omitempty"` 103 104 Config map[string]string `yaml:"config,omitempty" json:"config,omitempty"` 105 Annotations map[string]interface{} `yaml:"annotations,omitempty" json:"annotations,omitempty"` 106 107 Build []string `yaml:"build,omitempty" json:"build,omitempty"` 108 109 Expects Expects `yaml:"expects,omitempty" json:"expects,omitempty"` 110 Triggers []Trigger `yaml:"triggers,omitempty" json:"triggers,omitempty"` 111 } 112 113 // Trigger represents a trigger for a FuncFileV20180708 114 type Trigger struct { 115 Name string `yaml:"name,omitempty" json:"name,omitempty"` 116 Type string `yaml:"type,omitempty" json:"type,omitempty"` 117 Source string `yaml:"source,omitempty" json:"source,omitempty"` 118 } 119 120 // ImageName returns the name of a funcfile image 121 func (ff *FuncFile) ImageName() string { 122 fname := ff.Name 123 if !strings.Contains(fname, "/") { 124 125 reg := viper.GetString(config.EnvFnRegistry) 126 if reg != "" { 127 if reg[len(reg)-1] != '/' { 128 reg += "/" 129 } 130 fname = fmt.Sprintf("%s%s", reg, fname) 131 } 132 } 133 if ff.Version != "" { 134 fname = fmt.Sprintf("%s:%s", fname, ff.Version) 135 } 136 return fname 137 } 138 139 // RuntimeTag returns the runtime and tag. 140 func (ff *FuncFile) RuntimeTag() (runtime, tag string) { 141 if ff.Runtime == "" { 142 return "", "" 143 } 144 145 rt := ff.Runtime 146 tagpos := strings.Index(rt, ":") 147 if tagpos == -1 { 148 return rt, "" 149 } 150 151 return rt[:tagpos], rt[tagpos+1:] 152 } 153 154 // findFuncfile for a func.yaml/json/yml file in path 155 func FindFuncfile(path string) (string, error) { 156 for _, fn := range validFuncfileNames { 157 fullfn := filepath.Join(path, fn) 158 if Exists(fullfn) { 159 return fullfn, nil 160 } 161 } 162 return "", NewNotFoundError("could not find function file") 163 } 164 165 // FindAndParseFuncfile for a func.yaml/json/yml file. 166 func FindAndParseFuncfile(path string) (fpath string, ff *FuncFile, err error) { 167 fpath, err = FindFuncfile(path) 168 if err != nil { 169 return "", nil, err 170 } 171 ff, err = ParseFuncfile(fpath) 172 if err != nil { 173 return "", nil, err 174 } 175 return fpath, ff, err 176 } 177 178 // LoadFuncfile returns a parsed funcfile. 179 func LoadFuncfile(path string) (string, *FuncFile, error) { 180 return FindAndParseFuncfile(path) 181 } 182 183 // ParseFuncfile check file type to decode and parse. 184 func ParseFuncfile(path string) (*FuncFile, error) { 185 ext := filepath.Ext(path) 186 switch ext { 187 case ".json": 188 return decodeFuncfileJSON(path) 189 case ".yaml", ".yml": 190 return decodeFuncfileYAML(path) 191 } 192 return nil, errUnexpectedFileFormat 193 } 194 195 func storeFuncfile(path string, ff *FuncFile) error { 196 ext := filepath.Ext(path) 197 switch ext { 198 case ".json": 199 return encodeFuncfileJSON(path, ff) 200 case ".yaml", ".yml": 201 return EncodeFuncfileYAML(path, ff) 202 } 203 return errUnexpectedFileFormat 204 } 205 206 func decodeFuncfileJSON(path string) (*FuncFile, error) { 207 f, err := os.Open(path) 208 if err != nil { 209 return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err) 210 } 211 ff := &FuncFile{} 212 // ff.Route = &fnmodels.Route{} 213 err = json.NewDecoder(f).Decode(ff) 214 // ff := fff.MakeFuncFile() 215 return ff, err 216 } 217 218 func decodeFuncfileYAML(path string) (*FuncFile, error) { 219 b, err := ioutil.ReadFile(path) 220 if err != nil { 221 return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err) 222 } 223 ff := &FuncFile{} 224 err = yaml.Unmarshal(b, ff) 225 // ff := fff.MakeFuncFile() 226 return ff, err 227 } 228 229 func encodeFuncfileJSON(path string, ff *FuncFile) error { 230 f, err := os.Open(path) 231 if err != nil { 232 return fmt.Errorf("could not open %s for encoding. Error: %v", path, err) 233 } 234 return json.NewEncoder(f).Encode(ff) 235 } 236 237 // EncodeFuncfileYAML encodes function file. 238 func EncodeFuncfileYAML(path string, ff *FuncFile) error { 239 b, err := yaml.Marshal(ff) 240 if err != nil { 241 return fmt.Errorf("could not encode function file. Error: %v", err) 242 } 243 return ioutil.WriteFile(path, b, os.FileMode(0644)) 244 } 245 246 // IsFuncFile check vaid funcfile. 247 func IsFuncFile(path string, info os.FileInfo) bool { 248 if info.IsDir() { 249 return false 250 } 251 252 basefn := filepath.Base(path) 253 for _, fn := range validFuncfileNames { 254 if basefn == fn { 255 return true 256 } 257 } 258 return false 259 } 260 261 // --------- FuncFileV20180708 ------------- 262 263 func FindAndParseFuncFileV20180708(path string) (fpath string, ff *FuncFileV20180708, err error) { 264 fpath, err = FindFuncfile(path) 265 if err != nil { 266 return "", nil, err 267 } 268 ff, err = ParseFuncFileV20180708(fpath) 269 if err != nil { 270 return "", nil, err 271 } 272 return fpath, ff, err 273 } 274 275 func LoadFuncFileV20180708(path string) (string, *FuncFileV20180708, error) { 276 return FindAndParseFuncFileV20180708(path) 277 } 278 279 func ParseFuncFileV20180708(path string) (*FuncFileV20180708, error) { 280 ext := filepath.Ext(path) 281 switch ext { 282 case ".json": 283 return decodeFuncFileV20180708JSON(path) 284 case ".yaml", ".yml": 285 return decodeFuncFileV20180708YAML(path) 286 } 287 return nil, errUnexpectedFileFormat 288 } 289 290 func decodeFuncFileV20180708JSON(path string) (*FuncFileV20180708, error) { 291 f, err := os.Open(path) 292 if err != nil { 293 return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err) 294 } 295 ff := &FuncFileV20180708{} 296 // ff.Route = &fnmodels.Route{} 297 err = json.NewDecoder(f).Decode(ff) 298 // ff := fff.MakeFuncFile() 299 return ff, err 300 } 301 302 func decodeFuncFileV20180708YAML(path string) (*FuncFileV20180708, error) { 303 b, err := ioutil.ReadFile(path) 304 if err != nil { 305 return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err) 306 } 307 ff := &FuncFileV20180708{} 308 err = yaml.Unmarshal(b, ff) 309 // ff := fff.MakeFuncFile() 310 return ff, err 311 } 312 313 func encodeFuncFileV20180708JSON(path string, ff *FuncFileV20180708) error { 314 f, err := os.Open(path) 315 if err != nil { 316 return fmt.Errorf("could not open %s for encoding. Error: %v", path, err) 317 } 318 return json.NewEncoder(f).Encode(ff) 319 } 320 321 // EncodeFuncfileYAML encodes function file. 322 func EncodeFuncFileV20180708YAML(path string, ff *FuncFileV20180708) error { 323 b, err := yaml.Marshal(ff) 324 if err != nil { 325 return fmt.Errorf("could not encode function file. Error: %v", err) 326 } 327 return ioutil.WriteFile(path, b, os.FileMode(0644)) 328 } 329 330 func storeFuncFileV20180708(path string, ff *FuncFileV20180708) error { 331 ext := filepath.Ext(path) 332 switch ext { 333 case ".json": 334 return encodeFuncFileV20180708JSON(path, ff) 335 case ".yaml", ".yml": 336 return EncodeFuncFileV20180708YAML(path, ff) 337 } 338 return errUnexpectedFileFormat 339 } 340 341 // ImageName returns the name of a funcfile image 342 func (ff *FuncFileV20180708) ImageNameV20180708() string { 343 fname := ff.Name 344 if !strings.Contains(fname, "/") { 345 346 reg := viper.GetString(config.EnvFnRegistry) 347 if reg != "" { 348 if reg[len(reg)-1] != '/' { 349 reg += "/" 350 } 351 fname = fmt.Sprintf("%s%s", reg, fname) 352 } 353 } 354 if ff.Version != "" { 355 fname = fmt.Sprintf("%s:%s", fname, ff.Version) 356 } 357 return fname 358 }