Skip to content

Commit 47bbdb3

Browse files
make eval to use kpt container & exec runtime (#1898)
* change eval to use kpt container runtime * make eval to use kpt exec runtime
1 parent 479c762 commit 47bbdb3

File tree

6 files changed

+144
-299
lines changed

6 files changed

+144
-299
lines changed

e2e/testdata/fn-eval/missing-fn-config/.expected/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
testType: eval
1616
exitCode: 1
1717
image: gcr.io/kpt-fn/set-namespace:v0.1
18-
stdErr: "failed to configure function: input namespace cannot be emptyerror: exit status 1"
18+
stdErr: "failed to configure function: input namespace cannot be empty"

e2e/testdata/fn-eval/missing-fn-image/.expected/config.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,4 @@ exitCode: 1
1717
image: gcr.io/kpt-fn/dne # non-existing image
1818
args:
1919
namespace: staging
20-
stdErr: "Failed to fetch \"latest\" from request \"/v2/kpt-fn/dne/manifests/latest\""
21-
20+
stdErr: 'failed to check function image existence: function image "gcr.io/kpt-fn/dne" doesn''t exist'

internal/fnruntime/container.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/GoogleContainerTools/kpt/internal/errors"
2828
"github.com/GoogleContainerTools/kpt/internal/printer"
2929
"github.com/GoogleContainerTools/kpt/internal/types"
30+
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
3031
)
3132

