# Custom WaitRule for Resources
## Today waitRules can be defined like this:
```
waitRules:
- supportsObservedGeneration: true
conditionMatchers:
- type: Failed
status: "True"
failure: true
- type: Deployed
status: "True"
success: true
resourceMatchers:
- apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: DatabaseInstance}
```
We are lacking the waitRules to define on custom objects and transitive resources. I propose below for the waitRules on custom Resources:
## Path Approach
### Scenario 1:
I want to define the waitRule w.r.t. `currentState` value of `status`. e.g.
```
...
status:
currentState: Running
...
```
Then waitRule can be:
```
waitRules:
customMatchers:
paths:
- path/Key: [status, currentState]
value: "Failed"
failure: true
- path/Key: [status, currentState]
value: "Running"
success: true
resourceMatchers:
- apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: DatabaseInstance}
```
### Scenario 2:
I want to define the waitRule w.r.t. condition Type and status value. e.g.
```
status:
conditions:
- type: Ready
status: "False"
...
- type: Failed
status: "True"
...
```
Then waitRule can be:
```
waitRules:
customMatchers:
paths:
- path/Key: [status, conditions, {type:Failed}, status]
value: "True"
failure: true
- path/Key: [status, conditions, {type:Ready}, status]
value: "True"
success: true
resourceMatchers:
- apiVersionKindMatcher: {apiVersion: v1, kind: Pod}
```
## YTT integration Approach:
```
waitRules:
yttMatchers:
- ytt:
assertContractV1:
assertion.yml: |
...
success/failure: true
resourceMatchers:
- apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: DatabaseInstance}
```
### Scenario 1:
waitRules with ytt:
```
...
status:
currentState: Running
...
```
WaitRule defined with starlark (.star) file instead of yaml -
```
waitRules:
yttMatchers:
- ytt:
waitRuleContractV1:
rules.star: |
load("@ytt:data", "data")
def DoneApplyState(state):
if state == "Failed":
return {"Done":True, "Successful": False, "Message": ""}
elif state == "Running":
return {"Done":True, "Successful": True, "Message": ""}
else:
return {"Done":False, "Successful": False, "Message": "Not in Failed or Running state"}
end
end
DoneApplyState(data.values.status.currentState))
resourceMatchers:
- apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: DatabaseInstance}
```
using yaml file -
```
waitRules:
yttMatchers:
- ytt:
assertContractV1:
assertion.yml: |
#@ load("@ytt:data", "data")
val = data.values.status.currentState
#@ val if val == "Failed" else assert.fail("Not in the failed state")
failure: true
- ytt:
assertContractV1:
assertion.yml: |
#@ load("@ytt:data", "data")
#@ load("@ytt:assertion", "assertion")
#@ val = data.values.status.currentState
#@ val if val == "Running" else assert.fail("Not in the running state")
success: true
resourceMatchers:
- apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: DatabaseInstance}
```
### Scenario 2:
I want to define the waitRule w.r.t. condition Type and status value. e.g.
```
status:
conditions:
- type: Ready
status: "False"
...
- type: Failed
status: "True"
...
```
WaitRule defined with starlark (.star) file -
```
waitRules:
yttMatchers:
- ytt:
assertContractV1:
rules.star: |
load("@ytt:data", "data")
def DoneApplyState(conditions):
for k in conditions:
if k.type == "Ready" and k.status == "True":
return {"Done":True, "Successful": True, "Message": ""}
elif k.type == "Failed" and k.status == "True":
return {"Done":True, "Successful": False, "Message": ""}
# is type Failed and status False will be considered as success ?
# should we have a check for status to be either true or false ?
end
end
end
DoneApplyState(data.values.status.conditions)
resourceMatchers:
- apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: DatabaseInstance}
```
With yaml file -
```
waitRules:
yttMatchers:
- ytt:
assertContractV1:
assertion.yml: |
#@ load("@ytt:data", "data")
#@ load("@ytt:assert", "assert")
#@ secrets = []
#@ conditions = data.values.status.conditions
#@ status = None
#@ for k in conditions:
#@ if k.type=="Ready":
#@ status = k.status
#@ end
#@ end
#@ status if status == "True" else assert.fail("status is not True")
success: True
- ytt:
assertContractV1:
assertion.yml: |
#@ load("@ytt:data", "data")
#@ load("@ytt:assert", "assert")
#@ secrets = []
#@ conditions = data.values.status.conditions
#@ status = None
#@ for k in conditions:
#@ if k.type=="Failed":
#@ status = k.status
#@ end
#@ end
#@ status if status == "True" else assert.fail("status is not True")
failure: True
resourceMatchers:
- apiVersionKindMatcher: {apiVersion: corp.com/v1, kind: DatabaseInstance}
```
### Pros/Cons of each Approach
#### Path Approach
##### Pros
1. No need to learn anything new.
##### Cons
1. Difficult to handle scenario which are not string match e.g. less than or greater than.
#### yttMatcher Approach
##### Pros
1. All the scenarios such as string match, less than or greater than etc. can be taken care by ytt itself.
##### Cons
1. User needs to have knowledge of ytt.