diff --git a/cmd/crossplane/render/engine.go b/cmd/crossplane/render/engine.go index 1e08ce4..333fb79 100644 --- a/cmd/crossplane/render/engine.go +++ b/cmd/crossplane/render/engine.go @@ -65,13 +65,15 @@ type EngineFlags struct { // NewEngineFromFlags creates an Engine from the flag configuration. If a binary // path is set, it returns a local engine. Otherwise it returns a Docker engine -// using the resolved image reference. -func NewEngineFromFlags(f *EngineFlags, log logging.Logger) Engine { +// using the resolved image reference. The network parameter sets the Docker +// network the render container should join; it is derived from function +// annotations (AnnotationKeyRuntimeDockerNetwork) by the caller. +func NewEngineFromFlags(f *EngineFlags, network string, log logging.Logger) Engine { if f.CrossplaneBinary != "" { return &localRenderEngine{BinaryPath: f.CrossplaneBinary} } - return &dockerRenderEngine{image: crossplaneImageFromFlags(f), log: log} + return &dockerRenderEngine{image: crossplaneImageFromFlags(f), network: network, log: log} } func crossplaneImageFromFlags(f *EngineFlags) string { diff --git a/cmd/crossplane/render/engine_docker.go b/cmd/crossplane/render/engine_docker.go index b67cf54..63ccb3d 100644 --- a/cmd/crossplane/render/engine_docker.go +++ b/cmd/crossplane/render/engine_docker.go @@ -53,8 +53,7 @@ func (realContainerRunner) Run(ctx context.Context, img string, opts ...docker.R type dockerRenderEngine struct { // image is the Crossplane Docker image reference. image string - // network is the Docker network to connect the container to. When set, - // the container joins this network so it can reach function containers. + // network is the Docker network to connect the container to. network string log logging.Logger @@ -83,19 +82,28 @@ func (e *dockerRenderEngine) CheckContextSupport() error { // containers also join it. The returned cleanup function removes the // network. func (e *dockerRenderEngine) Setup(ctx context.Context, fns []pkgv1.Function) (func(), error) { - networkID, networkName, err := createRenderNetwork(ctx) - if err != nil { - return func() {}, errors.Wrap(err, "cannot create Docker network for rendering") - } + var networkID, networkName string + + if e.network == "" { + var err error + networkID, networkName, err = createRenderNetwork(ctx) + if err != nil { + return func() {}, errors.Wrap(err, "cannot create Docker network for rendering") + } + e.network = networkName + + injectNetworkAnnotation(fns, networkName) - e.network = networkName - injectNetworkAnnotation(fns, networkName) + cleanup := func() { //nolint:contextcheck // Detached context for cleanup. + _ = removeRenderNetwork(context.Background(), networkID) + } - cleanup := func() { //nolint:contextcheck // Detached context for cleanup. - _ = removeRenderNetwork(context.Background(), networkID) + return cleanup, nil } - return cleanup, nil + // e.network was pre-configured by the caller (e.g. from a function + // annotation). We don't own the network, so there is nothing to clean up. + return func() {}, nil } // Render marshals the request, runs it through a Docker container, and returns diff --git a/cmd/crossplane/render/op/cmd.go b/cmd/crossplane/render/op/cmd.go index 04e6bc9..ee70914 100644 --- a/cmd/crossplane/render/op/cmd.go +++ b/cmd/crossplane/render/op/cmd.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "time" "github.com/alecthomas/kong" @@ -84,7 +85,7 @@ type Cmd struct { fs afero.Fs // newEngine constructs the render Engine. - newEngine func(*render.EngineFlags, logging.Logger) render.Engine + newEngine func(*render.EngineFlags, string, logging.Logger) render.Engine } // Help prints out the help for the alpha render op command. @@ -167,7 +168,20 @@ func (c *Cmd) Run(k *kong.Context, log logging.Logger, sp terminal.SpinnerPrinte } } - engine := c.newEngine(&c.EngineFlags, log) + var network string + for _, annotation := range c.FunctionAnnotations { + parts := strings.SplitN(annotation, "=", 2) + if len(parts) != 2 { + return errors.Errorf("invalid function annotation format %q, expected key=value", annotation) + } + key, value := parts[0], parts[1] + if key == render.AnnotationKeyRuntimeDockerNetwork { + network = value + break + } + } + + engine := c.newEngine(&c.EngineFlags, network, log) seedCtx := len(c.ContextValues) > 0 || len(c.ContextFiles) > 0 captureCtx := c.IncludeContext diff --git a/cmd/crossplane/render/op/cmd_test.go b/cmd/crossplane/render/op/cmd_test.go index f17ec0c..6a9e16e 100644 --- a/cmd/crossplane/render/op/cmd_test.go +++ b/cmd/crossplane/render/op/cmd_test.go @@ -63,8 +63,8 @@ var includeFunctionResultsOutput string //go:embed testdata/cmd/output/include-full-operation.yaml var includeFullOperationOutput string -func newEngineFunc(engine render.Engine) func(*render.EngineFlags, logging.Logger) render.Engine { - return func(*render.EngineFlags, logging.Logger) render.Engine { +func newEngineFunc(engine render.Engine) func(*render.EngineFlags, string, logging.Logger) render.Engine { + return func(*render.EngineFlags, string, logging.Logger) render.Engine { return engine } } diff --git a/cmd/crossplane/render/render.go b/cmd/crossplane/render/render.go index 5cc373f..25b0be8 100644 --- a/cmd/crossplane/render/render.go +++ b/cmd/crossplane/render/render.go @@ -158,7 +158,11 @@ func injectNetworkAnnotation(fns []pkgv1.Function, networkName string) { if fns[i].Annotations == nil { fns[i].Annotations = make(map[string]string) } - fns[i].Annotations[AnnotationKeyRuntimeDockerNetwork] = networkName + + _, ok := fns[i].Annotations[AnnotationKeyRuntimeDockerNetwork] + if !ok { + fns[i].Annotations[AnnotationKeyRuntimeDockerNetwork] = networkName + } } } diff --git a/cmd/crossplane/render/xr/cmd.go b/cmd/crossplane/render/xr/cmd.go index f3995b7..70401ef 100644 --- a/cmd/crossplane/render/xr/cmd.go +++ b/cmd/crossplane/render/xr/cmd.go @@ -92,7 +92,7 @@ type Cmd struct { fs afero.Fs // newEngine constructs the render Engine. - newEngine func(*render.EngineFlags, logging.Logger) render.Engine + newEngine func(*render.EngineFlags, string, logging.Logger) render.Engine } // Help prints out the help for the render command. @@ -224,7 +224,20 @@ func (c *Cmd) Run(k *kong.Context, log logging.Logger, sp terminal.SpinnerPrinte } } - engine := c.newEngine(&c.EngineFlags, log) + var network string + for _, annotation := range c.FunctionAnnotations { + parts := strings.SplitN(annotation, "=", 2) + if len(parts) != 2 { + return errors.Errorf("invalid function annotation format %q, expected key=value", annotation) + } + key, value := parts[0], parts[1] + if key == render.AnnotationKeyRuntimeDockerNetwork { + network = value + break + } + } + + engine := c.newEngine(&c.EngineFlags, network, log) seedCtx := len(c.ContextValues) > 0 || len(c.ContextFiles) > 0 captureCtx := c.IncludeContext diff --git a/cmd/crossplane/render/xr/cmd_test.go b/cmd/crossplane/render/xr/cmd_test.go index d57cc14..cf40315 100644 --- a/cmd/crossplane/render/xr/cmd_test.go +++ b/cmd/crossplane/render/xr/cmd_test.go @@ -78,8 +78,8 @@ var includeFunctionResultsOutput string //go:embed testdata/cmd/output/include-full-xr.yaml var includeFullXROutput string -func newEngineFunc(engine render.Engine) func(*render.EngineFlags, logging.Logger) render.Engine { - return func(*render.EngineFlags, logging.Logger) render.Engine { +func newEngineFunc(engine render.Engine) func(*render.EngineFlags, string, logging.Logger) render.Engine { + return func(*render.EngineFlags, string, logging.Logger) render.Engine { return engine } }