Skip to content

Commit c2e4cf1

Browse files
authored
new scaffolding (#3237)
1 parent 7b16655 commit c2e4cf1

30 files changed

+542
-496
lines changed

cmd/build.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ For more options, run 'func build --help'`, err)
226226
if err != nil {
227227
return
228228
}
229+
230+
if err = client.Scaffold(cmd.Context(), f, ""); err != nil {
231+
return
232+
}
233+
229234
if f, err = client.Build(cmd.Context(), f, buildOptions...); err != nil {
230235
return
231236
}
@@ -479,6 +484,7 @@ func (c buildConfig) clientOptions() ([]fn.Option, error) {
479484
switch c.Builder {
480485
case builders.Host:
481486
o = append(o,
487+
fn.WithScaffolder(oci.NewScaffolder(c.Verbose)),
482488
fn.WithBuilder(oci.NewBuilder(builders.Host, c.Verbose)),
483489
fn.WithPusher(oci.NewPusher(c.RegistryInsecure, false, c.Verbose,
484490
oci.WithTransport(newTransport(c.RegistryInsecure)),
@@ -487,6 +493,7 @@ func (c buildConfig) clientOptions() ([]fn.Option, error) {
487493
)
488494
case builders.Pack:
489495
o = append(o,
496+
fn.WithScaffolder(buildpacks.NewScaffolder(c.Verbose)),
490497
fn.WithBuilder(buildpacks.NewBuilder(
491498
buildpacks.WithName(builders.Pack),
492499
buildpacks.WithTimestamp(c.WithTimestamp),
@@ -497,6 +504,7 @@ func (c buildConfig) clientOptions() ([]fn.Option, error) {
497504
docker.WithVerbose(c.Verbose))))
498505
case builders.S2I:
499506
o = append(o,
507+
fn.WithScaffolder(s2i.NewScaffolder(c.Verbose)),
500508
fn.WithBuilder(s2i.NewBuilder(
501509
s2i.WithName(builders.S2I),
502510
s2i.WithVerbose(c.Verbose))),

cmd/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) {
6666
fn.WithVerbose(cfg.Verbose),
6767
fn.WithTransport(t),
6868
fn.WithRepositoriesPath(config.RepositoriesPath()),
69+
fn.WithScaffolder(buildpacks.NewScaffolder(cfg.Verbose)),
6970
fn.WithBuilder(buildpacks.NewBuilder(buildpacks.WithVerbose(cfg.Verbose))),
7071
fn.WithRemovers(knative.NewRemover(cfg.Verbose), k8s.NewRemover(cfg.Verbose)),
7172
fn.WithDescribers(knative.NewDescriber(cfg.Verbose), k8s.NewDescriber(cfg.Verbose)),

cmd/deploy.go

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -458,12 +458,7 @@ For more options, run 'func deploy --help'`, err)
458458
if buildOptions, err = cfg.buildOptions(); err != nil {
459459
return
460460
}
461-
462-
var (
463-
digested bool
464-
justBuilt bool
465-
justPushed bool
466-
)
461+
var digested bool
467462

468463
// Validate the image and check whether its digested or not
469464
if cfg.Image != "" {
@@ -483,20 +478,30 @@ For more options, run 'func deploy --help'`, err)
483478
if digested {
484479
f.Deploy.Image = cfg.Image
485480
} else {
486-
// NOT digested, build & push the Function unless specified otherwise
487-
if f, justBuilt, err = build(cmd, cfg.Build, f, client, buildOptions); err != nil {
481+
// NOT digested: scaffold, build & push as per config
482+
var (
483+
shouldBuild bool
484+
justPushed bool
485+
)
486+
if shouldBuild, err = build(cmd, cfg.Build, f); err != nil {
488487
return
489488
}
489+
if shouldBuild {
490+
if err = client.Scaffold(cmd.Context(), f, ""); err != nil {
491+
return
492+
}
493+
if f, err = client.Build(cmd.Context(), f, buildOptions...); err != nil {
494+
return
495+
}
496+
}
490497
if cfg.Push {
491498
if f, justPushed, err = client.Push(cmd.Context(), f); err != nil {
492499
return
493500
}
494501
}
495-
// TODO: gauron99 - temporary fix for undigested image direct deploy
496-
// (w/out build) This might be more complex to do than leaving like this
497-
// image digests are created via the registry on push.
498-
if (justBuilt || justPushed) && f.Build.Image != "" {
499-
// f.Build.Image is set in Push for now, just set it as a deployed image
502+
// image was just built and pushed to registry => potentially new image
503+
if (shouldBuild || justPushed) && f.Build.Image != "" {
504+
// f.Build.Image is set when pushed to registry, just set it as a deployed image
500505
f.Deploy.Image = f.Build.Image
501506
}
502507
}
@@ -523,33 +528,23 @@ For more options, run 'func deploy --help'`, err)
523528
return f.Stamp()
524529
}
525530

