diff --git a/controller/api_test.go b/controller/api_test.go index 8600d432..4d07cf1e 100644 --- a/controller/api_test.go +++ b/controller/api_test.go @@ -81,8 +81,10 @@ func TestFunctionApi(t *testing.T) { Namespace: api.NamespaceDefault, }, Spec: fission.FunctionSpec{ - EnvironmentName: "nodejs", - Deployment: fission.FunctionPackageRef{ + Environment: fission.EnvironmentReference{ + Name: "nodejs", + }, + Package: fission.FunctionPackageRef{ FunctionName: "xxx", }, }, @@ -99,7 +101,7 @@ func TestFunctionApi(t *testing.T) { _, err = g.client.FunctionCreate(testFunc) assertNameReuseFailure(err, "function") - testFunc.Spec.Deployment.FunctionName = "yyy" + testFunc.Spec.Package.FunctionName = "yyy" _, err = g.client.FunctionUpdate(testFunc) panicIf(err) diff --git a/controller/packageApi.go b/controller/packageApi.go index 63601df1..dcb993df 100644 --- a/controller/packageApi.go +++ b/controller/packageApi.go @@ -65,7 +65,12 @@ func (a *API) PackageApiCreate(w http.ResponseWriter, r *http.Request) { } // Ensure size limits - if len(f.Spec.Literal) > 256*1024 { + if len(f.Spec.Source.Literal) > 256*1024 { + err := fission.MakeError(fission.ErrorInvalidArgument, "Package literal larger than 256K") + a.respondWithError(w, err) + return + } + if len(f.Spec.Deployment.Literal) > 256*1024 { err := fission.MakeError(fission.ErrorInvalidArgument, "Package literal larger than 256K") a.respondWithError(w, err) return @@ -104,7 +109,7 @@ func (a *API) PackageApiGet(w http.ResponseWriter, r *http.Request) { var resp []byte if raw != "" { - resp = []byte(f.Spec.Literal) + resp = []byte(f.Spec.Deployment.Literal) } else { resp, err = json.Marshal(f) if err != nil { diff --git a/environments/fetcher/fetcher.go b/environments/fetcher/fetcher.go index b74777a0..7f3c0426 100644 --- a/environments/fetcher/fetcher.go +++ b/environments/fetcher/fetcher.go @@ -155,11 +155,8 @@ func (fetcher *Fetcher) Handler(w http.ResponseWriter, r *http.Request) { // get pkg var pkg *tpr.Package - if req.FetchType == FETCH_SOURCE { - pkg, err = fetcher.fissionClient.Packages(fn.Spec.Source.PackageRef.Namespace).Get(fn.Spec.Source.PackageRef.Name) - } else if req.FetchType == FETCH_DEPLOYMENT { - pkg, err = fetcher.fissionClient.Packages(fn.Spec.Deployment.PackageRef.Namespace).Get(fn.Spec.Deployment.PackageRef.Name) - } + pkg, err = fetcher.fissionClient. + Packages(fn.Spec.Package.PackageRef.Namespace).Get(fn.Spec.Package.PackageRef.Name) if err != nil { e := fmt.Sprintf("Failed to get package: %v", err) log.Printf(e) @@ -167,10 +164,17 @@ func (fetcher *Fetcher) Handler(w http.ResponseWriter, r *http.Request) { return } + var archive *fission.Archive + if req.FetchType == FETCH_SOURCE { + archive = &pkg.Spec.Source + } else if req.FetchType == FETCH_DEPLOYMENT { + archive = &pkg.Spec.Deployment + } + // get package data as literal or by url - if len(pkg.Spec.Literal) > 0 { + if len(archive.Literal) > 0 { // write pkg.Literal into tmpPath - err = ioutil.WriteFile(tmpPath, pkg.Spec.Literal, 0600) + err = ioutil.WriteFile(tmpPath, archive.Literal, 0600) if err != nil { e := fmt.Sprintf("Failed to write file %v: %v", tmpPath, err) log.Printf(e) @@ -180,7 +184,7 @@ func (fetcher *Fetcher) Handler(w http.ResponseWriter, r *http.Request) { } else { // download and verify - err = downloadUrl(pkg.Spec.URL, tmpPath) + err = downloadUrl(archive.URL, tmpPath) if err != nil { e := fmt.Sprintf("Failed to download url %v: %v", req.Url, err) log.Printf(e) @@ -188,7 +192,7 @@ func (fetcher *Fetcher) Handler(w http.ResponseWriter, r *http.Request) { return } - err = verifyChecksum(tmpPath, &pkg.Spec.Checksum) + err = verifyChecksum(tmpPath, &archive.Checksum) if err != nil { e := fmt.Sprintf("Failed to verify checksum: %v", err) log.Printf(e) diff --git a/fission/function.go b/fission/function.go index 4f5add11..d3ab5a2d 100644 --- a/fission/function.go +++ b/fission/function.go @@ -18,7 +18,6 @@ package main import ( "context" - "errors" "fmt" "io/ioutil" "os" @@ -44,14 +43,13 @@ func fileSize(filePath string) int64 { return info.Size() } -// updatePackageSpecWithFile uploads or serializes the file into the -// provided package object -func updatePackageSpecWithFile(client *client.Client, pkgSpec *fission.PackageSpec, fileName string) { - if fileSize(fileName) < fission.PackageLiteralSizeLimit { - pkgContents := getPackageContents(fileName) - - pkgSpec.Type = fission.PackageTypeLiteral - pkgSpec.Literal = pkgContents +// upload a file and return a fission.Archive +func createArchive(client *client.Client, fileName string) *fission.Archive { + var archive fission.Archive + if fileSize(fileName) < fission.ArchiveLiteralSizeLimit { + contents := getContents(fileName) + archive.Type = fission.ArchiveTypeLiteral + archive.Literal = contents } else { u := strings.TrimSuffix(client.Url, "/") + "/proxy/storage" ssClient := storageSvcClient.MakeClient(u) @@ -62,54 +60,13 @@ func updatePackageSpecWithFile(client *client.Client, pkgSpec *fission.PackageSp archiveUrl := ssClient.GetUrl(id) - pkgSpec.Type = fission.PackageTypeUrl - pkgSpec.URL = archiveUrl + archive.Type = fission.ArchiveTypeUrl + archive.URL = archiveUrl } + return &archive } -// createPackageFromFile is a function that helps to upload the content -// of given file to controller to create a TPR package resource, and then -// return a function package reference for further usage. -func createPackageFromFile(client *client.Client, fnName string, fileName string) fission.FunctionPackageRef { - pkgName := fmt.Sprintf("%v-%v", fnName, strings.ToLower(uniuri.NewLen(6))) - pkg := &tpr.Package{ - Metadata: api.ObjectMeta{ - Name: pkgName, - Namespace: api.NamespaceDefault, - }, - } - - updatePackageSpecWithFile(client, &pkg.Spec, fileName) - - _, err := client.PackageCreate(pkg) - checkErr(err, "upload package") - - return fission.FunctionPackageRef{ - PackageRef: fission.PackageRef{ - Name: pkgName, - Namespace: pkg.Metadata.Namespace, - }, - } -} - -// updatePackageContents is a function that reads content from given file -// and updates the package content of TPR package resource. -func updatePackageContents(client *client.Client, pkgName string, fileName string) error { - pkg, err := client.PackageGet(&api.ObjectMeta{ - Name: pkgName, - Namespace: api.NamespaceDefault, - }) - if err != nil { - return errors.New(fmt.Sprintf("read package '%v'", pkgName)) - } - - updatePackageSpecWithFile(client, &pkg.Spec, fileName) - - _, err = client.PackageUpdate(pkg) - return err -} - -func getPackageContents(filePath string) []byte { +func getContents(filePath string) []byte { var code []byte var err error @@ -148,18 +105,39 @@ func fnCreate(c *cli.Context) error { Namespace: api.NamespaceDefault, }, Spec: fission.FunctionSpec{ - EnvironmentName: envName, + Environment: fission.EnvironmentReference{ + Name: envName, + Namespace: api.NamespaceDefault, + }, }, } + var pkgSpec fission.PackageSpec if len(srcPkgName) > 0 { - function.Spec.Source = createPackageFromFile(client, fnName, srcPkgName) + pkgSpec.Source = *createArchive(client, srcPkgName) } if len(deployPkgName) > 0 { - function.Spec.Deployment = createPackageFromFile(client, fnName, deployPkgName) + pkgSpec.Deployment = *createArchive(client, deployPkgName) } + pkgName := fmt.Sprintf("%v-%v", fnName, strings.ToLower(uniuri.NewLen(6))) + pkg := &tpr.Package{ + Metadata: api.ObjectMeta{ + Name: pkgName, + Namespace: api.NamespaceDefault, + }, + Spec: pkgSpec, + } + newpkg, err := client.PackageCreate(pkg) + checkErr(err, "create package") - _, err := client.FunctionCreate(function) + function.Spec.Package = fission.FunctionPackageRef{ + PackageRef: fission.PackageRef{ + Name: newpkg.Name, + Namespace: newpkg.Namespace, + ResourceVersion: newpkg.ResourceVersion, + }, + } + _, err = client.FunctionCreate(function) checkErr(err, "create function") fmt.Printf("function '%v' created\n", fnName) @@ -210,12 +188,12 @@ func fnGet(c *cli.Context) error { checkErr(err, "get function") pkg, err := client.PackageGet(&api.ObjectMeta{ - Name: fn.Spec.Deployment.PackageRef.Name, - Namespace: fn.Spec.Deployment.PackageRef.Namespace, + Name: fn.Spec.Package.PackageRef.Name, + Namespace: fn.Spec.Package.PackageRef.Namespace, }) checkErr(err, "get package") - os.Stdout.Write(pkg.Spec.Literal) + os.Stdout.Write(pkg.Spec.Deployment.Literal) return err } @@ -238,7 +216,7 @@ func fnGetMeta(c *cli.Context) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) fmt.Fprintf(w, "%v\t%v\t%v\n", "NAME", "UID", "ENV") fmt.Fprintf(w, "%v\t%v\t%v\n", - f.Metadata.Name, f.Metadata.UID, f.Spec.EnvironmentName) + f.Metadata.Name, f.Metadata.UID, f.Spec.Environment.Name) w.Flush() return err } @@ -268,31 +246,30 @@ func fnUpdate(c *cli.Context) error { fatal("Need --env or --code or --package or --srcpkg argument.") } - // Now builder manager only starts a build if a function has a source package - // but no deployment package. This behavior will be changed after we move builds - // to package level (https://github.com/fission/fission/pull/297). - - if len(srcPkgName) > 0 { - // Check the existence of the package, create it if not exist. - if len(function.Spec.Source.PackageRef.Name) > 0 { - err := updatePackageContents(client, function.Spec.Source.PackageRef.Name, srcPkgName) - checkErr(err, "update source package") - } else { - function.Spec.Source = createPackageFromFile(client, fnName, srcPkgName) + if len(deployPkgName) != 0 || len(srcPkgName) != 0 { + // get existing package + pkg, err := client.PackageGet(&api.ObjectMeta{ + Name: function.Spec.Package.PackageRef.Name, + Namespace: function.Spec.Package.PackageRef.Namespace, + }) + // update package spec + if len(srcPkgName) > 0 { + archive := createArchive(client, srcPkgName) + pkg.Spec.Source = *archive } - } - - if len(deployPkgName) > 0 { - if len(function.Spec.Deployment.PackageRef.Name) > 0 { - err := updatePackageContents(client, function.Spec.Deployment.PackageRef.Name, deployPkgName) - checkErr(err, "update source package") - } else { - function.Spec.Deployment = createPackageFromFile(client, fnName, deployPkgName) + if len(deployPkgName) > 0 { + archive := createArchive(client, deployPkgName) + pkg.Spec.Deployment = *archive } + // updage package object + newpkg, err := client.PackageUpdate(pkg) + checkErr(err, "update package") + // update function spec with resource version + function.Spec.Package.PackageRef.ResourceVersion = newpkg.ResourceVersion } if len(envName) > 0 { - function.Spec.EnvironmentName = envName + function.Spec.Environment.Name = envName } _, err = client.FunctionUpdate(function) @@ -333,7 +310,7 @@ func fnList(c *cli.Context) error { fmt.Fprintf(w, "%v\t%v\t%v\n", "NAME", "UID", "ENV") for _, f := range fns { fmt.Fprintf(w, "%v\t%v\t%v\n", - f.Metadata.Name, f.Metadata.UID, f.Spec.EnvironmentName) + f.Metadata.Name, f.Metadata.UID, f.Spec.Environment.Name) } w.Flush() diff --git a/poolmgr/api.go b/poolmgr/api.go index 2b77a2c7..35d9dd08 100644 --- a/poolmgr/api.go +++ b/poolmgr/api.go @@ -167,7 +167,7 @@ func (poolMgr *Poolmgr) getFunctionEnv(m *api.ObjectMeta) (*tpr.Environment, err // Get env from metadata log.Printf("[%v] getting env", m) - env, err = poolMgr.fissionClient.Environments(f.Metadata.Namespace).Get(f.Spec.EnvironmentName) + env, err = poolMgr.fissionClient.Environments(f.Spec.Environment.Namespace).Get(f.Spec.Environment.Name) if err != nil { return nil, err } diff --git a/tpr/tpr_test.go b/tpr/tpr_test.go index d4e5a81f..9c6ab3cc 100644 --- a/tpr/tpr_test.go +++ b/tpr/tpr_test.go @@ -46,15 +46,16 @@ func functionTests(tprClient *rest.RESTClient) { Name: "hello", }, Spec: fission.FunctionSpec{ - Source: fission.FunctionPackageRef{}, - Deployment: fission.FunctionPackageRef{ + Package: fission.FunctionPackageRef{ PackageRef: fission.PackageRef{ Name: "foo", Namespace: "bar", }, FunctionName: "hello", }, - EnvironmentName: "xxx", + Environment: fission.EnvironmentReference{ + Name: "xxx", + }, }, } @@ -74,14 +75,14 @@ func functionTests(tprClient *rest.RESTClient) { // read f, err = fi.Get(function.Metadata.Name) panicIf(err) - if f.Spec.Deployment.FunctionName != function.Spec.Deployment.FunctionName { + if f.Spec.Environment.Name != function.Spec.Environment.Name { log.Panicf("Bad result from Get: %v", f) } log.Printf("f.Metadata = %#v", f.Metadata) // update - function.Spec.EnvironmentName = "yyy" + function.Spec.Environment.Name = "yyy" f, err = fi.Update(function) panicIf(err) @@ -93,7 +94,7 @@ func functionTests(tprClient *rest.RESTClient) { if len(fl.Items) != 1 { log.Panicf("wrong count from list: %v", fl) } - if fl.Items[0].Spec.EnvironmentName != function.Spec.EnvironmentName { + if fl.Items[0].Spec.Environment.Name != function.Spec.Environment.Name { log.Panicf("bad object from list: %v", fl.Items[0]) } @@ -122,7 +123,7 @@ func functionTests(tprClient *rest.RESTClient) { if !ok { log.Panicf("Can't cast to Function") } - if wf.Spec.EnvironmentName != function.Spec.EnvironmentName { + if wf.Spec.Environment.Name != function.Spec.Environment.Name { log.Panicf("Bad object from watch: %#v", wf) } log.Printf("watch event took %v", time.Now().Sub(start)) diff --git a/types.go b/types.go index e0cab61f..e5966488 100644 --- a/types.go +++ b/types.go @@ -34,16 +34,16 @@ type ( Sum string `json:"sum"` } - // PackageType is either literal or URL, indicating whether - // the package is specified in the Package struct or + // ArchiveType is either literal or URL, indicating whether + // the package is specified in the Archive struct or // externally. - PackageType string + ArchiveType string // Package contains or references a collection of source or // binary files. - PackageSpec struct { + Archive struct { // Type defines how the package is specified: literal or URL. - Type PackageType `json:"type"` + Type ArchiveType `json:"type"` // Literal contents of the package. Can be used for // encoding packages below TODO (256KB?) size. @@ -57,12 +57,34 @@ type ( Checksum Checksum `json:"checksum"` } + EnvironmentReference struct { + Namespace string `json:"namespace"` + Name string `json:"name"` + } + + BuildStatus string + + PackageSpec struct { + Environment EnvironmentReference `json:"environment"` + Source Archive `json:"source"` + Deployment Archive `json:"deployment"` + // In the future, we can have a debug build here too + } + PackageStatus struct { + BuildStatus BuildStatus `json:"buildstatus"` + BuildLog string `json:"buildlog"` // output of the build (errors etc) + } + PackageRef struct { - Name string - Namespace string + Namespace string `json:"namespace"` + Name string `json:"name"` + + // Including resource version in the reference forces the function to be updated on + // package update, making it possible to cache the function based on its metadata. + ResourceVersion string `json:"resourceversion"` } FunctionPackageRef struct { - PackageRef PackageRef + PackageRef PackageRef `json:"packageref"` // FunctionName specifies a specific function within the package. This allows // functions to share packages, by having different functions within the same @@ -77,18 +99,13 @@ type ( // FunctionSpec describes the contents of the function. FunctionSpec struct { - // EnvironmentName is the name of the environment that this function is associated - // with. An Environment with this name should exist, otherwise the function cannot - // be invoked. - EnvironmentName string `json:"environmentName"` + // Environment is the build and runtime environment that this function is + // associated with. An Environment with this name should exist, otherwise the + // function cannot be invoked. + Environment EnvironmentReference `json:"environment"` - // Source is an source package for this function; it's used for the build step if - // the environment defines a build container. - Source FunctionPackageRef `json:"source"` - - // Deployment is a deployable package for this function. This is the package that's - // loaded into the environment's runtime container. - Deployment FunctionPackageRef `json:"deployment"` + // Reference to a package containing deployment and optionally the source + Package FunctionPackageRef `json:"package"` } FunctionReferenceType string @@ -220,12 +237,19 @@ const ( ) const ( - // PackageTypeLiteral means the package contents are specified in the Literal field of + // ArchiveTypeLiteral means the package contents are specified in the Literal field of // resource itself. - PackageTypeLiteral PackageType = "literal" + ArchiveTypeLiteral ArchiveType = "literal" - // PackageTypeUrl means the package contents are at the specified URL. - PackageTypeUrl PackageType = "url" + // ArchiveTypeUrl means the package contents are at the specified URL. + ArchiveTypeUrl ArchiveType = "url" +) + +const ( + BuildStatusPending = "pending" + BuildStatusRunning = "running" + BuildStatusSucceeded = "succeeded" + BuildStatusFailed = "failed" ) const ( @@ -266,5 +290,5 @@ var errorDescriptions = []string{ } const ( - PackageLiteralSizeLimit int64 = 256 * 1024 + ArchiveLiteralSizeLimit int64 = 256 * 1024 )