mirror of https://github.com/knative/func.git
base builder flag (#2935)
This commit is contained in:
parent
30315ea15e
commit
211df1657f
16
cmd/build.go
16
cmd/build.go
|
|
@ -69,8 +69,8 @@ EXAMPLES
|
|||
`,
|
||||
SuggestFor: []string{"biuld", "buidl", "built"},
|
||||
PreRunE: bindEnv("image", "path", "builder", "registry", "confirm",
|
||||
"push", "builder-image", "platform", "verbose", "build-timestamp",
|
||||
"registry-insecure", "username", "password", "token"),
|
||||
"push", "builder-image", "base-image", "platform", "verbose",
|
||||
"build-timestamp", "registry-insecure", "username", "password", "token"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runBuild(cmd, args, newClient)
|
||||
},
|
||||
|
|
@ -110,6 +110,8 @@ EXAMPLES
|
|||
builderImage := f.Build.BuilderImages[f.Build.Builder]
|
||||
cmd.Flags().StringP("builder-image", "", builderImage,
|
||||
"Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)")
|
||||
cmd.Flags().StringP("base-image", "", f.Build.BaseImage,
|
||||
"Override the base image for your function (host builder only)")
|
||||
cmd.Flags().StringP("image", "i", f.Image,
|
||||
"Full image name in the form [registry]/[namespace]/[name]:[tag] (optional). This option takes precedence over --registry ($FUNC_IMAGE)")
|
||||
|
||||
|
|
@ -223,6 +225,10 @@ type buildConfig struct {
|
|||
// image name derivation based on registry and function name)
|
||||
Image string
|
||||
|
||||
// BaseImage is an image to build a function upon (host builder only)
|
||||
// TODO: gauron99 -- make option to add a path to dockerfile ?
|
||||
BaseImage string
|
||||
|
||||
// Path of the function implementation on local disk. Defaults to current
|
||||
// working directory of the process.
|
||||
Path string
|
||||
|
|
@ -260,6 +266,7 @@ func newBuildConfig() buildConfig {
|
|||
RegistryInsecure: viper.GetBool("registry-insecure"),
|
||||
},
|
||||
BuilderImage: viper.GetString("builder-image"),
|
||||
BaseImage: viper.GetString("base-image"),
|
||||
Image: viper.GetString("image"),
|
||||
Path: viper.GetString("path"),
|
||||
Platform: viper.GetString("platform"),
|
||||
|
|
@ -281,6 +288,7 @@ func (c buildConfig) Configure(f fn.Function) fn.Function {
|
|||
f.Build.BuilderImages[f.Build.Builder] = c.BuilderImage
|
||||
}
|
||||
f.Image = c.Image
|
||||
f.Build.BaseImage = c.BaseImage
|
||||
// Path, Platform and Push are not part of a function's state.
|
||||
return f
|
||||
}
|
||||
|
|
@ -360,6 +368,10 @@ func (c buildConfig) Validate() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// BaseImage is only supported with the host builder
|
||||
if c.BaseImage != "" && c.Builder != "host" {
|
||||
err = errors.New("only host builds support specifying the base image")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,12 @@ func TestBuild_Authentication(t *testing.T) {
|
|||
testAuthentication(NewBuildCmd, t)
|
||||
}
|
||||
|
||||
// TestBuild_BaseImage ensures that base image is used only with the right
|
||||
// builders and propagates into f.Build.BaseImage
|
||||
func TestBuild_BaseImage(t *testing.T) {
|
||||
testBaseImage(NewBuildCmd, t)
|
||||
}
|
||||
|
||||
// TestBuild_Push ensures that the build command properly pushes and respects
|
||||
// the --push flag.
|
||||
// - Push triggered after a successful build
|
||||
|
|
|
|||
|
|
@ -128,7 +128,11 @@ EXAMPLES
|
|||
|
||||
`,
|
||||
SuggestFor: []string{"delpoy", "deplyo"},
|
||||
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"),
|
||||
PreRunE: bindEnv("build", "build-timestamp", "builder", "builder-image",
|
||||
"base-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"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDeploy(cmd, newClient)
|
||||
},
|
||||
|
|
@ -163,6 +167,8 @@ EXAMPLES
|
|||
builderImage := f.Build.BuilderImages[f.Build.Builder]
|
||||
cmd.Flags().String("builder-image", builderImage,
|
||||
"Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)")
|
||||
cmd.Flags().StringP("base-image", "", f.Build.BaseImage,
|
||||
"Override the base image for your function (host builder only)")
|
||||
cmd.Flags().StringP("image", "i", f.Image,
|
||||
"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)")
|
||||
|
||||
|
|
|
|||
|
|
@ -2170,3 +2170,75 @@ func Test_isDigested(t *testing.T) {
|
|||
t.Fatal("did not report image reference has digest")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeploy_BaseImage(t *testing.T) {
|
||||
testBaseImage(NewDeployCmd, t)
|
||||
}
|
||||
|
||||
func testBaseImage(cmdFn commandConstructor, t *testing.T) {
|
||||
const baseImage = "example.com/repo/baseImage"
|
||||
tests := []struct {
|
||||
name string
|
||||
runtime string
|
||||
builder string
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
name: "should-succeed: python-runtime with host-builder",
|
||||
runtime: "python",
|
||||
builder: "host",
|
||||
},
|
||||
{
|
||||
name: "should-succeed: go-runtime with host-builder",
|
||||
runtime: "go",
|
||||
builder: "host",
|
||||
},
|
||||
{
|
||||
name: "should-fail: python-runtime with pack-builder",
|
||||
runtime: "python",
|
||||
builder: "pack",
|
||||
expErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
root := FromTempDirectory(t)
|
||||
|
||||
// func init
|
||||
f := fn.Function{Runtime: tt.runtime, Root: root}
|
||||
_, err := fn.New().Init(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
//create cmd
|
||||
cmd := cmdFn(NewTestClient(
|
||||
fn.WithBuilder(mock.NewBuilder()),
|
||||
fn.WithDeployer(mock.NewDeployer()),
|
||||
fn.WithRegistry(TestRegistry),
|
||||
))
|
||||
|
||||
// create flags for cmd
|
||||
args := []string{
|
||||
fmt.Sprintf("--builder=%s", tt.builder),
|
||||
fmt.Sprintf("--base-image=%s", baseImage),
|
||||
}
|
||||
|
||||
cmd.SetArgs(args)
|
||||
err = cmd.Execute()
|
||||
|
||||
// ASSERT
|
||||
|
||||
// got error but expected success
|
||||
if err != nil && !tt.expErr {
|
||||
err = fmt.Errorf("Expected the test to succeed but instead got: %w", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// succeeded but expected fail
|
||||
if err == nil && tt.expErr {
|
||||
t.Fatal(fmt.Errorf("Expected error but test succeeded"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,9 @@ EXAMPLES
|
|||
$ {{rootCmdUse}} run --json
|
||||
`,
|
||||
SuggestFor: []string{"rnu"},
|
||||
PreRunE: bindEnv("build", "builder", "builder-image", "confirm", "container", "env", "image", "path", "registry", "start-timeout", "verbose", "address", "json"),
|
||||
PreRunE: bindEnv("build", "builder", "builder-image", "base-image",
|
||||
"confirm", "container", "env", "image", "path", "registry",
|
||||
"start-timeout", "verbose", "address", "json"),
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runRun(cmd, newClient)
|
||||
},
|
||||
|
|
@ -109,6 +111,8 @@ EXAMPLES
|
|||
builderImage := f.Build.BuilderImages[f.Build.Builder]
|
||||
cmd.Flags().String("builder-image", builderImage,
|
||||
"Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)")
|
||||
cmd.Flags().StringP("base-image", "", f.Build.BaseImage,
|
||||
"Override the base image for your function (host builder only)")
|
||||
cmd.Flags().StringP("image", "i", f.Image,
|
||||
"Full image name in the form [registry]/[namespace]/[name]:[tag]. This option takes precedence over --registry. Specifying tag is optional. ($FUNC_IMAGE)")
|
||||
cmd.Flags().StringArrayP("env", "e", []string{},
|
||||
|
|
|
|||
|
|
@ -515,3 +515,89 @@ func TestRun_Address(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRun_BaseImage ensures that running func run --base-image with various
|
||||
// other
|
||||
func TestRun_BaseImage(t *testing.T) {
|
||||
const baseImage = "example.com/repo/baseImage"
|
||||
tests := []struct {
|
||||
name string
|
||||
runtime string
|
||||
builder string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "should-succeed: python-runtime with host-builder",
|
||||
runtime: "python",
|
||||
builder: "host",
|
||||
},
|
||||
{
|
||||
name: "should-succeed: go-runtime with host-builder",
|
||||
runtime: "go",
|
||||
builder: "host",
|
||||
},
|
||||
{
|
||||
name: "should-fail: python-runtime with pack-builder",
|
||||
runtime: "python",
|
||||
builder: "pack",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
root := FromTempDirectory(t)
|
||||
runner := mock.NewRunner()
|
||||
|
||||
runner.RunFn = func(_ context.Context, f fn.Function, _ string, _ time.Duration) (*fn.Job, error) {
|
||||
errs := make(chan error, 1)
|
||||
stop := func() error { return nil }
|
||||
return fn.NewJob(f, "127.0.0.1", "8080", errs, stop, false)
|
||||
}
|
||||
|
||||
builder := mock.NewBuilder()
|
||||
//if tt.expectError {
|
||||
// builder.BuildFn = func(f fn.Function) error { return fmt.Errorf("expected error") }
|
||||
//}
|
||||
|
||||
cmd := NewRunCmd(NewTestClient(
|
||||
fn.WithRunner(runner),
|
||||
fn.WithBuilder(builder),
|
||||
fn.WithRegistry(TestRegistry),
|
||||
))
|
||||
args := []string{"--build=true", fmt.Sprintf("--builder=%s", tt.builder), fmt.Sprintf("--base-image=%s", baseImage)}
|
||||
cmd.SetArgs(args)
|
||||
|
||||
// set test case's function instance
|
||||
_, err := fn.New().Init(fn.Function{Root: root, Runtime: tt.runtime})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
runErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
t0 := tt // capture tt into closure
|
||||
_, err := cmd.ExecuteContextC(ctx)
|
||||
if err != nil && t0.expectError {
|
||||
// This is an expected error, so simply continue execution ignoring
|
||||
// the error (send nil on the channel to release the parent routine
|
||||
runErrCh <- nil
|
||||
return
|
||||
} else if err != nil {
|
||||
runErrCh <- err // error not expected
|
||||
return
|
||||
}
|
||||
|
||||
// No errors, but an error was expected:
|
||||
if t0.expectError {
|
||||
runErrCh <- fmt.Errorf("Expected error but got '%v'\n", err)
|
||||
}
|
||||
close(runErrCh) // release the waiting parent process
|
||||
}()
|
||||
cancel() // trigger the return of cmd.ExecuteContextC in the routine
|
||||
<-ctx.Done()
|
||||
if err := <-runErrCh; err != nil { // wait for completion of assertions
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ func build
|
|||
### Options
|
||||
|
||||
```
|
||||
--base-image string Override the base image for your function (host builder only)
|
||||
--build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder.
|
||||
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". ($FUNC_BUILDER) (default "pack")
|
||||
--builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ func deploy
|
|||
### Options
|
||||
|
||||
```
|
||||
--base-image string Override the base image for your function (host builder only)
|
||||
--build string[="true"] Build the function. [auto|true|false]. ($FUNC_BUILD) (default "auto")
|
||||
--build-timestamp Use the actual time as the created time for the docker image. This is only useful for buildpacks builder.
|
||||
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". (default "pack")
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ func run
|
|||
|
||||
```
|
||||
--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)
|
||||
--base-image string Override the base image for your function (host builder only)
|
||||
--build string[="true"] Build the function. [auto|true|false]. ($FUNC_BUILD) (default "auto")
|
||||
-b, --builder string Builder to use when creating the function's container. Currently supported builders are "host", "pack" and "s2i". (default "pack")
|
||||
--builder-image string Specify a custom builder image for use by the builder other than its default. ($FUNC_BUILDER_IMAGE)
|
||||
|
|
|
|||
|
|
@ -152,6 +152,9 @@ type BuildSpec struct {
|
|||
// in .func/built-image
|
||||
Image string `yaml:"-"`
|
||||
|
||||
// BaseImage defines an override for the function to be built upon (host bulder only)
|
||||
BaseImage string `yaml:"baseImage,omitempty"`
|
||||
|
||||
// Mounts used in build phase. This is useful in particular for paketo bindings.
|
||||
Mounts []MountSpec `yaml:"volumes,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ type languageBuilder interface {
|
|||
// Base returns the base image (if any) to use. Ideally this is a
|
||||
// multi-arch base image with a corresponding platform image for
|
||||
// each requested to be built.
|
||||
Base() string
|
||||
Base(customBase string) string
|
||||
|
||||
// WriteShared layers (not platform-specific) which need to be genearted
|
||||
// on demand per language, such as shared dependencies.
|
||||
|
|
@ -568,12 +568,13 @@ func newCertsTarball(source, target string, verbose bool) error {
|
|||
// Its layers are automatically downloaded into the local cache if this is
|
||||
// the first fetch and their blobs linked into the final OCI image.
|
||||
func pullBase(job buildJob, p v1.Platform) (image v1.Image, err error) {
|
||||
if job.languageBuilder.Base() == "" {
|
||||
baseImage := job.function.Build.BaseImage
|
||||
if job.languageBuilder.Base(baseImage) == "" {
|
||||
return // FROM SCRATCH
|
||||
}
|
||||
|
||||
// Parse the base into a reference
|
||||
ref, err := name.ParseReference(job.languageBuilder.Base())
|
||||
ref, err := name.ParseReference(job.languageBuilder.Base(baseImage))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -461,7 +461,7 @@ func TestBuilder_StaticEnvs(t *testing.T) {
|
|||
// OCI builder for each language, and can be overridden for testing
|
||||
type TestLanguageBuilder struct {
|
||||
BaseInvoked bool
|
||||
BaseFn func() string
|
||||
BaseFn func(customImage string) string
|
||||
|
||||
WriteSharedInvoked bool
|
||||
WriteSharedFn func(buildJob) ([]imageLayer, error)
|
||||
|
|
@ -475,7 +475,7 @@ type TestLanguageBuilder struct {
|
|||
|
||||
func NewTestLanguageBuilder() *TestLanguageBuilder {
|
||||
return &TestLanguageBuilder{
|
||||
BaseFn: func() string { return "" },
|
||||
BaseFn: func(customImage string) string { return "" },
|
||||
WriteSharedFn: func(buildJob) ([]imageLayer, error) { return []imageLayer{}, nil },
|
||||
WritePlatformFn: func(buildJob, v1.Platform) ([]imageLayer, error) { return []imageLayer{}, nil },
|
||||
ConfigureFn: func(buildJob, v1.Platform, v1.ConfigFile) (v1.ConfigFile, error) {
|
||||
|
|
@ -484,9 +484,9 @@ func NewTestLanguageBuilder() *TestLanguageBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
func (l *TestLanguageBuilder) Base() string {
|
||||
func (l *TestLanguageBuilder) Base(customImage string) string {
|
||||
l.BaseInvoked = true
|
||||
return l.BaseFn()
|
||||
return l.BaseFn(customImage)
|
||||
}
|
||||
|
||||
func (l *TestLanguageBuilder) WriteShared(job buildJob) ([]imageLayer, error) {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ import (
|
|||
|
||||
type goBuilder struct{}
|
||||
|
||||
func (b goBuilder) Base() string {
|
||||
return "" // scratch
|
||||
func (b goBuilder) Base(customImage string) string {
|
||||
// if not defined -> return "", meaning building from scratch
|
||||
return customImage
|
||||
}
|
||||
|
||||
func (b goBuilder) Configure(_ buildJob, _ v1.Platform, cf v1.ConfigFile) (v1.ConfigFile, error) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ var defaultPythonBase = "python:3.13-slim" // Moving from docker.io. See issue
|
|||
|
||||
type pythonBuilder struct{}
|
||||
|
||||
func (b pythonBuilder) Base() string {
|
||||
func (b pythonBuilder) Base(customBase string) string {
|
||||
if customBase != "" {
|
||||
return customBase
|
||||
}
|
||||
return defaultPythonBase
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@
|
|||
"type": "string",
|
||||
"description": "RemoteStorageClass specifies the storage class to use for the volume used\non-cluster during when built remotely."
|
||||
},
|
||||
"baseImage": {
|
||||
"type": "string",
|
||||
"description": "BaseImage defines an override for the function to be built upon (host bulder only)"
|
||||
},
|
||||
"volumes": {
|
||||
"items": {
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
|
|
|
|||
Loading…
Reference in New Issue