3233
// containerNetworkName is a type for network name used in container
@@ -43,6 +44,7 @@ const (
4344
// function such as network access.
4445
type ContainerFnPermission struct {
4546
AllowNetwork bool
47+
AllowMount bool
4648
}
4749

4850
// ContainerFnWrapper wraps the real function filter, prints
@@ -75,6 +77,15 @@ type ContainerFn struct {
7577
// The default value is 5 minutes.
7678
Timeout time.Duration
7779
Perm ContainerFnPermission
80+
// UIDGID is the os User ID and Group ID that will be
81+
// used to run the container in format userId:groupId.
82+
// If it's empty, "nobody" will be used.
83+
UIDGID string
84+
// StorageMounts are the storage or directories to mount
85+
// into the container
86+
StorageMounts []runtimeutil.StorageMount
87+
// Env is a slice of env string that will be exposed to container
88+
Env []string
7889
}
7990

8091
// Run runs the container function using docker runtime.
@@ -116,6 +127,10 @@ func (f *ContainerFn) getDockerCmd() (*exec.Cmd, context.CancelFunc) {
116127
if f.Perm.AllowNetwork {
117128
network = networkNameHost
118129
}
130+
uidgid := "nobody"
131+
if f.UIDGID != "" {
132+
uidgid = f.UIDGID
133+
}
119134

120135
args := []string{
121136
"run", "--rm", "-i",
@@ -125,8 +140,14 @@ func (f *ContainerFn) getDockerCmd() (*exec.Cmd, context.CancelFunc) {
125140
// to stderr. We don't need this once we support structured
126141
// results.
127142
"-e", "LOG_TO_STDERR=true",
143+
"--user", uidgid,
128144
"--security-opt=no-new-privileges",
129145
}
146+
for _, storageMount := range f.StorageMounts {
147+
args = append(args, "--mount", storageMount.String())
148+
}
149+
args = append(args,
150+
runtimeutil.NewContainerEnvFromStringSlice(f.Env).GetDockerFlags()...)
130151
args = append(args, f.Image)
131152
// setup container run timeout
132153
timeout := defaultTimeout

internal/fnruntime/exec.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fnruntime
16+
17+
import (
18+
"bytes"
19+
"context"
20+
goerrors "errors"
21+
"fmt"
22+
"io"
23+
"os/exec"
24+
"time"
25+
26+
"github.com/GoogleContainerTools/kpt/internal/errors"
27+
"github.com/GoogleContainerTools/kpt/internal/printer"
28+
)
29+
30+
type ExecFn struct {
31+
// Path is the os specific path to the executable
32+
// file. It can be relative or absolute.
33+
Path string
34+
// Args are the arguments to the executable
35+
Args []string
36+
// Container function will be killed after this timeour.
37+
// The default value is 5 minutes.
38+
Timeout time.Duration
39+
}
40+
41+
// Run runs the executable file which reads the input from r and
42+
// writes the output to w.
43+
func (f *ExecFn) Run(r io.Reader, w io.Writer) error {
44+
// setup exec run timeout
45+
timeout := defaultTimeout
46+
if f.Timeout != 0 {
47+
timeout = f.Timeout
48+
}
49+
ctx, cancel := context.WithTimeout(context.Background(), timeout)
50+
defer cancel()
51+
52+
cmd := exec.CommandContext(ctx, f.Path, f.Args...)
53+
54+
errSink := bytes.Buffer{}
55+
cmd.Stdin = r
56+
cmd.Stdout = w
57+
cmd.Stderr = &errSink
58+
59+
if err := cmd.Run(); err != nil {
60+
var exitErr *exec.ExitError
61+
if goerrors.As(err, &exitErr) {
62+
return &errors.FnExecError{
63+
OriginalErr: exitErr,
64+
ExitCode: exitErr.ExitCode(),
65+
Stderr: errSink.String(),
66+
TruncateOutput: printer.TruncateOutput,
67+
}
68+
}
69+
return fmt.Errorf("unexpected function error: %w", err)
70+
}
71+
72+
return nil
73+
}

thirdparty/kyaml/runfn/runfn.go

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@ import (
1616
"sync/atomic"
1717

1818
"sigs.k8s.io/kustomize/kyaml/errors"
19-
"sigs.k8s.io/kustomize/kyaml/fn/runtime/container"
20-
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
2119
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
22-
"sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark"
2320
"sigs.k8s.io/kustomize/kyaml/kio"
2421
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
2522
"sigs.k8s.io/kustomize/kyaml/yaml"
2623

24+
"github.com/GoogleContainerTools/kpt/internal/fnruntime"
2725
"github.com/GoogleContainerTools/kpt/internal/pkg"
2826
"github.com/GoogleContainerTools/kpt/internal/types"
2927
"github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1alpha2"
@@ -218,26 +216,7 @@ func (r RunFns) runFunctions(
218216
Outputs: outputs,
219217
ContinueOnEmptyResult: r.ContinueOnEmptyResult,
220218
}
221-
if r.LogSteps {
222-
err = pipeline.ExecuteWithCallback(func(op kio.Filter) {
223-
var identifier string
224-
225-
switch filter := op.(type) {
226-
case *container.Filter:
227-
identifier = filter.Image
228-
case *exec.Filter:
229-
identifier = filter.Path
230-
case *starlark.Filter:
231-
identifier = filter.String()
232-
default:
233-
identifier = "unknown-type function"
234-
}
235-
236-
_, _ = fmt.Fprintf(r.LogWriter, "Running %s\n", identifier)
237-
})
238-
} else {
239-
err = pipeline.Execute()
240-
}
219+
err = pipeline.Execute()
241220
if err != nil {
242221
return err
243222
}
@@ -446,28 +425,38 @@ func (r *RunFns) defaultFnFilterProvider(spec runtimeutil.FunctionSpec, fnConfig
446425
if err != nil {
447426
return nil, err
448427
}
449-
c := container.NewContainer(
450-
runtimeutil.ContainerSpec{
451-
Image: spec.Container.Image,
452-
Network: spec.Container.Network,
453-
StorageMounts: r.StorageMounts,
454-
Env: spec.Container.Env,
428+
c := &fnruntime.ContainerFn{
429+
Path: r.uniquePath,
430+
Image: spec.Container.Image,
431+
UIDGID: uidgid,
432+
StorageMounts: r.StorageMounts,
433+
Env: spec.Container.Env,
434+
Perm: fnruntime.ContainerFnPermission{
435+
AllowNetwork: spec.Container.Network,
436+
// mounts are always from CLI flags so we allow
437+
// them by default for eval
438+
AllowMount: true,
455439
},
456-
uidgid,
457-
)
458-
cf := &c
459-
cf.Exec.FunctionConfig = fnConfig
460-
cf.Exec.ResultsFile = resultsFile
461-
cf.Exec.DeferFailure = spec.DeferFailure
440+
}
441+
cf := &runtimeutil.FunctionFilter{
442+
Run: c.Run,
443+
FunctionConfig: fnConfig,
444+
DeferFailure: spec.DeferFailure,
445+
ResultsFile: resultsFile,
446+
}
462447
return cf, nil
463448
}
464449

465450
if spec.Exec.Path != "" {
466-
ef := &exec.Filter{Path: spec.Exec.Path}
467-
468-
ef.FunctionConfig = fnConfig
469-
ef.ResultsFile = resultsFile
470-
ef.DeferFailure = spec.DeferFailure
451+
e := &fnruntime.ExecFn{
452+
Path: spec.Exec.Path,
453+
}
454+
ef := &runtimeutil.FunctionFilter{
455+
Run: e.Run,
456+
FunctionConfig: fnConfig,
457+
DeferFailure: spec.DeferFailure,
458+
ResultsFile: resultsFile,
459+
}
471460
return ef, nil
472461
}
473462

0 commit comments

Comments
 (0)