Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/buildpacks/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (b *Builder) Build(ctx context.Context, f fn.Function, platforms []fn.Platf

// set scaffolding path to buildpacks builder
if f.Runtime == "go" {
opts.Env["BP_GO_WORKDIR"] = ".func/build"
opts.Env["BP_GO_WORKDIR"] = filepath.Join(fn.RunDataDir, fn.BuildDir)
}

// Get middleware version and set as image label via BP_IMAGE_LABELS
Expand Down
2 changes: 1 addition & 1 deletion pkg/buildpacks/scaffolder.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"knative.dev/func/pkg/scaffolding"
)

const defaultPath = ".func/build"
const defaultPath = fn.RunDataDir + "/" + fn.BuildDir

// Scaffolder for buildpacks builder
type Scaffolder struct {
Expand Down
10 changes: 10 additions & 0 deletions pkg/functions/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ const (
RunDataDir = ".func"
RunDataLocalFile = "local.yaml"

// BuildDir is the subdirectory within RunDataDir for build scaffolding
// and artifacts (e.g. ".func/build").
BuildDir = "build"

// BuiltHash is a name of a file that holds hash of built Function in runtime
// metadata dir (RunDataDir)
BuiltHash = "built-hash"
Expand Down Expand Up @@ -583,6 +587,12 @@ func (f Function) Initialized() bool {
return !f.Created.IsZero()
}

// HasScaffolding returns true if the function runtime supports scaffolding.
// Scaffoldable runtimes (go, python) require a generated main wrapper to build.
func (f Function) HasScaffolding() bool {
return f.Runtime == "go" || f.Runtime == "python"
}

// LabelsMap combines default labels with the labels slice provided.
// It will the resulting slice with ValidateLabels and return a key/value map.
// - key: EXAMPLE1 # Label directly from a value
Expand Down
6 changes: 3 additions & 3 deletions pkg/oci/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -857,13 +857,13 @@ func newBuildJob(ctx context.Context, f fn.Function, pp []fn.Platform, verbose b
// some convenience accessors

func (j buildJob) buildDir() string {
return filepath.Join(j.function.Root, fn.RunDataDir, "build")
return filepath.Join(j.function.Root, fn.RunDataDir, fn.BuildDir)
}
func (j buildJob) ociDir() string {
return filepath.Join(j.function.Root, fn.RunDataDir, "build", "oci")
return filepath.Join(j.function.Root, fn.RunDataDir, fn.BuildDir, "oci")
}
func (j buildJob) blobsDir() string {
return filepath.Join(j.function.Root, fn.RunDataDir, "build", "oci", "blobs", "sha256")
return filepath.Join(j.function.Root, fn.RunDataDir, fn.BuildDir, "oci", "blobs", "sha256")
}
func (j buildJob) cacheDir() string {
return filepath.Join(j.function.Root, fn.RunDataDir, "blob-cache")
Expand Down
8 changes: 4 additions & 4 deletions pkg/oci/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func TestBuilder_BuildGo(t *testing.T) {
t.Fatal(err)
}

oci := filepath.Join(f.Root, fn.RunDataDir, "build", "oci")
oci := filepath.Join(f.Root, fn.RunDataDir, fn.BuildDir, "oci")

validateOCIStructure(oci, t) // validate OCI compliant
}
Expand Down Expand Up @@ -117,7 +117,7 @@ func TestBuilder_BuildPython(t *testing.T) {
t.Fatal(err)
}

oci := filepath.Join(f.Root, fn.RunDataDir, "build", "oci")
oci := filepath.Join(f.Root, fn.RunDataDir, fn.BuildDir, "oci")

validateOCIStructure(oci, t) // validate OCI compliant
}
Expand Down Expand Up @@ -184,7 +184,7 @@ func TestBuilder_Files(t *testing.T) {
{Path: "/func/go.mod"},
}

oci := filepath.Join(f.Root, fn.RunDataDir, "build", "oci")
oci := filepath.Join(f.Root, fn.RunDataDir, fn.BuildDir, "oci")

validateOCIFiles(oci, expected, t)
}
Expand Down Expand Up @@ -357,7 +357,7 @@ func TestBuilder_StaticEnvs(t *testing.T) {
// variables on each of the constituent containers.
// ---
// Get the images list (manifest descripors) from the index
ociPath := filepath.Join(f.Root, fn.RunDataDir, "build", "oci")
ociPath := filepath.Join(f.Root, fn.RunDataDir, fn.BuildDir, "oci")
data, err := os.ReadFile(filepath.Join(ociPath, "index.json"))
if err != nil {
t.Fatal(err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/oci/pusher.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func (p *Pusher) handleUpdates(ctx context.Context) {

// getBuildDir returns the build directory
func getBuildDir(f fn.Function) (string, error) {
dir := filepath.Join(f.Root, fn.RunDataDir, "build")
dir := filepath.Join(f.Root, fn.RunDataDir, fn.BuildDir)
if _, err := os.Stat(dir); os.IsNotExist(err) {
return dir, fmt.Errorf("build directory not found '%v'. Has it been built?", dir)
}
Expand Down
8 changes: 3 additions & 5 deletions pkg/oci/scaffolder.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import (
"knative.dev/func/pkg/scaffolding"
)

const (
defaultPath = ".func/build"
)
const defaultPath = fn.RunDataDir + "/" + fn.BuildDir

// Scaffolder for host (OCI) builder
type Scaffolder struct {
Expand All @@ -26,9 +24,9 @@ func NewScaffolder(verbose bool) *Scaffolder {
// Scaffold the function so that it can be built via oci builder.
// 'path' is an optional override. Assign "" (empty string) most of the time
func (s Scaffolder) Scaffold(ctx context.Context, f fn.Function, path string) error {
if f.Runtime != "go" && f.Runtime != "python" {
if !f.HasScaffolding() {
if s.verbose {
fmt.Println("Scaffolding skipped. Currently available for runtimes go & python")
fmt.Println("Scaffolding skipped. Runtime does not support scaffolding.")
}
return nil
}
Expand Down
9 changes: 8 additions & 1 deletion pkg/pipelines/tekton/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"text/template"
Expand All @@ -28,6 +29,8 @@ const (
// S2I related properties
defaultS2iImageScriptsUrl = "image:///usr/libexec/s2i"
quarkusS2iImageScriptsUrl = "image:///usr/local/s2i"
// URL (not a filesystem path), so forward slashes are used instead of filepath.Join.
scaffoldedS2iImageScriptsUrl = "file://" + fn.RunDataDir + "/" + fn.BuildDir + "/bin"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw path.Join() uses forward slashes.


// The branch or tag we are targeting with Pipelines (ie: main, refs/tags/*)
defaultPipelinesTargetBranch = "main"
Expand Down Expand Up @@ -153,6 +156,8 @@ func createPipelineRunTemplatePAC(f fn.Function, labels map[string]string) error
s2iImageScriptsUrl := defaultS2iImageScriptsUrl
if f.Runtime == "quarkus" {
s2iImageScriptsUrl = quarkusS2iImageScriptsUrl
} else if f.HasScaffolding() {
s2iImageScriptsUrl = scaffoldedS2iImageScriptsUrl
}

image := f.Deploy.Image
Expand Down Expand Up @@ -349,12 +354,14 @@ func createAndApplyPipelineRunTemplate(f fn.Function, namespace string, labels m

// add BP_GO_WORKDIR for go-build buildpack
if f.Runtime == "go" {
buildEnvs = append(buildEnvs, "BP_GO_WORKDIR=.func/build")
buildEnvs = append(buildEnvs, "BP_GO_WORKDIR="+filepath.Join(fn.RunDataDir, fn.BuildDir))
}

s2iImageScriptsUrl := defaultS2iImageScriptsUrl
if f.Runtime == "quarkus" {
s2iImageScriptsUrl = quarkusS2iImageScriptsUrl
} else if f.HasScaffolding() {
s2iImageScriptsUrl = scaffoldedS2iImageScriptsUrl
}

// Determine if TLS verification should be skipped
Expand Down
14 changes: 7 additions & 7 deletions pkg/s2i/assemblers.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func assembler(f fn.Function) (string, error) {
// GoAssembler
//
// Adapted from /usr/libexec/s2i/assemble within the UBI-8 go-toolchain
// such that the "go build" command builds subdirectory .s2i/build
// such that the "go build" command builds subdirectory .func/build
// (where main resides) rather than the root.
const GoAssembler = `
#!/bin/bash
Expand Down Expand Up @@ -76,7 +76,7 @@ if [[ $(go list -f {{.Incomplete}}) == "true" ]]; then
/$STI_SCRIPTS_PATH/usage
exit 1
else
pushd .s2i/build
pushd .func/build
go mod tidy
go build -o /opt/app-root/gobinary
popd
Expand All @@ -87,9 +87,9 @@ fi
// PythonAssembler
//
// Adapted from /usr/libexec/s2i/assemble within the UBI-8 python-toolchain
// such that the script executes from subdirectory .s2i/builds/last
// such that the script executes from subdirectory .func/build
// (where main resides) rather than the root, and indicates the main is
// likewise in .s2i/build/service/main.py via Procfile. See the comment
// likewise in .func/build/service/main.py via Procfile. See the comment
// inline on line 50 of the script for where the directory change instruction
// was added.
const PythonAssembler = `
Expand Down Expand Up @@ -151,12 +151,12 @@ echo "---> (Functions) Writing app.sh ..."
cat << 'EOF' > app.sh
#!/bin/bash
set -e
exec python .s2i/build/service/main.py
exec python .func/build/service/main.py
EOF
chmod +x app.sh

echo "---> (Functions) Changing directory to .s2i/build ..."
cd .s2i/build
echo "---> (Functions) Changing directory to .func/build ..."
cd .func/build
# END MODIFICATION FOR FUNCTIONS
# ------------------------------

Expand Down
13 changes: 10 additions & 3 deletions pkg/s2i/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,18 @@ func (b *Builder) Build(ctx context.Context, f fn.Function, platforms []fn.Platf
// https://github.com/openshift/source-to-image/issues/1141
ForceCopy: true,
// Excludes
// Do not include .git, .env, .func or any language-specific cache directories
// Do not include .git, .env or any language-specific cache directories
// (node_modules, etc) in the tar file sent to the builder, as this both
// bloats the build process and can cause unexpected errors in the resultant
// function.
ExcludeRegExp: "(^|/)\\.git|\\.env|\\.func|node_modules(/|$)",
// function. Note: .func is NOT excluded as it contains scaffolding for go/python.
ExcludeRegExp: "(^|/)\\.git|\\.env|node_modules(/|$)",
}

// Tell S2I where to find the assemble script written by Scaffolder.
// Use an absolute path because s2i's FileURLReader resolves file:// URLs
// relative to the process CWD, not relative to the source directory.
if f.HasScaffolding() {
cfg.ScriptsURL = "file://" + filepath.Join(f.Root, fn.RunDataDir, fn.BuildDir, "bin")
}

// Set middleware version label
Expand Down
47 changes: 47 additions & 0 deletions pkg/s2i/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"errors"
"io"
"os"
"path/filepath"
"testing"

"github.com/docker/docker/api/types"
Expand Down Expand Up @@ -356,3 +358,48 @@ func (n notFoundErr) NotFound() {

// Just a type assert in case docker decides to change NotFoundError interface again
var _ errdefs.ErrNotFound = notFoundErr{}

// Test_ScaffoldWritesToFuncBuild ensures that scaffolding for Go/Python
// runtimes is written to .func/build/ instead of .s2i/build/
func Test_ScaffoldWritesToFuncBuild(t *testing.T) {
var (
root, done = Mktemp(t)
f = fn.Function{
Name: "test",
Root: root,
Runtime: "go",
Registry: "example.com/alice",
}
scaffolder = s2i.NewScaffolder(false)
err error
)
defer done()

// Initialize the test function
if f, err = fn.New().Init(f); err != nil {
t.Fatal(err)
}

// Call Scaffold
if err := scaffolder.Scaffold(context.Background(), f, ""); err != nil {
t.Fatal(err)
}

// Assert: scaffolding should be in .func/build/
expectedPath := filepath.Join(root, fn.RunDataDir, fn.BuildDir)
if _, err := os.Stat(expectedPath); os.IsNotExist(err) {
t.Errorf("expected scaffolding at %s, but directory does not exist", expectedPath)
}

// Assert: S2I scripts should be at .func/build/bin/
scriptsPath := filepath.Join(root, fn.RunDataDir, fn.BuildDir, "bin", "assemble")
if _, err := os.Stat(scriptsPath); os.IsNotExist(err) {
t.Errorf("expected assemble script at %s, but file does not exist", scriptsPath)
}

// Assert: .s2i directory should NOT exist at root level
s2iPath := filepath.Join(root, ".s2i")
if _, err := os.Stat(s2iPath); err == nil {
t.Errorf(".s2i directory should not exist at root level, but found at %s", s2iPath)
}
}
15 changes: 7 additions & 8 deletions pkg/s2i/scaffolder.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import (
"knative.dev/func/pkg/scaffolding"
)

const (
defaultPath = ".s2i/build"
)
const defaultPath = fn.RunDataDir + "/" + fn.BuildDir

// Scaffolder for S2I builder
type Scaffolder struct {
Expand All @@ -26,9 +24,9 @@ func NewScaffolder(verbose bool) *Scaffolder {
// Scaffold the function so that it can be built via s2i builder.
// 'path' is an optional override. Assign "" (empty string) most of the time
func (s Scaffolder) Scaffold(ctx context.Context, f fn.Function, path string) error {
if f.Runtime != "go" && f.Runtime != "python" {
if !f.HasScaffolding() {
if s.verbose {
fmt.Println("Scaffolding skipped. Currently available for runtimes go & python")
fmt.Println("Scaffolding skipped. Runtime does not support scaffolding.")
}
return nil
}
Expand All @@ -55,10 +53,11 @@ func (s Scaffolder) Scaffold(ctx context.Context, f fn.Function, path string) er
return err
}
if assemble != "" {
if err := os.MkdirAll(filepath.Join(f.Root, ".s2i", "bin"), 0755); err != nil {
return fmt.Errorf("unable to create .s2i bin dir. %w", err)
binDir := filepath.Join(appRoot, "bin")
if err := os.MkdirAll(binDir, 0755); err != nil {
return fmt.Errorf("unable to create scaffolding bin dir. %w", err)
}
if err := os.WriteFile(filepath.Join(f.Root, ".s2i", "bin", "assemble"), []byte(assemble), 0700); err != nil {
if err := os.WriteFile(filepath.Join(binDir, "assemble"), []byte(assemble), 0700); err != nil {
return fmt.Errorf("unable to write assembler script. %w", err)
}
}
Expand Down
Loading
Loading