Skip to content

Commit 065006c

Browse files
authored
fix: force container=true for pack/s2i builders when not explicitly set (knative#2966)
* fix: force container=true for pack/s2i builders when not explicitly set - Added logic to automatically set container=true for containerized builders (pack/s2i) when --container flag is not explicitly provided by user - Fixes issue where 'func run --builder=pack' would incorrectly run on host instead of container - Solution checks if builder is pack/s2i and --container flag wasn't changed by user - Resolves knative#2955 * fix: enforce container mode for pack/s2i builders - Auto-set container=true for pack/s2i when --container not explicitly set - Validate and error on incompatible --builder=pack --container=false combinations - Handle both flags and FUNC_CONTAINER environment variable Fixes knative#2955 Addresses @gauron99 feedback on explicit container=false validation * added logic to prevent test failure in testfunctionwithoutcontainer and also fixed some formatting issues to prevent test failure * refactored the way of implementation based on the suggestion provided also added the test as requested to do so * fixed the linting errors * fimplemented smart builder/container auto-selection automatically handles pack->container=true and container=false->host builder. also updated the e2e test and added new smartbuilderselection test in run_test.go * fix linting errors
1 parent aeb51b3 commit 065006c

File tree

2 files changed

+128
-3
lines changed

2 files changed

+128
-3
lines changed

cmd/run.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,16 +171,31 @@ func runRun(cmd *cobra.Command, newClient ClientFactory) (err error) {
171171
if f, err = fn.NewFunction(cfg.Path); err != nil {
172172
return
173173
}
174-
if err = cfg.Validate(cmd, f); err != nil {
175-
return
176-
}
177174
if !f.Initialized() {
178175
return fn.NewErrNotInitialized(f.Root)
179176
}
180177
if f, err = cfg.Configure(f); err != nil { // Updates f with deploy cfg
181178
return
182179
}
183180

181+
// Smart auto-fix logic for builder/container compatibility
182+
// This fixes the original bug where --builder=pack doesn't default to container=true
183+
184+
// Case 1: Containerized builders (pack/s2i) should force container=true when not explicitly set
185+
if (f.Build.Builder == "pack" || f.Build.Builder == "s2i") && !cfg.Container && !cmd.Flags().Changed("container") {
186+
cfg.Container = true
187+
}
188+
189+
// Case 2: container=false should auto-select host builder when no builder explicitly set
190+
if !cfg.Container && cmd.Flags().Changed("container") && !cmd.Flags().Changed("builder") {
191+
f.Build.Builder = "host"
192+
}
193+
194+
// Validate after configure and auto-fix
195+
if err = cfg.Validate(cmd, f); err != nil {
196+
return
197+
}
198+
184199
// Ignore the verbose flag if JSON output
185200
if cfg.JSON {
186201
cfg.Verbose = false
@@ -401,6 +416,11 @@ func (c runConfig) Validate(cmd *cobra.Command, f fn.Function) (err error) {
401416
return fmt.Errorf("the %q runtime currently requires being run in a container", f.Runtime)
402417
}
403418

419+
// Validate that containerized builders (pack/s2i) cannot be used with container=false
420+
if (f.Build.Builder == "pack" || f.Build.Builder == "s2i") && !c.Container {
421+
return fmt.Errorf("builder %q requires container mode but --container=false was set", f.Build.Builder)
422+
}
423+
404424
// When the docker runner respects the StartTimeout, this validation check
405425
// can be removed
406426
if c.StartTimeout != 0 && c.Container {

cmd/run_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"context"
55
"fmt"
6+
"path/filepath"
67
"testing"
78
"time"
89

@@ -601,3 +602,107 @@ func TestRun_BaseImage(t *testing.T) {
601602
})
602603
}
603604
}
605+
606+
// TestCtrBuilder tests that pack builder auto-forces container mode
607+
func TestCtrBuilder(t *testing.T) {
608+
var runner = mock.NewRunner()
609+
var builder = mock.NewBuilder()
610+
tmp := t.TempDir()
611+
612+
fnPath := filepath.Join(tmp, "fn")
613+
f, err := fn.New().Init(fn.Function{Root: fnPath, Runtime: "python"})
614+
if err != nil {
615+
t.Fatal(err)
616+
}
617+
err = f.Write()
618+
if err != nil {
619+
t.Fatal(err)
620+
}
621+
t.Chdir(fnPath)
622+
623+
cmd := NewRunCmd(NewTestClient(
624+
fn.WithRunner(runner),
625+
fn.WithBuilder(builder),
626+
fn.WithRegistry(TestRegistry),
627+
))
628+
cmd.SetArgs([]string{"--builder=pack", "--build=1"})
629+
ctx, cancel := context.WithCancel(context.Background())
630+
time.AfterFunc(time.Second, func() {
631+
cancel()
632+
})
633+
err = cmd.ExecuteContext(ctx)
634+
if err != nil {
635+
t.Fatal(err)
636+
}
637+
// verify that builder was called since `--builder=pack` was provided
638+
if !builder.BuildInvoked {
639+
t.Error("builder has not been invoked")
640+
}
641+
}
642+
643+
// TestCtrBuilderConflict tests that pack builder with explicit container=false shows validation error
644+
func TestCtrBuilderConflict(t *testing.T) {
645+
var runner = mock.NewRunner()
646+
var builder = mock.NewBuilder()
647+
tmp := t.TempDir()
648+
649+
fnPath := filepath.Join(tmp, "fn")
650+
f, err := fn.New().Init(fn.Function{Root: fnPath, Runtime: "python"})
651+
if err != nil {
652+
t.Fatal(err)
653+
}
654+
err = f.Write()
655+
if err != nil {
656+
t.Fatal(err)
657+
}
658+
t.Chdir(fnPath)
659+
660+
cmd := NewRunCmd(NewTestClient(
661+
fn.WithRunner(runner),
662+
fn.WithBuilder(builder),
663+
fn.WithRegistry(TestRegistry),
664+
))
665+
cmd.SetArgs([]string{"--builder=pack", "--build=1", "--container=0"})
666+
ctx, cancel := context.WithCancel(context.Background())
667+
time.AfterFunc(time.Second, func() {
668+
cancel()
669+
})
670+
err = cmd.ExecuteContext(ctx)
671+
// since explicit flag `--builder=pack` and `--container=0` are contradiction we want error
672+
if err == nil {
673+
t.Error("error expected but got <nil>")
674+
}
675+
}
676+
677+
// TestSmartBuilderSelection tests that container=false auto-selects host builder
678+
func TestSmartBuilderSelection(t *testing.T) {
679+
var runner = mock.NewRunner()
680+
var builder = mock.NewBuilder()
681+
tmp := t.TempDir()
682+
683+
fnPath := filepath.Join(tmp, "fn")
684+
f, err := fn.New().Init(fn.Function{Root: fnPath, Runtime: "go"})
685+
if err != nil {
686+
t.Fatal(err)
687+
}
688+
err = f.Write()
689+
if err != nil {
690+
t.Fatal(err)
691+
}
692+
t.Chdir(fnPath)
693+
694+
cmd := NewRunCmd(NewTestClient(
695+
fn.WithRunner(runner),
696+
fn.WithBuilder(builder),
697+
fn.WithRegistry(TestRegistry),
698+
))
699+
cmd.SetArgs([]string{"--container=false", "--build=1"})
700+
ctx, cancel := context.WithCancel(context.Background())
701+
time.AfterFunc(time.Second, func() {
702+
cancel()
703+
})
704+
err = cmd.ExecuteContext(ctx)
705+
if err != nil {
706+
t.Fatal(err)
707+
}
708+
}

0 commit comments

Comments
 (0)