Skip to content

Commit 211df16

Browse files
authored
base builder flag (knative#2935)
1 parent 30315ea commit 211df16

File tree

15 files changed

+215
-14
lines changed

15 files changed

+215
-14
lines changed

cmd/build.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ EXAMPLES
6969
`,
7070
SuggestFor: []string{"biuld", "buidl", "built"},
7171
PreRunE: bindEnv("image", "path", "builder", "registry", "confirm",
72-
"push", "builder-image", "platform", "verbose", "build-timestamp",
73-
"registry-insecure", "username", "password", "token"),
72+
"push", "builder-image", "base-image", "platform", "verbose",
73+
"build-timestamp", "registry-insecure", "username", "password", "token"),
7474
RunE: func(cmd *cobra.Command, args []string) error {
7575
return runBuild(cmd, args, newClient)
7676
},
@@ -110,6 +110,8 @@ EXAMPLES
110110
builderImage := f.Build.BuilderImages[f.Build.Builder]
111111
cmd.Flags().StringP("builder-image", "", builderImage,
112112
"Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)")
113+
cmd.Flags().StringP("base-image", "", f.Build.BaseImage,
114+
"Override the base image for your function (host builder only)")
113115
cmd.Flags().StringP("image", "i", f.Image,
114116
"Full image name in the form [registry]/[namespace]/[name]:[tag] (optional). This option takes precedence over --registry ($FUNC_IMAGE)")
115117

@@ -223,6 +225,10 @@ type buildConfig struct {
223225
// image name derivation based on registry and function name)
224226
Image string
225227

228+
// BaseImage is an image to build a function upon (host builder only)
229+
// TODO: gauron99 -- make option to add a path to dockerfile ?
230+
BaseImage string
231+
226232
// Path of the function implementation on local disk. Defaults to current
227233
// working directory of the process.
228234
Path string
@@ -260,6 +266,7 @@ func newBuildConfig() buildConfig {
260266
RegistryInsecure: viper.GetBool("registry-insecure"),
261267
},
262268
BuilderImage: viper.GetString("builder-image"),
269+
BaseImage: viper.GetString("base-image"),
263270
Image: viper.GetString("image"),
264271
Path: viper.GetString("path"),
265272
Platform: viper.GetString("platform"),
@@ -281,6 +288,7 @@ func (c buildConfig) Configure(f fn.Function) fn.Function {
281288
f.Build.BuilderImages[f.Build.Builder] = c.BuilderImage
282289
}
283290
f.Image = c.Image
291+
f.Build.BaseImage = c.BaseImage
284292
// Path, Platform and Push are not part of a function's state.
285293
return f
286294
}
@@ -360,6 +368,10 @@ func (c buildConfig) Validate() (err error) {
360368
return
361369
}
362370

371+
// BaseImage is only supported with the host builder
372+
if c.BaseImage != "" && c.Builder != "host" {
373+
err = errors.New("only host builds support specifying the base image")
374+
}
363375
return
364376
}
365377

cmd/build_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ func TestBuild_Authentication(t *testing.T) {
9595
testAuthentication(NewBuildCmd, t)
9696
}
9797

98+
// TestBuild_BaseImage ensures that base image is used only with the right
99+
// builders and propagates into f.Build.BaseImage
100+
func TestBuild_BaseImage(t *testing.T) {
101+
testBaseImage(NewBuildCmd, t)
102+
}
103+
98104
// TestBuild_Push ensures that the build command properly pushes and respects
99105
// the --push flag.
100106
// - Push triggered after a successful build

cmd/deploy.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ EXAMPLES
128128
129129
`,
130130
SuggestFor: []string{"delpoy", "deplyo"},
131-
PreRunE: bindEnv("build", "build-timestamp", "builder", "builder-image", "confirm", "domain", "env", "git-branch", "git-dir", "git-url", "image", "namespace", "path", "platform", "push", "pvc-size", "service-account", "registry", "registry-insecure", "remote", "username", "password", "token", "verbose", "remote-storage-class"),
131+
PreRunE: bindEnv("build", "build-timestamp", "builder", "builder-image",
132+
"base-image", "confirm", "domain", "env", "git-branch", "git-dir",
133+
"git-url", "image", "namespace", "path", "platform", "push", "pvc-size",
134+
"service-account", "registry", "registry-insecure", "remote",
135+
"username", "password", "token", "verbose", "remote-storage-class"),
132136
RunE: func(cmd *cobra.Command, args []string) error {
133137
return runDeploy(cmd, newClient)
134138
},
@@ -163,6 +167,8 @@ EXAMPLES
163167
builderImage := f.Build.BuilderImages[f.Build.Builder]
164168
cmd.Flags().String("builder-image", builderImage,
165169
"Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)")
170+
cmd.Flags().StringP("base-image", "", f.Build.BaseImage,
171+
"Override the base image for your function (host builder only)")
166172
cmd.Flags().StringP("image", "i", f.Image,
167173
"Full image name in the form [registry]/[namespace]/[name]:[tag]@[digest]. This option takes precedence over --registry. Specifying digest is optional, but if it is given, 'build' and 'push' phases are disabled. ($FUNC_IMAGE)")
168174

cmd/deploy_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,3 +2170,75 @@ func Test_isDigested(t *testing.T) {
21702170
t.Fatal("did not report image reference has digest")
21712171
}
21722172
}
2173+
2174+
func TestDeploy_BaseImage(t *testing.T) {
2175+
testBaseImage(NewDeployCmd, t)
2176+
}
2177+
2178+
func testBaseImage(cmdFn commandConstructor, t *testing.T) {
2179+
const baseImage = "example.com/repo/baseImage"
2180+
tests := []struct {
2181+
name string
2182+
runtime string
2183+
builder string
2184+
expErr bool
2185+
}{
2186+
{
2187+
name: "should-succeed: python-runtime with host-builder",
2188+
runtime: "python",
2189+
builder: "host",
2190+
},
2191+
{
2192+
name: "should-succeed: go-runtime with host-builder",
2193+
runtime: "go",
2194+
builder: "host",
2195+
},
2196+
{
2197+
name: "should-fail: python-runtime with pack-builder",
2198+
runtime: "python",
2199+
builder: "pack",
2200+
expErr: true,
2201+
},
2202+
}
2203+
for _, tt := range tests {
2204+
t.Run(tt.name, func(t *testing.T) {
2205+
root := FromTempDirectory(t)
2206+
2207+
// func init
2208+
f := fn.Function{Runtime: tt.runtime, Root: root}
2209+
_, err := fn.New().Init(f)
2210+
if err != nil {
2211+
t.Fatal(err)
2212+
}
2213+
2214+
//create cmd
2215+
cmd := cmdFn(NewTestClient(
2216+
fn.WithBuilder(mock.NewBuilder()),
2217+
fn.WithDeployer(mock.NewDeployer()),
2218+
fn.WithRegistry(TestRegistry),
2219+
))
2220+
2221+
// create flags for cmd
2222+
args := []string{
2223+
fmt.Sprintf("--builder=%s", tt.builder),
2224+
fmt.Sprintf("--base-image=%s", baseImage),
2225+
}
2226+
2227+
cmd.SetArgs(args)
2228+
err = cmd.Execute()
2229+
2230+
// ASSERT
2231+
2232+
// got error but expected success
2233+
if err != nil && !tt.expErr {
2234+
err = fmt.Errorf("Expected the test to succeed but instead got: %w", err)
2235+
t.Fatal(err)
2236+
}
2237+
2238+
// succeeded but expected fail
2239+
if err == nil && tt.expErr {
2240+
t.Fatal(fmt.Errorf("Expected error but test succeeded"))
2241+
}
2242+
})
2243+
}
2244+
}

