Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions cmd/crossplane/render/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,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 {
Expand Down
30 changes: 19 additions & 11 deletions cmd/crossplane/render/engine_docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ import (
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
Expand All @@ -61,19 +60,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
Expand Down
18 changes: 16 additions & 2 deletions cmd/crossplane/render/op/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"time"

"github.com/alecthomas/kong"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions cmd/crossplane/render/op/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
8 changes: 6 additions & 2 deletions cmd/crossplane/render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,18 @@ func SetComposedResourceMetadata(cd resource.Object, xr resource.LegacyComposite
return errors.Wrapf(meta.AddControllerReference(cd, or), "cannot set composite resource %q as controller ref of composed resource", xr.GetName())
}

// injectNetworkAnnotation sets the Docker network annotation on all functions
// injectNetworkAnnotation sets the Docker network annotation on all functions without existing runtime-docker-network annotation
// so their containers join the specified network.
func injectNetworkAnnotation(fns []pkgv1.Function, networkName string) {
for i := range fns {
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
}
}
}

Expand Down
65 changes: 65 additions & 0 deletions cmd/crossplane/render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/composed"
ucomposite "github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/composite"
"github.com/crossplane/crossplane-runtime/v2/pkg/resource/unstructured/reference"

pkgv1 "github.com/crossplane/crossplane/apis/v2/pkg/v1"
)

func TestGetSecret(t *testing.T) {
Expand Down Expand Up @@ -231,6 +233,69 @@ func TestSetComposedResourceMetadata(t *testing.T) {
}
}

func TestInjectNetworkAnnotation(t *testing.T) {
cases := map[string]struct {
reason string
fns []pkgv1.Function
networkName string
want []pkgv1.Function
}{
"SetsAnnotationWhenAbsent": {
reason: "The network annotation should be set when the function has no existing annotation.",
networkName: "render-network",
fns: []pkgv1.Function{
{ObjectMeta: metav1.ObjectMeta{Name: "fn-a"}},
},
want: []pkgv1.Function{
{ObjectMeta: metav1.ObjectMeta{Name: "fn-a", Annotations: map[string]string{
AnnotationKeyRuntimeDockerNetwork: "render-network",
}}},
},
},
"DoesNotOverwriteExistingAnnotation": {
reason: "The network annotation should not be overwritten when the function already has one set.",
networkName: "render-network",
fns: []pkgv1.Function{
{ObjectMeta: metav1.ObjectMeta{Name: "fn-a", Annotations: map[string]string{
AnnotationKeyRuntimeDockerNetwork: "my-custom-network",
}}},
},
want: []pkgv1.Function{
{ObjectMeta: metav1.ObjectMeta{Name: "fn-a", Annotations: map[string]string{
AnnotationKeyRuntimeDockerNetwork: "my-custom-network",
}}},
},
},
"MixedFunctions": {
reason: "The annotation should only be set on functions that don't already have it.",
networkName: "render-network",
fns: []pkgv1.Function{
{ObjectMeta: metav1.ObjectMeta{Name: "fn-a"}},
{ObjectMeta: metav1.ObjectMeta{Name: "fn-b", Annotations: map[string]string{
AnnotationKeyRuntimeDockerNetwork: "my-custom-network",
}}},
},
want: []pkgv1.Function{
{ObjectMeta: metav1.ObjectMeta{Name: "fn-a", Annotations: map[string]string{
AnnotationKeyRuntimeDockerNetwork: "render-network",
}}},
{ObjectMeta: metav1.ObjectMeta{Name: "fn-b", Annotations: map[string]string{
AnnotationKeyRuntimeDockerNetwork: "my-custom-network",
}}},
},
},
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
injectNetworkAnnotation(tc.fns, tc.networkName)
if diff := cmp.Diff(tc.want, tc.fns); diff != "" {
t.Errorf("\n%s\ninjectNetworkAnnotation(...): -want +got:\n%s", tc.reason, diff)
}
})
}
}

func MustStructJSON(j string) *structpb.Struct {
s := &structpb.Struct{}
if err := protojson.Unmarshal([]byte(j), s); err != nil {
Expand Down
18 changes: 16 additions & 2 deletions cmd/crossplane/render/xr/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"time"

"dario.cat/mergo"
Expand Down Expand Up @@ -90,7 +91,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.
Expand Down Expand Up @@ -222,7 +223,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
Expand Down
4 changes: 2 additions & 2 deletions cmd/crossplane/render/xr/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
Loading