Skip to content

Commit dcfb0d7

Browse files
authored
Merge pull request #326 from Evernorth/support-namespaced-extraresources
Support Namespaced ExtraResources
2 parents a56b7ab + 65168a4 commit dcfb0d7

File tree

12 files changed

+233
-7
lines changed

12 files changed

+233
-7
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,9 @@ spec:
539539
By defining one or more special `ExtraResources`, you can ask Crossplane to retrieve additional resources from the local cluster and make them available to your templates. See the [docs](https://github.com/crossplane/crossplane/blob/main/design/design-doc-composition-functions-extra-resources.md) for more information.
540540

541541
> With ExtraResources, you can fetch cluster-scoped resources, but not namespaced resources such as claims.
542-
> If you need to get a composite resource via its claim name you can use `matchLabels` with `crossplane.io/claim-name: <claimname>`
542+
> If you need to get a composite resource via its claim name you can use `matchLabels` with `crossplane.io/claim-name: <claimname>`.
543+
544+
> Namespace scoped resources can be queried with the `matchNamespace` field. Leaving the `matchNamespace` field empty or not defining it will query a cluster scoped resource.
543545

544546
```yaml
545547
apiVersion: krm.kcl.dev/v1alpha1
@@ -564,6 +566,20 @@ spec:
564566
apiVersion: "example.com/v1beta1",
565567
kind: "Bar",
566568
matchName: "my-bar"
569+
},
570+
baz = {
571+
apiVersion: "example.m.com/v1beta1",
572+
kind: "Bar",
573+
matchName: "my-bar"
574+
matchNamespace: "my-baz-ns"
575+
},
576+
quux = {
577+
apiVersion: "example.m.com/v1beta1",
578+
kind: "Quux",
579+
matchLabels: {
580+
"baz": "quux"
581+
}
582+
matchNamespace: "my-quux-ns"
567583
}
568584
}
569585
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
run:
2+
crossplane render --verbose xr.yaml composition.yaml functions.yaml -r --extra-resources extra_resources_namespaced.yaml
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Example Manifests
2+
3+
You can run your function locally and test it using `crossplane render`
4+
with these example manifests.
5+
6+
```shell
7+
# Run the function locally
8+
$ go run . --insecure --debug
9+
```
10+
11+
```shell
12+
# Then, in another terminal, call it with these example manifests
13+
$ crossplane render --verbose xr.yaml composition.yaml functions.yaml -r --extra-resources extra_resources_namespaced.yaml
14+
---
15+
---
16+
apiVersion: example.crossplane.io/v1beta1
17+
kind: XR
18+
metadata:
19+
name: example
20+
status:
21+
conditions:
22+
- lastTransitionTime: "2024-01-01T00:00:00Z"
23+
message: 'Unready resources: another-awesome-dev-bucket, my-awesome-dev-bucket'
24+
reason: Creating
25+
status: "False"
26+
type: Ready
27+
---
28+
apiVersion: example/v1alpha1
29+
kind: Foo
30+
metadata:
31+
annotations:
32+
crossplane.io/composition-resource-name: another-awesome-dev-bucket
33+
generateName: example-
34+
labels:
35+
crossplane.io/composite: example
36+
name: another-awesome-dev-bucket
37+
ownerReferences:
38+
- apiVersion: example.crossplane.io/v1beta1
39+
blockOwnerDeletion: true
40+
controller: true
41+
kind: XR
42+
name: example
43+
uid: ""
44+
---
45+
apiVersion: example/v1alpha1
46+
kind: Foo
47+
metadata:
48+
annotations:
49+
crossplane.io/composition-resource-name: my-awesome-dev-bucket
50+
generateName: example-
51+
labels:
52+
crossplane.io/composite: example
53+
name: my-awesome-dev-bucket
54+
ownerReferences:
55+
- apiVersion: example.crossplane.io/v1beta1
56+
blockOwnerDeletion: true
57+
controller: true
58+
kind: XR
59+
name: example
60+
uid: ""
61+
```
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
apiVersion: apiextensions.crossplane.io/v1
2+
kind: Composition
3+
metadata:
4+
name: function-template-go
5+
spec:
6+
compositeTypeRef:
7+
apiVersion: example.crossplane.io/v1beta1
8+
kind: XR
9+
mode: Pipeline
10+
pipeline:
11+
- step: normal
12+
functionRef:
13+
name: kcl-function
14+
input:
15+
apiVersion: krm.kcl.dev/v1alpha1
16+
kind: KCLInput
17+
metadata:
18+
annotations:
19+
"krm.kcl.dev/default_ready": "True"
20+
name: basic
21+
spec:
22+
source: |
23+
oxr = option("params").oxr
24+
er = option("params")?.extraResources
25+
26+
foo = [{
27+
apiVersion: "example/v1alpha1"
28+
kind: "Foo"
29+
metadata = {
30+
name: k.Resource.metadata.name
31+
}
32+
} for k in er?.bucket] if er?.bucket else []
33+
34+
dxr = {
35+
**oxr
36+
}
37+
38+
details = {
39+
apiVersion: "meta.krm.kcl.dev/v1alpha1"
40+
kind: "ExtraResources"
41+
requirements = {
42+
bucket = {
43+
apiVersion: "s3.aws.m.upbound.io/v1beta1",
44+
kind: "Bucket",
45+
matchLabels: {
46+
"foo": "bar"
47+
}
48+
matchNamespace: "awesome-namespace"
49+
}
50+
}
51+
}
52+
items = [
53+
details
54+
dxr
55+
] + foo
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apiVersion: s3.aws.m.upbound.io/v1beta1
2+
kind: Bucket
3+
metadata:
4+
annotations:
5+
crossplane.io/external-name: my-awesome-dev-bucket
6+
labels:
7+
foo: bar
8+
name: my-awesome-dev-bucket
9+
namespace: awesome-namespace
10+
spec:
11+
forProvider:
12+
region: us-west-1
13+
status:
14+
atProvider:
15+
id: random-bucket-id
16+
---
17+
apiVersion: s3.aws.m.upbound.io/v1beta1
18+
kind: Bucket
19+
metadata:
20+
annotations:
21+
crossplane.io/external-name: my-awesome-dev-bucket
22+
labels:
23+
foo: bar
24+
name: another-awesome-dev-bucket
25+
namespace: awesome-namespace
26+
spec:
27+
forProvider:
28+
region: us-west-1
29+
status:
30+
atProvider:
31+
id: random-bucket-id
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: pkg.crossplane.io/v1beta1
2+
kind: Function
3+
metadata:
4+
name: kcl-function
5+
annotations:
6+
# This tells crossplane render to connect to the function locally.
7+
render.crossplane.io/runtime: Development
8+
spec:
9+
package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: example.crossplane.io/v1beta1
2+
kind: XR
3+
metadata:
4+
name: example
5+
spec:
6+
count: 1

fn_test.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ func TestRunFunctionSimple(t *testing.T) {
214214
"spec": {
215215
"target": "Default",
216216
"source": "items = [\n{\n apiVersion: \"meta.krm.kcl.dev/v1alpha1\"\n kind: \"ExtraResources\"\n requirements = {\n \"cool-extra-resource\" = {\n apiVersion: \"example.org/v1\"\n kind: \"CoolExtraResource\"\n matchName: \"cool-extra-resource\"\n }\n }\n},\n{\n apiVersion: \"meta.krm.kcl.dev/v1alpha1\"\n kind: \"ExtraResources\"\n requirements = {\n \"another-cool-extra-resource\" = {\n apiVersion: \"example.org/v1\"\n kind: \"CoolExtraResource\"\n matchLabels = {\n key: \"value\"\n }\n }\n \"yet-another-cool-extra-resource\" = {\n apiVersion: \"example.org/v1\"\n kind: \"CoolExtraResource\"\n matchName: \"foo\"\n }\n }\n},\n{\n apiVersion: \"meta.krm.kcl.dev/v1alpha1\"\n kind: \"ExtraResources\"\n requirements = {\n \"all-cool-resources\" = {\n apiVersion: \"example.org/v1\"\n kind: \"CoolExtraResource\"\n matchLabels = {}\n }\n }\n}\n]\n"
217-
}
218-
}`),
217+
}
218+
}`),
219219
Observed: &fnv1.State{
220220
Composite: &fnv1.Resource{
221221
Resource: resource.MustStructJSON(xr),
@@ -286,6 +286,46 @@ func TestRunFunctionSimple(t *testing.T) {
286286
},
287287
},
288288
},
289+
"ExtraResourcesNamespacedMatchName": {
290+
reason: "The Function should pass through a single extra namespaced resource with matchName and matchNamespace",
291+
args: args{
292+
req: &fnv1.RunFunctionRequest{
293+
Meta: &fnv1.RequestMeta{Tag: "extra-resources-namespace-matchname"},
294+
Input: resource.MustStructJSON(`{
295+
"apiVersion": "krm.kcl.dev/v1alpha1",
296+
"kind": "KCLInput",
297+
"metadata": {"name": "basic"},
298+
"spec": {
299+
"target": "Default",
300+
"source": "items = [{ apiVersion: \"meta.krm.kcl.dev/v1alpha1\", kind: \"ExtraResources\", requirements = { \"cool-ns-resource-matchname\" = { apiVersion: \"example.m.org/v1\", kind: \"CoolExtraResource\", matchNamespace: \"cool-ns-scoped-ns\", matchName: \"cool-ns-scoped-resource\" } } }]"
301+
}
302+
}`),
303+
Observed: &fnv1.State{
304+
Composite: &fnv1.Resource{Resource: resource.MustStructJSON(xr)},
305+
},
306+
Desired: &fnv1.State{
307+
Composite: &fnv1.Resource{Resource: resource.MustStructJSON(xr)},
308+
},
309+
},
310+
},
311+
want: want{
312+
rsp: &fnv1.RunFunctionResponse{
313+
Meta: &fnv1.ResponseMeta{Tag: "extra-resources-namespace-matchname", Ttl: durationpb.New(response.DefaultTTL)},
314+
Results: []*fnv1.Result{},
315+
Requirements: &fnv1.Requirements{
316+
ExtraResources: map[string]*fnv1.ResourceSelector{
317+
"cool-ns-resource-matchname": {
318+
ApiVersion: "example.m.org/v1",
319+
Kind: "CoolExtraResource",
320+
Namespace: ptr.To[string]("cool-ns-scoped-ns"),
321+
Match: &fnv1.ResourceSelector_MatchName{MatchName: "cool-ns-scoped-resource"},
322+
},
323+
},
324+
},
325+
Desired: &fnv1.State{Composite: &fnv1.Resource{Resource: resource.MustStructJSON(xr)}},
326+
},
327+
},
328+
},
289329
"ExtraResourcesIn": {
290330
reason: "The Function should return the extra resources from the request.",
291331
args: args{

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
dario.cat/mergo v1.0.1
99
github.com/alecthomas/kong v1.12.1
1010
github.com/crossplane/crossplane-runtime v1.20.0
11-
github.com/crossplane/function-sdk-go v0.4.0
11+
github.com/crossplane/function-sdk-go v0.5.0-rc.0.0.20250805171053-2910b68d255d
1212
github.com/go-logr/logr v1.4.3
1313
github.com/google/go-cmp v0.7.0
1414
github.com/pkg/errors v0.9.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,8 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
750750
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
751751
github.com/crossplane/crossplane-runtime v1.20.0 h1:I54uipRIecqZyms+vz1J/l62yjVQ7HV5w+Nh3RMrUtc=
752752
github.com/crossplane/crossplane-runtime v1.20.0/go.mod h1:lfV1VJenDc9PNVLxDC80YjPoTm+JdSZ13xlS2h37Dvg=
753-
github.com/crossplane/function-sdk-go v0.4.0 h1:1jd+UIaZlVNQCUO4hLAgUqWBRnUKw2ObF9ZuMw5CpKk=
754-
github.com/crossplane/function-sdk-go v0.4.0/go.mod h1:jLnzUG8pt8tn/U6/uvtNStAhDjhIq4wCR31yECT54NM=
753+
github.com/crossplane/function-sdk-go v0.5.0-rc.0.0.20250805171053-2910b68d255d h1:bzt8qEg9I2GrLc216IuuTn4x+GECxc+DoGlDZ4PMuJY=
754+
github.com/crossplane/function-sdk-go v0.5.0-rc.0.0.20250805171053-2910b68d255d/go.mod h1:fEwSBgMH6+kicaBeOWz6PZRwhjLg4tu9QEDeP/9O2yE=
755755
github.com/crossplane/upjet v1.4.1-0.20240911184956-3afbb7796d46 h1:2IH1YPTBrNmBj0Z1OCjEBTrQCuRaLutZbWLaswFeCFQ=
756756
github.com/crossplane/upjet v1.4.1-0.20240911184956-3afbb7796d46/go.mod h1:wkdZf/Cvhr6PI30VdHIOjg4dX39Z5uijqnLWFk5PbGM=
757757
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=

0 commit comments

Comments
 (0)