mirror of https://github.com/knative/func.git
169 lines
3.6 KiB
Go
169 lines
3.6 KiB
Go
package buildpacks
|
|
|
|
import (
|
|
"archive/tar"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/client"
|
|
)
|
|
|
|
// Hack implementation of DockerClient that overrides CopyToContainer method.
|
|
// The CopyToContainer method hijacks the uploaded project stream and injects function scaffolding to it.
|
|
// It basically moves content of /workspace to /workspace/fn and then setup scaffolding code directly in /workspace.
|
|
type pyScaffoldInjector struct {
|
|
client.CommonAPIClient
|
|
invoke string
|
|
}
|
|
|
|
func (s pyScaffoldInjector) CopyToContainer(ctx context.Context, ctr, p string, r io.Reader, opts container.CopyToContainerOptions) error {
|
|
|
|
if pc, _, _, ok := runtime.Caller(1); ok &&
|
|
!strings.Contains(runtime.FuncForPC(pc).Name(), "build.copyDir") {
|
|
// We are not called by "project dir copy" so we do simple direct forward call.
|
|
return s.CommonAPIClient.CopyToContainer(ctx, ctr, p, r, opts)
|
|
}
|
|
|
|
pr, pw := io.Pipe()
|
|
go func() {
|
|
var err error
|
|
defer func() {
|
|
_ = pw.CloseWithError(err)
|
|
}()
|
|
tr := tar.NewReader(r)
|
|
tw := tar.NewWriter(pw)
|
|
|
|
for {
|
|
var hdr *tar.Header
|
|
hdr, err = tr.Next()
|
|
|
|
if err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
err = nil
|
|
break
|
|
}
|
|
return
|
|
}
|
|
|
|
if strings.HasPrefix(hdr.Name, "/workspace/") {
|
|
hdr.Name = strings.Replace(hdr.Name, "/workspace/", "/workspace/fn/", 1)
|
|
}
|
|
|
|
err = tw.WriteHeader(hdr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
_, err = io.Copy(tw, tr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
err = writePythonScaffolding(tw, s.invoke)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = tw.Close()
|
|
}()
|
|
|
|
return s.CommonAPIClient.CopyToContainer(ctx, ctr, p, pr, opts)
|
|
}
|
|
|
|
func writePythonScaffolding(tw *tar.Writer, invoke string) error {
|
|
for _, f := range []struct {
|
|
path string
|
|
content string
|
|
}{
|
|
{
|
|
path: "pyproject.toml",
|
|
content: pyprojectToml,
|
|
},
|
|
{
|
|
path: "service/main.py",
|
|
content: serviceMain(invoke),
|
|
},
|
|
{
|
|
path: "service/__init__.py",
|
|
content: "",
|
|
},
|
|
} {
|
|
err := tw.WriteHeader(&tar.Header{
|
|
Name: "/workspace/" + f.path,
|
|
Size: int64(len(f.content)),
|
|
Mode: 0644,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = tw.Write([]byte(f.content))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const pyprojectToml = `[project]
|
|
name = "service"
|
|
description = "an autogenerated service which runs a Function"
|
|
version = "0.1.0"
|
|
requires-python = ">=3.9"
|
|
license = "MIT"
|
|
dependencies = [
|
|
"func-python",
|
|
"function @ ./fn"
|
|
]
|
|
authors = [
|
|
{ name="The Knative Authors", email="knative-dev@googlegroups.com"},
|
|
]
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[tool.hatch.metadata]
|
|
allow-direct-references = true
|
|
|
|
[tool.poetry.dependencies]
|
|
python = ">=3.9,<4.0"
|
|
|
|
[tool.poetry.scripts]
|
|
script = "service.main:main"
|
|
`
|
|
|
|
func serviceMain(invoke string) string {
|
|
template := `"""
|
|
This code is glue between a user's Function and the middleware which will
|
|
expose it as a network service. This code is written on-demand when a
|
|
Function is being built, deployed or run. This will be included in the
|
|
final container.
|
|
"""
|
|
import logging
|
|
from func_python.%s import serve
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
try:
|
|
from function import new as handler # type: ignore[import]
|
|
except ImportError:
|
|
try:
|
|
from function import handle as handler # type: ignore[import]
|
|
except ImportError:
|
|
logging.error("Function must export either 'new' or 'handle'")
|
|
raise
|
|
|
|
|
|
def main():
|
|
logging.info("Functions middleware invoking user function")
|
|
serve(handler)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
`
|
|
return fmt.Sprintf(template, invoke)
|
|
}
|