Skip to content

Commit

Permalink
[descriptor] add includeIf support in patches
Browse files Browse the repository at this point in the history
  • Loading branch information
rmannibucau committed Jun 15, 2023
1 parent 6dd5afd commit 0d60ae3
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,43 @@ private String toPatches(final Manifest.Alveolus alveolus, final AlveolusHandler
}
final var patches = alveolus.getPatches().stream()
.filter(p -> Objects.equals(p.getDescriptorName(), desc.getConfiguration().getName()))
.map(it -> " . Patch " + it.getPatch())
.map(it -> " . Patch " + (it.getIncludeIf() != null ? conditions(it.getIncludeIf()) : "") + it.getPatch())
.collect(joining("\n", "", ""))
.trim();
return patches.isBlank() ? "" : ("\n" + patches + "\n");
}

private String conditions(final Manifest.Conditions includeIf) {
final var conditions = includeIf.getConditions();
if (conditions == null || conditions.isEmpty()) {
return "";
}
if (conditions.size() == 1) {
final var cond = conditions.get(0);
return '(' + condition(cond) + ')';
}
return conditions.stream()
.map(this::condition)
.collect(joining(
" " + (includeIf.getOperator() == Manifest.ConditionOperator.ANY ? "OR" : "AND") + " ",
" (", ")"));
}

private String condition(final Manifest.Condition cond) {
final String type;
switch (cond.getType()) {
case ENV:
type = "env. var.";
break;
case SYSTEM_PROPERTY:
type = "system prop.";
break;
default:
type = "";
}
return type + " '" + cond.getKey() + "' has " + (cond.isNegate() ? "not " : "") + "value '" + cond.getValue() + "'";
}

