Repository: brooklyn-server Updated Branches: refs/heads/master baad0c039 -> e040f074f
does a yaml parse and compares the hash of the result when checking plan equivalence means that the same plan entered twice but with different comments, spacing, quotations etc, will not result in an error. it only treats a plan as non-equivalent if their yaml is different after a deserialize-then-serialize cycle. previously a catalog push for an already-present non-snapshot plan would fail unless the plan was identical up to a trim. now it only fails if the objects are different. Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/066cbbf2 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/066cbbf2 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/066cbbf2 Branch: refs/heads/master Commit: 066cbbf2f70cb9b7e4b79a6eeb29bf87422dd2f6 Parents: 6f093c7 Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com> Authored: Sat Jun 2 00:29:02 2018 +0100 Committer: Duncan Grant <duncan.gr...@cloudsoftcorp.com> Committed: Mon Jun 4 12:11:40 2018 +0100 ---------------------------------------------------------------------- .../brooklyn/core/typereg/RegisteredTypes.java | 47 ++++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/066cbbf2/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java index 9a4a5dd..83e7f05 100644 --- a/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java +++ b/core/src/main/java/org/apache/brooklyn/core/typereg/RegisteredTypes.java @@ -50,6 +50,7 @@ import org.apache.brooklyn.core.mgmt.BrooklynTags; import org.apache.brooklyn.core.mgmt.BrooklynTags.NamedStringTag; import org.apache.brooklyn.core.objs.BrooklynObjectInternal; import org.apache.brooklyn.core.typereg.JavaClassNameTypePlanTransformer.JavaClassNameTypeImplementationPlan; +import org.apache.brooklyn.util.collections.Jsonya; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.guava.Maybe.Absent; @@ -682,27 +683,65 @@ public class RegisteredTypes { // it does mean a format change will be ignored return "equivalent-plan("+Streams.getMd5Checksum(Streams.newInputStreamWithContents(input.trim()))+")"; } - + + /** parse the plan as yaml/json, re-serialize it, and take that checksum. + * this allows plans that are equivalent post-parse to be treated as equivalent. + * returns {@link Absent} if the input is not valid yaml. + */ + private static Maybe<String> tagForEquivalentYamlPlan(String input) { + // plans may be trimmed by yaml parser so do that before checking equivalence + // it does mean a format change will be ignored + try { + Iterator<Object> plansI = Yamls.parseAll(input).iterator(); + if (!plansI.hasNext()) { + return Maybe.absent("No data found"); + } + String planOut = ""; + while (plansI.hasNext()) { + Object plan = plansI.next(); + if (!planOut.isEmpty()) planOut += "\n"; + planOut += Jsonya.render(plan); + } + return Maybe.of(tagForEquivalentPlan(planOut)); + } catch (Exception e) { + return Maybe.absent(e); + } + } + @Beta public static void notePlanEquivalentToThis(RegisteredType type, TypeImplementationPlan plan) { Object data = plan.getPlanData(); if (data==null) throw new IllegalStateException("No plan data for "+plan+" noted equivalent to "+type); if (!(data instanceof String)) throw new IllegalStateException("Expected plan for equivalent to "+type+" to be a string; was "+data); ((BasicRegisteredType)type).tags.add(tagForEquivalentPlan((String)data)); + Maybe<String> reserializeEquivalenceTag = tagForEquivalentYamlPlan((String)data); + if (reserializeEquivalenceTag.isPresent()) ((BasicRegisteredType)type).tags.add(reserializeEquivalenceTag.get()); } + /** Checks whether two types have plans which are identical, or identical after a YAML parse, + * or if either has an "equivalent-plan" tag indicating its equivalence to the other plan + * (as set by {@link #notePlanEquivalentToThis(RegisteredType, TypeImplementationPlan)}). + */ @Beta public static boolean arePlansEquivalent(RegisteredType type1, RegisteredType type2) { String plan1 = getImplementationDataStringForSpec(type1); String plan2 = getImplementationDataStringForSpec(type2); if (Strings.isNonBlank(plan1) && Strings.isNonBlank(plan2)) { - String p2tag = tagForEquivalentPlan(plan2); String p1tag = tagForEquivalentPlan(plan1); - // allow same plan under trimming, - // or any recorded tag in either direction + String p2tag = tagForEquivalentPlan(plan2); + if (Objects.equal(p1tag, p2tag)) return true; + if (type1.getTags().contains(p2tag)) return true; if (type2.getTags().contains(p1tag)) return true; + + Maybe<String> rp2tag = tagForEquivalentYamlPlan(plan2); + if (rp2tag.isPresent() && type1.getTags().contains(rp2tag.get())) return true; + + Maybe<String> rp1tag = tagForEquivalentYamlPlan(plan1); + if (rp1tag.isPresent() && type2.getTags().contains(rp1tag.get())) return true; + + if (rp1tag.isPresent() && rp2tag.isPresent() && rp1tag.get().equals(rp2tag.get())) return true; } return Objects.equal(type1.getPlan(), type2.getPlan()); }