cmd/run.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ EXAMPLES
7777
$ {{rootCmdUse}} run --json
7878
`,
7979
SuggestFor: []string{"rnu"},
80-
PreRunE: bindEnv("build", "builder", "builder-image", "confirm", "container", "env", "image", "path", "registry", "start-timeout", "verbose", "address", "json"),
80+
PreRunE: bindEnv("build", "builder", "builder-image", "base-image",
81+
"confirm", "container", "env", "image", "path", "registry",
82+
"start-timeout", "verbose", "address", "json"),
8183
RunE: func(cmd *cobra.Command, _ []string) error {
8284
return runRun(cmd, newClient)
8385
},
@@ -109,6 +111,8 @@ EXAMPLES
109111
builderImage := f.Build.BuilderImages[f.Build.Builder]
110112
cmd.Flags().String("builder-image", builderImage,
111113
"Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)")
114+
cmd.Flags().StringP("base-image", "", f.Build.BaseImage,
115+
"Override the base image for your function (host builder only)")
112116
cmd.Flags().StringP("image", "i", f.Image,
113117
"Full image name in the form [registry]/[namespace]/[name]:[tag]. This option takes precedence over --registry. Specifying tag is optional. ($FUNC_IMAGE)")
114118
cmd.Flags().StringArrayP("env", "e", []string{},

cmd/run_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,3 +515,89 @@ func TestRun_Address(t *testing.T) {
515515
t.Fatal(err)
516516
}
517517
}
518+
519+
// TestRun_BaseImage ensures that running func run --base-image with various
520+
// other
521+
func TestRun_BaseImage(t *testing.T) {
522+
const baseImage = "example.com/repo/baseImage"
523+
tests := []struct {
524+
name string
525+
runtime string
526+
builder string
527+
expectError bool
528+
}{
529+
{
530+
name: "should-succeed: python-runtime with host-builder",
531+
runtime: "python",
532+
builder: "host",
533+
},
534+
{
535+
name: "should-succeed: go-runtime with host-builder",
536+
runtime: "go",
537+
builder: "host",
538+
},
539+
{
540+
name: "should-fail: python-runtime with pack-builder",
541+
runtime: "python",
542+
builder: "pack",
543+
expectError: true,
544+
},
545+
}
546+
for _, tt := range tests {
547+
t.Run(tt.name, func(t *testing.T) {
548+
root := FromTempDirectory(t)
549+
runner := mock.NewRunner()
550+
551+
runner.RunFn = func(_ context.Context, f fn.Function, _ string, _ time.Duration) (*fn.Job, error) {
552+
errs := make(chan error, 1)
553+
stop := func() error { return nil }
554+
return fn.NewJob(f, "127.0.0.1", "8080", errs, stop, false)
555+
}
556+
557+
builder := mock.NewBuilder()
558+
//if tt.expectError {
559+
// builder.BuildFn = func(f fn.Function) error { return fmt.Errorf("expected error") }
560+
//}
561+
562+
cmd := NewRunCmd(NewTestClient(
563+
fn.WithRunner(runner),
564+
fn.WithBuilder(builder),
565+
fn.WithRegistry(TestRegistry),
566+
))
567+
args := []string{"--build=true", fmt.Sprintf("--builder=%s", tt.builder), fmt.Sprintf("--base-image=%s", baseImage)}
568+
cmd.SetArgs(args)
569+
570+
// set test case's function instance
571+
_, err := fn.New().Init(fn.Function{Root: root, Runtime: tt.runtime})
572+
if err != nil {
573+
t.Fatal(err)
574+
}
575+
ctx, cancel := context.WithCancel(context.Background())
576+
runErrCh := make(chan error, 1)
577+
go func() {
578+
t0 := tt // capture tt into closure
579+
_, err := cmd.ExecuteContextC(ctx)
580+
if err != nil && t0.expectError {
581+
// This is an expected error, so simply continue execution ignoring
582+
// the error (send nil on the channel to release the parent routine
583+
runErrCh <- nil
584+
return
585+
} else if err != nil {
586+
runErrCh <- err // error not expected
587+
return
588+
}
589+
590+
// No errors, but an error was expected:
591+
if t0.expectError {
592+
runErrCh <- fmt.Errorf("Expected error but got '%v'\n", err)
593+
}
594+
close(runErrCh) // release the waiting parent process
595+
}()
596+
cancel() // trigger the return of cmd.ExecuteContextC in the routine
597+
<-ctx.Done()
598+
if err := <-runErrCh; err != nil { // wait for completion of assertions
599+
t.Fatal(err)
600+
}
601+
})
602+
}
603+
}

docs/reference/func_build.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func build
5757
### Options
5858

5959
```
60+
--base-image string Override the base image for your function (host builder only)
6061
--build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder.
6162
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". ($FUNC_BUILDER) (default "pack")
6263
--builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)

docs/reference/func_deploy.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ func deploy
113113
### Options
114114

115115
```
116+
--base-image string Override the base image for your function (host builder only)
116117
--build string[="true"] Build the function. [auto|true|false]. ($FUNC_BUILD) (default "auto")
117118
--build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder.
118119
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". (default "pack")

docs/reference/func_run.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ func run
6767

6868
```
6969
--address string Interface and port on which to bind and listen. Default is 127.0.0.1:8080, or an available port if 8080 is not available. ($FUNC_ADDRESS)
70+
--base-image string Override the base image for your function (host builder only)
7071
--build string[="true"] Build the function. [auto|true|false]. ($FUNC_BUILD) (default "auto")
7172
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". (default "pack")
7273
--builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)

pkg/functions/function.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ type BuildSpec struct {
152152
// in .func/built-image
153153
Image string `yaml:"-"`
154154

155+
// BaseImage defines an override for the function to be built upon (host bulder only)
156+
BaseImage string `yaml:"baseImage,omitempty"`
157+
155158
// Mounts used in build phase. This is useful in particular for paketo bindings.
156159
Mounts []MountSpec `yaml:"volumes,omitempty"`
157160
}

0 commit comments

Comments
 (0)