526-
// build when flag == 'auto' and the function is out-of-date, or when the
527-
// flag value is explicitly truthy such as 'true' or '1'. Error if flag
528-
// is neither 'auto' nor parseable as a boolean. Return CLI-specific error
529-
// message verbeage suitable for both Deploy and Run commands which feature an
530-
// optional build step. Boolean return value signifies if the image has gone
531-
// through a build process.
532-
func build(cmd *cobra.Command, flag string, f fn.Function, client *fn.Client, buildOptions []fn.BuildOption) (fn.Function, bool, error) {
533-
var err error
531+
// build determines if the function should be built based on given flag
532+
func build(cmd *cobra.Command, flag string, f fn.Function) (bool, error) {
534533
if flag == "auto" {
535534
if f.Built() {
536535
fmt.Fprintln(cmd.OutOrStdout(), "function up-to-date. Force rebuild with --build")
537-
return f, false, nil
536+
return false, nil
538537
} else {
539-
if f, err = client.Build(cmd.Context(), f, buildOptions...); err != nil {
540-
return f, false, err
541-
}
538+
return true, nil
542539
}
543540
} else if build, _ := strconv.ParseBool(flag); build {
544-
if f, err = client.Build(cmd.Context(), f, buildOptions...); err != nil {
545-
return f, false, err
546-
}
547-
} else if _, err = strconv.ParseBool(flag); err != nil {
548-
return f, false, fmt.Errorf("invalid value for the build flag (%q), valid value is either 'auto' or a boolean", flag)
541+
return true, nil
542+
} else if _, err := strconv.ParseBool(flag); err != nil {
543+
return false, fmt.Errorf("invalid value for the build flag (%q), valid value is either 'auto' or a boolean", flag)
549544
} else if !build {
550-
return f, false, nil
545+
return false, nil
551546
}
552-
return f, true, nil
547+
return false, nil
553548
}
554549

555550
func NewRegistryValidator(path string) survey.Validator {

cmd/func-util/main.go

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/openshift/source-to-image/pkg/cmd/cli"
1919
"k8s.io/klog/v2"
2020

21+
"knative.dev/func/pkg/buildpacks"
2122
fn "knative.dev/func/pkg/functions"
2223
"knative.dev/func/pkg/k8s"
2324
"knative.dev/func/pkg/knative"
@@ -76,12 +77,12 @@ func socat(ctx context.Context) error {
7677
}
7778

7879
func scaffold(ctx context.Context) error {
79-
80-
if len(os.Args) != 2 {
81-
return fmt.Errorf("expected exactly one positional argument (function project path)")
80+
if len(os.Args) != 3 {
81+
return fmt.Errorf("expected exactly 2 positional arguments (function project path & builder)")
8282
}
8383

8484
path := os.Args[1]
85+
builder := os.Args[2]
8586

8687
f, err := fn.NewFunction(path)
8788
if err != nil {
@@ -107,38 +108,17 @@ func scaffold(ctx context.Context) error {
107108
return fmt.Errorf("cannot write middleware version as a file: %w", err)
108109
}
109110

110-
if f.Runtime != "go" && f.Runtime != "python" {
111-
// Scaffolding is for now supported/needed only for Go/Python.
112-
return nil
113-
}
114-
115-
appRoot := filepath.Join(f.Root, ".s2i", "builds", "last")
116-
_ = os.RemoveAll(appRoot)
117-
118-
err = scaffolding.Write(appRoot, f.Root, f.Runtime, f.Invoke, embeddedRepo.FS())
119-
if err != nil {
120-
return fmt.Errorf("cannot write the scaffolding: %w", err)
121-
}
122-
123-
if err := os.MkdirAll(filepath.Join(f.Root, ".s2i", "bin"), 0755); err != nil {
124-
return fmt.Errorf("unable to create .s2i bin dir. %w", err)
125-
}
126-
127-
var asm string
128-
switch f.Runtime {
129-
case "go":
130-
asm = s2i.GoAssembler
131-
case "python":
132-
asm = s2i.PythonAssembler
111+
var scaffolder fn.Scaffolder
112+
switch builder {
113+
case "pack":
114+
scaffolder = buildpacks.NewScaffolder(true)
115+
case "s2i":
116+
scaffolder = s2i.NewScaffolder(true)
133117
default:
134-
panic("unreachable")
118+
return fmt.Errorf("unknown builder in func-util image '%v'", builder)
135119
}
136120

137-
if err := os.WriteFile(filepath.Join(f.Root, ".s2i", "bin", "assemble"), []byte(asm), 0755); err != nil {
138-
return fmt.Errorf("unable to write go assembler. %w", err)
139-
}
140-
141-
return nil
121+
return fn.New(fn.WithScaffolder(scaffolder)).Scaffold(ctx, f, "")
142122
}
143123

144124
func s2iCmd(ctx context.Context) error {
@@ -151,10 +131,6 @@ func s2iCmd(ctx context.Context) error {
151131
func deploy(ctx context.Context) error {
152132
const imageDigestFileName = "image-digest"
153133
var err error
154-
deployer := knative.NewDeployer(
155-
knative.WithDeployerVerbose(true),
156-
knative.WithDeployerDecorator(deployDecorator{}))
157-
158134
var root string
159135
if len(os.Args) > 1 {
160136
root = os.Args[1]
@@ -182,7 +158,14 @@ func deploy(ctx context.Context) error {
182158
f.Deploy.Image = f.Image
183159
}
184160

185-
res, err := deployer.Deploy(ctx, f)
161+
client := fn.New(
162+
fn.WithDeployer(
163+
knative.NewDeployer(
164+
knative.WithDeployerDecorator(deployDecorator{}),
165+
knative.WithDeployerVerbose(true)),
166+
),
167+
)
168+
res, err := client.Deploy(ctx, f, fn.WithDeploySkipBuildCheck(true))
186169
if err != nil {
187170
return fmt.Errorf("cannot deploy the function: %w", err)
188171
}

cmd/run.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,19 @@ Or if you have an existing function:
227227

228228
// actual build step
229229
if !digested {
230-
if f, _, err = build(cmd, cfg.Build, f, client, buildOptions); err != nil {
230+
shouldBuild, err := build(cmd, cfg.Build, f)
231+
if err != nil {
231232
return err
232233
}
234+
if shouldBuild {
235+
if err := client.Scaffold(cmd.Context(), f, ""); err != nil {
236+
return err
237+
}
238+
f, err = client.Build(cmd.Context(), f, buildOptions...)
239+
if err != nil {
240+
return err
241+
}
242+
}
233243
}
234244
} else { // if !container
235245
// don't run digested image without a container

pkg/builders/builders_int_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,15 @@ password=nbusr123
139139

140140
testCases := []struct {
141141
Name string
142+
Scaffolder fn.Scaffolder
142143
Builder fn.Builder
143144
BuilderImage func(ctx context.Context, t *testing.T, certDir string) string
144145
Envs []fn.Env
145146
Mounts []fn.MountSpec
146147
}{
147148
{
148149
Name: "s2i",
150+
Scaffolder: s2i.NewScaffolder(true),
149151
Builder: s2i.NewBuilder(s2i.WithVerbose(true)),
150152
BuilderImage: buildPatchedS2IBuilder,
151153
Mounts: []fn.MountSpec{
@@ -156,6 +158,7 @@ password=nbusr123
156158
},
157159
{
158160
Name: "pack",
161+
Scaffolder: buildpacks.NewScaffolder(true),
159162
Builder: buildpacks.NewBuilder(buildpacks.WithVerbose(true)),
160163
BuilderImage: buildPatchedBuildpackBuilder,
161164
Envs: []fn.Env{
@@ -184,6 +187,10 @@ password=nbusr123
184187
f.Build.Mounts = append(f.Build.Mounts, tt.Mounts...)
185188
f.Build.BuildEnvs = append(f.Build.BuildEnvs, tt.Envs...)
186189

190+
err = tt.Scaffolder.Scaffold(ctx, f, "")
191+
if err != nil {
192+
t.Fatal(err)
193+
}
187194
err = tt.Builder.Build(ctx, f, nil)
188195
if err != nil {
189196
t.Fatal(err)
@@ -282,7 +289,7 @@ USER 1001:0
282289
// Builds a tiny paketo builder that trusts to our self-signed certificate (see createCertificate).
283290
func buildPatchedBuildpackBuilder(ctx context.Context, t *testing.T, certDir string) string {
284291
tag := "localhost:50000/builder-jammy-tin:test"
285-
dockerfile := `FROM ghcr.io/knative/builder-jammy-tiny:latest
292+
dockerfile := `FROM ghcr.io/knative/builder-jammy-tiny:v2
286293
COPY 85c05568.0 /etc/ssl/certs/
287294
`
288295
return buildPatchedBuilder(ctx, t, tag, dockerfile, certDir)

pkg/buildpacks/builder.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ import (
2828
// DefaultName when no WithName option is provided to NewBuilder
2929
const DefaultName = builders.Pack
3030

31-
var DefaultBaseBuilder = "ghcr.io/knative/builder-jammy-base:latest"
32-
var DefaultTinyBuilder = "ghcr.io/knative/builder-jammy-tiny:latest"
31+
var DefaultBaseBuilder = "ghcr.io/knative/builder-jammy-base:v2"
32+
var DefaultTinyBuilder = "ghcr.io/knative/builder-jammy-tiny:v2"
3333

3434
var (
3535
DefaultBuilderImages = map[string]string{
@@ -186,6 +186,11 @@ func (b *Builder) Build(ctx context.Context, f fn.Function, platforms []fn.Platf
186186
opts.Env["BPE_DEFAULT_LISTEN_ADDRESS"] = "[::]:8080"
187187
}
188188

189+
// set scaffolding path to buildpacks builder
190+
if f.Runtime == "go" {
191+
opts.Env["BP_GO_WORKDIR"] = ".func/build"
192+
}
193+
189194
var bindings = make([]string, 0, len(f.Build.Mounts))
190195
for _, m := range f.Build.Mounts {
191196
bindings = append(bindings, fmt.Sprintf("%s:%s", m.Source, m.Destination))

pkg/buildpacks/scaffolder.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package buildpacks
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
9+
fn "knative.dev/func/pkg/functions"
10+
"knative.dev/func/pkg/scaffolding"
11+
)
12+
13+
const defaultPath = ".func/build"
14+
15+
// Scaffolder for buildpacks builder
16+
type Scaffolder struct {
17+
verbose bool
18+
}
19+
20+
func NewScaffolder(verbose bool) *Scaffolder {
21+
return &Scaffolder{verbose: verbose}
22+
}
23+
24+
// Scaffold the function so that it can be built via buildpacks builder.
25+
// 'path' is an optional override. Assign "" (empty string) most of the time
26+
func (s Scaffolder) Scaffold(ctx context.Context, f fn.Function, path string) error {
27+
// Because of Python injector we currently dont scaffold python.
28+
// Add it here once the injector is removed
29+
if f.Runtime != "go" {
30+
if s.verbose {
31+
fmt.Println("Scaffolding skipped. Currently available for runtime go")
32+
}
33+
return nil
34+
}
35+
36+
appRoot := path
37+
if appRoot == "" {
38+
appRoot = filepath.Join(f.Root, defaultPath)
39+
}
40+
if s.verbose {
41+
fmt.Printf("Writing buildpacks scaffolding at path '%v'\n", appRoot)
42+
}
43+
44+
embeddedRepo, err := fn.NewRepository("", "")
45+
if err != nil {
46+
return fmt.Errorf("unable to load embedded scaffolding: %w", err)
47+
}
48+
_ = os.RemoveAll(appRoot)
49+
return scaffolding.Write(appRoot, f.Root, f.Runtime, f.Invoke, embeddedRepo.FS())
50+
}

0 commit comments

Comments
 (0)