Skip to content

Commit 008e196

Browse files
authored
Merge pull request #172 from crossplane/backport-163-to-release-2.3
[Backport release-2.3] render: Have the context function listen on a TCP port
2 parents 97c0c84 + b15eb56 commit 008e196

6 files changed

Lines changed: 24 additions & 117 deletions

File tree

cmd/crossplane/render/contextfn/listener.go

Lines changed: 13 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ package contextfn
1919
import (
2020
"context"
2121
"encoding/json"
22+
"fmt"
2223
"net"
23-
"os"
24-
"path/filepath"
2524
"sync"
2625
"time"
2726

@@ -37,16 +36,10 @@ import (
3736

3837
// Handle is the owner of a running in-process context function.
3938
type Handle struct {
40-
// Target is the gRPC target that dials the function. Set this as the
41-
// FunctionInput address passed to the render engine.
42-
Target string
43-
39+
target string
4440
srv *grpc.Server
4541
fn *server
46-
socketPath string
47-
dir string
4842
stop sync.Once
49-
log logging.Logger
5043
seedInput *runtime.RawExtension
5144
captureInput *runtime.RawExtension
5245
}
@@ -58,9 +51,8 @@ func (h *Handle) Captured() *structpb.Struct {
5851
}
5952

6053
// Start starts an in-process gRPC server that implements the composition
61-
// function RunFunction RPC for context seeding and capture. The server
62-
// listens on a unix-domain socket inside a fresh temp directory. Callers
63-
// must call Handle.Stop when done.
54+
// function RunFunction RPC for context seeding and capture. Callers must call
55+
// Handle.Stop when done.
6456
func Start(ctx context.Context, log logging.Logger, contextData map[string]any) (*Handle, error) {
6557
si, err := json.Marshal(input{Mode: modeSeed})
6658
if err != nil {
@@ -71,51 +63,24 @@ func Start(ctx context.Context, log logging.Logger, contextData map[string]any)
7163
return nil, errors.Wrap(err, "cannot create capture context function input")
7264
}
7365

74-
dir, err := os.MkdirTemp("", "render-ctx-*")
75-
if err != nil {
76-
return nil, errors.Wrap(err, "cannot create temp dir for context function socket")
77-
}
78-
79-
cleanup := func() {
80-
_ = os.RemoveAll(dir)
81-
}
82-
83-
sockPath := filepath.Join(dir, "s")
8466
var lc net.ListenConfig
85-
lis, err := lc.Listen(ctx, "unix", sockPath)
67+
lis, err := lc.Listen(ctx, "tcp", ":0")
8668
if err != nil {
87-
cleanup()
88-
return nil, errors.Wrapf(err, "cannot listen on %q", sockPath)
89-
}
90-
91-
cleanup = func() {
92-
_ = lis.Close()
93-
_ = os.RemoveAll(dir)
94-
}
95-
96-
// In order for processes in Docker containers to connect to the socket, the
97-
// socket must be world-writeable and its containing directory must be
98-
// world-readable.
99-
if err := os.Chmod(dir, 0o755); err != nil { //nolint:gosec // Necessary.
100-
cleanup()
101-
return nil, errors.Wrapf(err, "cannot make socket directory world-readable")
102-
}
103-
if err := os.Chmod(sockPath, 0o777); err != nil { //nolint:gosec // Necessary.
104-
cleanup()
105-
return nil, errors.Wrapf(err, "cannot make socket file writeable")
69+
return nil, errors.Wrap(err, "cannot create listener for context function")
10670
}
10771

10872
srv := grpc.NewServer(grpc.Creds(insecure.NewCredentials()))
10973
fn := newServer(contextData)
11074
fnv1.RegisterFunctionRunnerServiceServer(srv, fn)
11175

76+
addr := lis.Addr().(*net.TCPAddr) //nolint:forcetypeassert // We specified "tcp" above.
77+
11278
h := &Handle{
113-
Target: "unix://" + sockPath,
79+
// Report the target as 127.0.0.1:PORT since the render machinery knows
80+
// how to handle functions listening on loopback.
81+
target: fmt.Sprintf("127.0.0.1:%d", addr.Port),
11482
srv: srv,
11583
fn: fn,
116-
socketPath: sockPath,
117-
dir: dir,
118-
log: log,
11984
seedInput: &runtime.RawExtension{Raw: si},
12085
captureInput: &runtime.RawExtension{Raw: ci},
12186
}
@@ -129,8 +94,8 @@ func Start(ctx context.Context, log logging.Logger, contextData map[string]any)
12994
return h, nil
13095
}
13196

132-
// Stop gracefully stops the function server and removes the socket directory.
133-
// Safe to call multiple times.
97+
// Stop gracefully stops the function server, which closes its listener. Safe to
98+
// call multiple times.
13499
func (h *Handle) Stop() {
135100
h.stop.Do(func() {
136101
done := make(chan struct{})
@@ -143,8 +108,5 @@ func (h *Handle) Stop() {
143108
case <-time.After(5 * time.Second):
144109
h.srv.Stop()
145110
}
146-
if err := os.RemoveAll(h.dir); err != nil {
147-
h.log.Debug("Cannot remove context function socket directory", "dir", h.dir, "error", err)
148-
}
149111
})
150112
}

cmd/crossplane/render/contextfn/listener_test.go

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package contextfn
1818

1919
import (
2020
"context"
21-
"os"
2221
"testing"
2322
"time"
2423

@@ -42,7 +41,7 @@ func TestListenerRoundTrip(t *testing.T) {
4241
}
4342
defer h.Stop()
4443

45-
conn, err := grpc.NewClient(h.Target, grpc.WithTransportCredentials(insecure.NewCredentials()))
44+
conn, err := grpc.NewClient(h.target, grpc.WithTransportCredentials(insecure.NewCredentials()))
4645
if err != nil {
4746
t.Fatalf("grpc.NewClient: %v", err)
4847
}
@@ -61,26 +60,3 @@ func TestListenerRoundTrip(t *testing.T) {
6160
t.Errorf("context (-want +got):\n%s", diff)
6261
}
6362
}
64-
65-
func TestStopRemovesSocket(t *testing.T) {
66-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
67-
defer cancel()
68-
69-
h, err := Start(ctx, logging.NewNopLogger(), nil)
70-
if err != nil {
71-
t.Fatalf("start: %v", err)
72-
}
73-
74-
if _, err := os.Stat(h.socketPath); err != nil {
75-
t.Fatalf("socket should exist: %v", err)
76-
}
77-
78-
h.Stop()
79-
80-
if _, err := os.Stat(h.socketPath); !os.IsNotExist(err) {
81-
t.Errorf("socket should not exist after Stop, got err=%v", err)
82-
}
83-
84-
// Second Stop is a no-op.
85-
h.Stop()
86-
}

cmd/crossplane/render/contextfn/wire.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ import (
2727
// Mirror the render package's runtime-selection annotations. Duplicated here
2828
// rather than importing cmd/crank/render to avoid an import cycle.
2929
const (
30-
annotationKeyRuntime = "render.crossplane.io/runtime"
31-
annotationValueRuntimeInProcess = "InProcess"
30+
annotationKeyRuntime = "render.crossplane.io/runtime"
31+
annotationValueRuntimeDevelopment = "Development"
32+
annotationKeyRuntimeDevelopmentTarget = "render.crossplane.io/runtime-development-target"
3233
)
3334

3435
// Function returns the Function definition the caller must add to the
@@ -37,8 +38,11 @@ const (
3738
func (h *Handle) Function() pkgv1.Function {
3839
return pkgv1.Function{
3940
ObjectMeta: metav1.ObjectMeta{
40-
Name: FunctionName,
41-
Annotations: map[string]string{annotationKeyRuntime: annotationValueRuntimeInProcess},
41+
Name: FunctionName,
42+
Annotations: map[string]string{
43+
annotationKeyRuntime: annotationValueRuntimeDevelopment,
44+
annotationKeyRuntimeDevelopmentTarget: h.target,
45+
},
4246
},
4347
}
4448
}

cmd/crossplane/render/engine_docker.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package render
1919
import (
2020
"context"
2121
"os"
22-
"path/filepath"
2322
"runtime"
2423
"strings"
2524

@@ -133,17 +132,6 @@ func (e *dockerRenderEngine) Render(ctx context.Context, req *renderv1alpha1.Ren
133132
opts = append(opts, docker.RunWithNetworkName(e.network))
134133
}
135134

136-
// Bind-mount the directory of every unix-socket function target into the
137-
// render container at the same path so unix:// targets are reachable.
138-
for _, fn := range getFunctionInputs(req) {
139-
addr := fn.GetAddress()
140-
if !strings.HasPrefix(addr, "unix://") {
141-
continue
142-
}
143-
dir := filepath.Dir(strings.TrimPrefix(addr, "unix://"))
144-
opts = append(opts, docker.RunWithBindMount(dir, dir))
145-
}
146-
147135
e.log.Debug("Running crossplane internal render in Docker", "image", e.image, "network", e.network)
148136

149137
runner := e.runner
@@ -173,16 +161,3 @@ func (e *dockerRenderEngine) Render(ctx context.Context, req *renderv1alpha1.Ren
173161

174162
return rsp, nil
175163
}
176-
177-
// getFunctionInputs returns the FunctionInput list regardless of which oneof
178-
// variant the RenderRequest carries.
179-
func getFunctionInputs(req *renderv1alpha1.RenderRequest) []*renderv1alpha1.FunctionInput {
180-
switch in := req.GetInput().(type) {
181-
case *renderv1alpha1.RenderRequest_Composite:
182-
return in.Composite.GetFunctions()
183-
case *renderv1alpha1.RenderRequest_Operation:
184-
return in.Operation.GetFunctions()
185-
default:
186-
return nil
187-
}
188-
}

cmd/crossplane/render/op/cmd.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,15 +216,10 @@ func (c *Cmd) Run(k *kong.Context, log logging.Logger, sp terminal.SpinnerPrinte
216216
}
217217
defer render.StopFunctionRuntimes(log, fnAddrs)
218218

219-
addrs := fnAddrs.Addresses()
220-
if ctxHandle != nil {
221-
addrs[contextfn.FunctionName] = ctxHandle.Target
222-
}
223-
224219
// Build and execute the render request.
225220
in := render.OperationInputs{
226221
Operation: op,
227-
FunctionAddrs: addrs,
222+
FunctionAddrs: fnAddrs.Addresses(),
228223
RequiredResources: rrs,
229224
RequiredSchemas: rsc,
230225
FunctionCredentials: fcreds,

cmd/crossplane/render/xr/cmd.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,16 +281,11 @@ func (c *Cmd) Run(k *kong.Context, log logging.Logger, sp terminal.SpinnerPrinte
281281
}
282282
defer render.StopFunctionRuntimes(log, fnAddrs)
283283

284-
addrs := fnAddrs.Addresses()
285-
if ctxHandle != nil {
286-
addrs[contextfn.FunctionName] = ctxHandle.Target
287-
}
288-
289284
// Build and execute the render request.
290285
in := render.CompositionInputs{
291286
CompositeResource: xr,
292287
Composition: comp,
293-
FunctionAddrs: addrs,
288+
FunctionAddrs: fnAddrs.Addresses(),
294289
ObservedResources: ors,
295290
RequiredResources: rrs,
296291
RequiredSchemas: rsc,

0 commit comments

Comments
 (0)