Skip to content

Commit 3b7c54d

Browse files
authored
Merge pull request #28 from deveeztech/feature/add-regex-support
Feature/add regex support
2 parents 2f29e55 + d480d6c commit 3b7c54d

File tree

6 files changed

+503
-10
lines changed

6 files changed

+503
-10
lines changed

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,47 @@ the `first-resource` is ready.
2424
2525
See `example/composition.yaml` for a complete example.
2626

27+
28+
It also allows you to provide a regex to match and capture a whole group of resources and wait for them to be ready.
29+
30+
For example, the pipeline step below, will ensure that `second-resource` is not created until all `first-subresource-*` objects are ready.
31+
32+
```yaml
33+
- step: sequence-creation
34+
functionRef:
35+
name: function-sequencer
36+
input:
37+
apiVersion: sequencer.fn.crossplane.io/v1beta1
38+
kind: Input
39+
rules:
40+
- sequence:
41+
- first-subresource-*
42+
- second-resource
43+
```
44+
45+
See `example/composition-regex.yaml` for a complete example.
46+
2747
## Installation
2848

2949
It can be installed as follows from the Upbound marketplace: https://marketplace.upbound.io/functions/crossplane-contrib/function-sequencer
50+
51+
## Developing this function
52+
53+
You can use `go run` to run your function locally
54+
```sh
55+
go run . --insecure --debug
56+
```
57+
58+
After that, you can use [Crossplane CLI's](https://docs.crossplane.io/latest/cli/) `crossplane render` to test what your function is doing.
59+
60+
To test a sequence:
61+
```sh
62+
# --observed-resources allows you to simulate already created objects
63+
crossplane render example/xr.yaml example/composition.yaml example/functions.yaml --observed-resources example/observed.yaml
64+
```
65+
66+
To test a regex sequence:
67+
```sh
68+
# --observed-resources allows you to simulate already created objects
69+
crossplane render example/xr.yaml example/composition-regex.yaml example/functions.yaml --observed-resources example/observed-regex.yaml
70+
```

example/composition-regex.yaml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
apiVersion: apiextensions.crossplane.io/v1
2+
kind: Composition
3+
metadata:
4+
name: function-sequencer-regex
5+
spec:
6+
compositeTypeRef:
7+
apiVersion: example.crossplane.io/v1
8+
kind: XR
9+
mode: Pipeline
10+
pipeline:
11+
- step: patch-and-transform
12+
functionRef:
13+
name: function-patch-and-transform
14+
input:
15+
apiVersion: pt.fn.crossplane.io/v1beta1
16+
kind: Resources
17+
resources:
18+
- name: first-subresource-1
19+
base:
20+
apiVersion: nop.crossplane.io/v1alpha1
21+
kind: NopResource
22+
metadata:
23+
name: first-subresource-1
24+
spec:
25+
forProvider:
26+
conditionAfter:
27+
- time: 5s
28+
conditionType: Ready
29+
conditionStatus: "False"
30+
- time: 10s
31+
conditionType: Ready
32+
conditionStatus: "True"
33+
# We should not delete the dependent resources if this turns back to unready.
34+
- time: 30s
35+
conditionType: Ready
36+
conditionStatus: "False"
37+
- time: 90s
38+
conditionType: Ready
39+
conditionStatus: "True"
40+
- name: first-subresource-2
41+
base:
42+
apiVersion: nop.crossplane.io/v1alpha1
43+
kind: NopResource
44+
metadata:
45+
name: first-subresource-2
46+
spec:
47+
forProvider:
48+
conditionAfter:
49+
- time: 5s
50+
conditionType: Ready
51+
conditionStatus: "False"
52+
- time: 10s
53+
conditionType: Ready
54+
conditionStatus: "True"
55+
- name: second-resource
56+
base:
57+
apiVersion: nop.crossplane.io/v1alpha1
58+
kind: NopResource
59+
metadata:
60+
name: second-resource
61+
spec:
62+
forProvider:
63+
conditionAfter:
64+
- time: 5s
65+
conditionType: Ready
66+
conditionStatus: "False"
67+
- time: 10s
68+
conditionType: Ready
69+
conditionStatus: "True"
70+
- step: detect-readiness
71+
functionRef:
72+
name: function-auto-ready
73+
- step: sequence-creation
74+
functionRef:
75+
name: function-sequencer
76+
input:
77+
apiVersion: sequencer.fn.crossplane.io/v1beta1
78+
kind: Input
79+
rules:
80+
- sequence:
81+
- first-subresource-*
82+
- second-resource

example/observed-regex.yaml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
apiVersion: nop.crossplane.io/v1alpha1
2+
kind: NopResource
3+
metadata:
4+
annotations:
5+
crossplane.io/external-name: foo
6+
crossplane.io/composition-resource-name: first-subresource-2
7+
name: first
8+
spec:
9+
forProvider:
10+
conditionAfter:
11+
- conditionStatus: "True"
12+
conditionType: Ready
13+
time: 1s
14+
- conditionStatus: "False"
15+
conditionType: Ready
16+
time: 0s
17+
status:
18+
atProvider: {}
19+
conditions:
20+
- lastTransitionTime: "2024-02-17T11:56:27Z"
21+
reason: ReconcileSuccess
22+
status: "True"
23+
type: Synced
24+
- lastTransitionTime: "2024-02-17T11:56:28Z"
25+
reason: ""
26+
status: "True"
27+
type: Ready
28+
---
29+
apiVersion: nop.crossplane.io/v1alpha1
30+
kind: NopResource
31+
metadata:
32+
annotations:
33+
crossplane.io/external-name: foo
34+
crossplane.io/composition-resource-name: first-subresource-1
35+
name: first
36+
spec:
37+
forProvider:
38+
conditionAfter:
39+
- conditionStatus: "True"
40+
conditionType: Ready
41+
time: 1s
42+
- conditionStatus: "False"
43+
conditionType: Ready
44+
time: 0s
45+
status:
46+
atProvider: {}
47+
conditions:
48+
- lastTransitionTime: "2024-02-17T11:56:27Z"
49+
reason: ReconcileSuccess
50+
status: "True"
51+
type: Synced
52+
- lastTransitionTime: "2024-02-17T11:56:28Z"
53+
reason: ""
54+
status: "True"
55+
type: Ready

fn.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"context"
55
"fmt"
6+
"regexp"
67

78
"github.com/crossplane/crossplane-runtime/pkg/errors"
89
"github.com/crossplane/crossplane-runtime/pkg/logging"
@@ -62,10 +63,38 @@ func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequ
6263
continue
6364
}
6465
for _, before := range sequence[:i] {
65-
if b, ok := desiredComposed[before]; !ok || b.Ready != resource.ReadyTrue {
66-
// A resource that should exist before this one is not in the desired list, or it is not ready yet.
67-
// So, we should not create the resource waiting for it yet.
68-
msg := fmt.Sprintf("Delaying creation of resource %q because %q is not ready or does not exist yet", r, before)
66+
re := regexp.MustCompile(string(before))
67+
keys := []resource.Name{}
68+
for k := range desiredComposed {
69+
if re.MatchString(string(k)) {
70+
keys = append(keys, k)
71+
}
72+
}
73+
74+
// We'll treat everything the same way adding all resources to the keys slice
75+
// and then checking if they are ready.
76+
desired := len(keys)
77+
readyResources := 0
78+
for _, k := range keys {
79+
if b, ok := desiredComposed[k]; ok && b.Ready == resource.ReadyTrue {
80+
// resource is ready, add it to the counter
81+
readyResources++
82+
}
83+
}
84+
85+
if desired == 0 || desired != readyResources {
86+
// no resource created
87+
msg := fmt.Sprintf("Delaying creation of resource %q because %q does not exist yet", r, before)
88+
if desired > 0 {
89+
// provide a nicer message if there are resources.
90+
msg = fmt.Sprintf(
91+
"Delaying creation of resource %q because %q is not fully ready (%d of %d)",
92+
r,
93+
before,
94+
readyResources,
95+
desired,
96+
)
97+
}
6998
response.Normal(rsp, msg)
7099
f.log.Info(msg)
71100
delete(desiredComposed, r)

0 commit comments

Comments
 (0)