mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
fnctl: improve bump call and link version to the publish process (#267)
* fnctl: improve bump call and link version to the publish process * fnctl: update README.md with latest changes for bump command
This commit is contained in:
committed by
Seif Lotfy سيف لطفي
parent
610e6aaa4d
commit
a4d44798dd
@@ -130,6 +130,7 @@ An example of a function file:
|
||||
app: myapp
|
||||
image: iron/hello
|
||||
route: "/custom/route"
|
||||
version: 0.0.1
|
||||
type: sync
|
||||
memory: 128
|
||||
config:
|
||||
@@ -150,6 +151,9 @@ route updated to use it.
|
||||
`route` (optional) allows you to overwrite the calculated route from the path
|
||||
position. You may use it to override the calculated route.
|
||||
|
||||
`version` represents current version of the function. When publishing, it is
|
||||
appended to the image as a tag.
|
||||
|
||||
`type` (optional) allows you to set the type of the route. `sync`, for functions
|
||||
whose response are sent back to the requester; or `async`, for functions that
|
||||
are started and return a task ID to customer while it executes in background.
|
||||
@@ -190,9 +194,8 @@ path result
|
||||
/app/test done
|
||||
```
|
||||
|
||||
`fnctl bump` will scan all IronFunctions for files named `VERSION` and bump
|
||||
their version according to [semver](http://semver.org/) rules. In their absence,
|
||||
it will skip.
|
||||
`fnctl bump` will scan all IronFunctions whose `version` key in function file
|
||||
follows [semver](http://semver.org/) rules and bump their version according.
|
||||
|
||||
`fnctl push` will scan all IronFunctions and push their images to Docker Hub,
|
||||
and update their routes accordingly.
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
bumper "github.com/giantswarm/semver-bump/bump"
|
||||
"github.com/giantswarm/semver-bump/storage"
|
||||
@@ -15,8 +13,6 @@ import (
|
||||
|
||||
var (
|
||||
initialVersion = "0.0.1"
|
||||
|
||||
errVersionFileNotFound = errors.New("no VERSION file found for this function")
|
||||
)
|
||||
|
||||
func bump() cli.Command {
|
||||
@@ -48,26 +44,56 @@ func (b *bumpcmd) walker(path string, info os.FileInfo, err error, w io.Writer)
|
||||
func (b *bumpcmd) bump(path string) error {
|
||||
fmt.Fprintln(b.verbwriter, "bumping version for", path)
|
||||
|
||||
dir := filepath.Dir(path)
|
||||
versionfile := filepath.Join(dir, "VERSION")
|
||||
if _, err := os.Stat(versionfile); os.IsNotExist(err) {
|
||||
return errVersionFileNotFound
|
||||
}
|
||||
|
||||
s, err := storage.NewVersionStorage("file", initialVersion)
|
||||
funcfile, err := parsefuncfile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := bumper.NewSemverBumper(s, versionfile)
|
||||
if funcfile.Version == "" {
|
||||
img, ver := imageversion(funcfile.Image)
|
||||
if ver == "" {
|
||||
return nil
|
||||
}
|
||||
funcfile.Image = img
|
||||
funcfile.Version = ver
|
||||
} else if funcfile.Version != "" && strings.Contains(funcfile.Image, ":") {
|
||||
return fmt.Errorf("cannot do version bump: this function has tag in its image name and version at same time. image: %s. version: %s", funcfile.Image, funcfile.Version)
|
||||
}
|
||||
|
||||
s, err := storage.NewVersionStorage("local", funcfile.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := bumper.NewSemverBumper(s, "")
|
||||
newver, err := version.BumpPatchVersion("", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(versionfile, []byte(newver.String()), 0666); err != nil {
|
||||
return err
|
||||
funcfile.Version = newver.String()
|
||||
|
||||
return storefuncfile(path, funcfile)
|
||||
}
|
||||
|
||||
func imageversion(image string) (name, ver string) {
|
||||
tagpos := strings.Index(image, ":")
|
||||
if tagpos == -1 {
|
||||
return image, initialVersion
|
||||
}
|
||||
|
||||
return nil
|
||||
imgname, imgver := image[:tagpos], image[tagpos+1:]
|
||||
|
||||
s, err := storage.NewVersionStorage("local", imgver)
|
||||
if err != nil {
|
||||
return imgname, ""
|
||||
}
|
||||
|
||||
version := bumper.NewSemverBumper(s, "")
|
||||
v, err := version.GetCurrentVersion()
|
||||
if err != nil {
|
||||
return imgname, ""
|
||||
}
|
||||
|
||||
return imgname, v.String()
|
||||
}
|
||||
|
||||
29
fnctl/bump_test.go
Normal file
29
fnctl/bump_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestImageversion(t *testing.T) {
|
||||
type args struct {
|
||||
image string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantName string
|
||||
wantVer string
|
||||
}{
|
||||
{"tag absent", args{"owner/imagename"}, "owner/imagename", initialVersion},
|
||||
{"non semver tag", args{"owner/imagename:tag"}, "owner/imagename", ""},
|
||||
{"semver tag (M.m.p)", args{"owner/imagename:0.0.1"}, "owner/imagename", "0.0.1"},
|
||||
{"semver tag (vM.m.p)", args{"owner/imagename:v0.0.1"}, "owner/imagename", "0.0.1"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
gotName, gotVer := imageversion(tt.args.image)
|
||||
if gotName != tt.wantName {
|
||||
t.Errorf("%q. imageversion() gotName = %v, want %v", tt.name, gotName, tt.wantName)
|
||||
}
|
||||
if gotVer != tt.wantVer {
|
||||
t.Errorf("%q. imageversion() gotVer = %v, want %v", tt.name, gotVer, tt.wantVer)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,27 +35,47 @@ var (
|
||||
)
|
||||
|
||||
type funcfile struct {
|
||||
App *string `yaml:"app,omitempty",json:"app,omitempty"`
|
||||
Image string `yaml:"image,omitempty",json:"image,omitempty"`
|
||||
Route *string `yaml:"route,omitempty",json:"route,omitempty"`
|
||||
Type *string `yaml:"type,omitempty",json:"type,omitempty"`
|
||||
Memory *int64 `yaml:"memory,omitempty",json:"memory,omitempty"`
|
||||
Config map[string]string `yaml:"config,omitempty",json:"config,omitempty"`
|
||||
Build []string `yaml:"build,omitempty",json:"build,omitempty"`
|
||||
App *string `yaml:"app,omitempty",json:"app,omitempty"`
|
||||
Image string `yaml:"image,omitempty",json:"image,omitempty"`
|
||||
Version string `yaml:"version,omitempty",json:"version,omitempty"`
|
||||
Route *string `yaml:"route,omitempty",json:"route,omitempty"`
|
||||
Type *string `yaml:"type,omitempty",json:"type,omitempty"`
|
||||
Memory *int64 `yaml:"memory,omitempty",json:"memory,omitempty"`
|
||||
Config map[string]string `yaml:"config,omitempty",json:"config,omitempty"`
|
||||
Build []string `yaml:"build,omitempty",json:"build,omitempty"`
|
||||
}
|
||||
|
||||
func (ff *funcfile) FullImage() string {
|
||||
image := ff.Image
|
||||
if ff.Version != "" {
|
||||
image = fmt.Sprintf("%s:%s", image, ff.Version)
|
||||
}
|
||||
return image
|
||||
}
|
||||
|
||||
func parsefuncfile(path string) (*funcfile, error) {
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".json":
|
||||
return funcfileJSON(path)
|
||||
return decodeFuncfileJSON(path)
|
||||
case ".yaml", ".yml":
|
||||
return funcfileYAML(path)
|
||||
return decodeFuncfileYAML(path)
|
||||
}
|
||||
return nil, errUnexpectedFileFormat
|
||||
}
|
||||
|
||||
func funcfileJSON(path string) (*funcfile, error) {
|
||||
func storefuncfile(path string, ff *funcfile) error {
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".json":
|
||||
return encodeFuncfileJSON(path, ff)
|
||||
case ".yaml", ".yml":
|
||||
return encodeFuncfileYAML(path, ff)
|
||||
}
|
||||
return errUnexpectedFileFormat
|
||||
}
|
||||
|
||||
func decodeFuncfileJSON(path string) (*funcfile, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err)
|
||||
@@ -65,7 +85,7 @@ func funcfileJSON(path string) (*funcfile, error) {
|
||||
return ff, err
|
||||
}
|
||||
|
||||
func funcfileYAML(path string) (*funcfile, error) {
|
||||
func decodeFuncfileYAML(path string) (*funcfile, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err)
|
||||
@@ -75,6 +95,22 @@ func funcfileYAML(path string) (*funcfile, error) {
|
||||
return ff, err
|
||||
}
|
||||
|
||||
func encodeFuncfileJSON(path string, ff *funcfile) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open %s for encoding. Error: %v", path, err)
|
||||
}
|
||||
return json.NewEncoder(f).Encode(ff)
|
||||
}
|
||||
|
||||
func encodeFuncfileYAML(path string, ff *funcfile) error {
|
||||
b, err := yaml.Marshal(ff)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not encode function file. Error: %v", err)
|
||||
}
|
||||
return ioutil.WriteFile(path, b, os.FileMode(0644))
|
||||
}
|
||||
|
||||
func isvalid(path string, info os.FileInfo) bool {
|
||||
if info.IsDir() {
|
||||
return false
|
||||
@@ -143,7 +179,7 @@ func (c *commoncmd) scan(walker func(path string, info os.FileInfo, err error, w
|
||||
|
||||
var walked bool
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0)
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, ' ', 0)
|
||||
fmt.Fprint(w, "path", "\t", "result", "\n")
|
||||
|
||||
err := filepath.Walk(c.wd, func(path string, info os.FileInfo, err error) error {
|
||||
@@ -222,7 +258,7 @@ func (c commoncmd) buildfunc(path string) (*funcfile, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.dockerbuild(path, funcfile.Image); err != nil {
|
||||
if err := c.dockerbuild(path, funcfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -244,8 +280,8 @@ func (c commoncmd) localbuild(path string, steps []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c commoncmd) dockerbuild(path, image string) error {
|
||||
cmd := exec.Command("docker", "build", "-t", image, filepath.Dir(path))
|
||||
func (c commoncmd) dockerbuild(path string, ff *funcfile) error {
|
||||
cmd := exec.Command("docker", "build", "-t", ff.FullImage(), filepath.Dir(path))
|
||||
cmd.Stderr = c.verbwriter
|
||||
cmd.Stdout = c.verbwriter
|
||||
if err := cmd.Run(); err != nil {
|
||||
|
||||
@@ -71,7 +71,7 @@ func (p *publishcmd) publish(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := p.dockerpush(funcfile.Image); err != nil {
|
||||
if err := p.dockerpush(funcfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ func (p *publishcmd) publish(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p publishcmd) dockerpush(image string) error {
|
||||
cmd := exec.Command("docker", "push", image)
|
||||
func (p publishcmd) dockerpush(ff *funcfile) error {
|
||||
cmd := exec.Command("docker", "push", ff.FullImage())
|
||||
cmd.Stderr = p.verbwriter
|
||||
cmd.Stdout = p.verbwriter
|
||||
if err := cmd.Run(); err != nil {
|
||||
@@ -114,7 +114,7 @@ func (p *publishcmd) route(path string, ff *funcfile) error {
|
||||
body := functions.RouteWrapper{
|
||||
Route: functions.Route{
|
||||
Path: *ff.Route,
|
||||
Image: ff.Image,
|
||||
Image: ff.FullImage(),
|
||||
Memory: *ff.Memory,
|
||||
Type_: *ff.Type,
|
||||
Config: expandEnvConfig(ff.Config),
|
||||
|
||||
@@ -52,7 +52,7 @@ func (p *pushcmd) push(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.dockerpush(funcfile.Image); err != nil {
|
||||
if err := p.dockerpush(funcfile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user