+1
I've implemented this for the server side at
https://github.com/apache/brooklyn-server/pull/999 .
One minor tweak to Aled's proposal, I think we should output a
structured YAML rather than the toString, so clients don't have to do
complex parsing. IE instead of sending the string syntax
`requiredUnless("X")` we'd send `requiredUnless: X`. (On input we can
accept either representation, a toString or yaml.)
Will add support in the UI next (new PR obviously).
Best
Alex
On 21/09/2018 09:08, Geoff Macartney wrote:
Hi Aled,
I'd say go for it, that looks like something that could be valuable in
various cases.
I take it your example of "exactly one of config X or config Y" would be
expressed
along the lines of a parallel set of constraints between each config -
On X:
constraints:
- requiredUnless("Y")
- forbiddenIf("Y")
On Y:
constraints:
- requiredUnless("X")
- forbiddenIf("X")
Geoff
On Tue, 18 Sep 2018 at 17:05 Aled Sage <[email protected]> wrote:
Hi all,
I'd like to add support for more sophisticated config constraints, where
there are inter-dependencies between config keys. I'd like the blueprint
composer web-console to understand (a small number of) these, and thus
to give feedback to the user about what is required and whether their
blueprint is valid.
For example, a blueprint that requires exactly one of config X or config
Y. Another example: config X2 is required if and only if config X1 is
supplied.
There are a few questions / decision points:
1. What constraints should we support out-of-the-box?
2. What naming convention do we use, so that the UI can parse +
understand these?
3. Should we support multiple constraints (we do in the REST api, but
not currently in the Java API or in the Blueprint Composer UI)
---
I suggest we support things like:
constraints:
- requiredUnless("Y")
- forbiddenIf("Y")
and:
constraints:
- requiredIf("X1")
- forbiddenUnless("X1")
The structure of this string would be:
{required,forbidden}{If,Unless}("<config>")
requiredIf("X1"): value is required if config X1 is set;
otherwise optional.
forbiddenUnless("X1"): value must be null if config X1 is not set;
otherwise optional.
requiredUnless("Y"): value is required if config Y is not set;
otherwise optional.
forbiddenIf("Y"): value must be null if config Y is set;
otherwise optional.
I don't think we want to get too sophisticated. For example, do *not*
support "must match regex '[a-z]+' unless config Y is present". I don't
think we should create a full-blown DSL for this!
---
Implementation notes:
We already have the basis of this in our Java code. We support a
predicate of type
`org.apache.brooklyn.core.objs.BrooklynObjectPredicate`, which has the
additional method `boolean apply(T input, BrooklynObject context)`. The
`BrooklynObject` could be an entity, or location, etc. An implementation
of this predicate can therefore lookup other config key's values, when
validating the value.
For the UI, the Blueprint Composer calls:
http://localhost:8081/v1/catalog/bundles/
<bundle>/<version>/types/<blueprint>/latest
This returns things like:
"config": [
{
"name": "myPredicate",
"type": "java.lang.String",
"reconfigurable": false,
"label": "myPredicate",
"pinned": false,
"constraints": [
"MyPredicateToString()"
],
"links": {}
},
...
The constraint returned here is the toString() of the predicate.
In the UI [1], there is currently some very simple logic to interpret
this string for particular types of constraint.
Aled
[1]
brooklyn-ui/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js