private String toPlacheolders(final Manifest.Alveolus alveolus) {
if (alveolus.getPlaceholders() == null || alveolus.getPlaceholders().isEmpty()) {
return "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ public boolean hasInterpolateValue() {

@Data
public static class Patch {
@Description("Conditions to include this patch. " +
"Enables for example to have an environment variable enabling part of the stack (ex: `MONITORING=true`)")
private Conditions includeIf;

@Description("The descriptor to patch. It can be any descriptor, including transitive ones. " +
"It can be `*` to patch all descriptors (`/metadata/label/app` for example) or " +
"`regex:<java pattern>` to match descriptor names with a regex.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,28 +523,36 @@ private LoadedDescriptor doPrepare(final LoadedDescriptor desc, final Map<Predic
if (patch.isInterpolate()) {
content = substitutor.replace(content);
}
if (patch.getPatch() != null) {
final JsonArray array;
if (patch.isInterpolate()) { // interpolate patch too, if not desired the patch can be split in 2
array = jsonb.fromJson(substitutor.replace(patch.getPatch().toString()), JsonArray.class);
} else {
array = patch.getPatch();
}
final var jsonPatch = jsonProvider.createPatch(array);
try {
final var structure = loadJsonStructure(desc, content);
content = jsonPatch.apply(structure).toString();
} catch (final JsonException je) {
if (!desc.getConfiguration().getInterpolate()) {
throw new IllegalStateException("Can't patch '" + desc.getConfiguration().getName() + "': " + je.getMessage(), je);
}
log.finest(() -> "" +
"Trying to interpolate the descriptor before patching it since it failed without: '" +
desc.getConfiguration().getName() + "'");
content = substitutor.replace(content);
alreadyInterpolated = true;
content = jsonPatch.apply(loadJsonStructure(desc, content)).toString();
if (patch.getPatch() == null) {
continue;
}
if (patch.getIncludeIf() != null &&
patch.getIncludeIf().getConditions() != null &&
!patch.getIncludeIf().getConditions().isEmpty() &&
!conditionEvaluator.test(patch.getIncludeIf())) {
continue;
}

final JsonArray array;
if (patch.isInterpolate()) { // interpolate patch too, if not desired the patch can be split in 2
array = jsonb.fromJson(substitutor.replace(patch.getPatch().toString()), JsonArray.class);
} else {
array = patch.getPatch();
}
final var jsonPatch = jsonProvider.createPatch(array);
try {
final var structure = loadJsonStructure(desc, content);
content = jsonPatch.apply(structure).toString();
} catch (final JsonException je) {
if (!desc.getConfiguration().getInterpolate()) {
throw new IllegalStateException("Can't patch '" + desc.getConfiguration().getName() + "': " + je.getMessage(), je);
}
log.finest(() -> "" +
"Trying to interpolate the descriptor before patching it since it failed without: '" +
desc.getConfiguration().getName() + "'");
content = substitutor.replace(content);
alreadyInterpolated = true;
content = jsonPatch.apply(loadJsonStructure(desc, content)).toString();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,43 @@ class ApplyCommandTest {
@HttpApiInject
private HttpApiHandler<?> handler;

@Test
void includeIfPatch(final CommandExecutor executor, final TestInfo info) {
final var spyingResponseLocator = newSpyingHandler(info);
handler.setResponseLocator(spyingResponseLocator);

// apply it
System.setProperty("ApplyCommandTest.includeIfPatch", "true");
try {
{
executor.wrap(handler, INFO, () -> new BundleBee().launch(
"apply", "--alveolus", "ApplyCommandTest.includeIfPatch",
"--injectBundleBeeMetadata", "false", "--injectTimestamp", "false"));
assertEquals(
Set.of("{\"apiVersion\":\"v1\",\"kind\":\"Service\"," +
"\"metadata\":{\"name\":\"s\",\"labels\":{\"app\":\"s-test\",\"patched\":\"true\"}}," +
"\"spec\":{\"type\":\"NodePort\",\"ports\":[{\"port\":1234,\"targetPort\":1234}],\"selector\":{\"app\":\"s-test\"}}}"),
spyingResponseLocator.requests.stream().map(Request::payload).collect(toSet()));
}

// skip it
spyingResponseLocator.requests.clear();
System.setProperty("ApplyCommandTest.includeIfPatch", "false");
{
executor.wrap(handler, INFO, () -> new BundleBee().launch(
"apply", "--alveolus", "ApplyCommandTest.includeIfPatch",
"--injectBundleBeeMetadata", "false", "--injectTimestamp", "false"));
assertEquals(
Set.of("{\"apiVersion\":\"v1\",\"kind\":\"Service\"," +
"\"metadata\":{\"name\":\"s\",\"labels\":{\"app\":\"s-test\"}}," +
"\"spec\":{\"type\":\"NodePort\",\"ports\":[{\"port\":1234,\"targetPort\":1234}],\"selector\":{\"app\":\"s-test\"}}}"),
spyingResponseLocator.requests.stream().map(Request::payload).collect(toSet()));
}
} finally {
System.clearProperty("ApplyCommandTest.includeIfPatch");
}
}

@Test
void fromTemplate(final CommandExecutor executor, final TestInfo info) {
final var spyingResponseLocator = newSpyingHandler(info);
Expand Down
29 changes: 29 additions & 0 deletions bundlebee-core/src/test/resources/bundlebee/manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
{
"alveoli": [
{
"name": "ApplyCommandTest.includeIfPatch",
"descriptors": [
{
"name": "ApplyCommandTest.d1"
}
],
"patches": [
{
"descriptorName": "ApplyCommandTest.d1",
"includeIf": {
"conditions": [
{
"type": "SYSTEM_PROPERTY",
"key": "ApplyCommandTest.includeIfPatch",
"value": "true"
}
]
},
"patch": [
{
"op": "add",
"path": "/metadata/labels/patched",
"value": "true"
}
]
}
]
},
{
"name": "ApplyCommandTest.template",
"descriptors": [
Expand Down

0 comments on commit 0d60ae3

Please sign in to comment.