You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by tb...@apache.org on 2021/07/14 12:41:40 UTC
[brooklyn-server] 01/01: Add support setup default initializers for
all deployment
This is an automated email from the ASF dual-hosted git repository.
tbouron pushed a commit to branch feature/default-initializers
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git
commit 856f033034f5f39006844c599c46a3cc1b6f0406
Author: Thomas Bouron <th...@cloudsoftcorp.com>
AuthorDate: Wed Jul 14 13:41:19 2021 +0100
Add support setup default initializers for all deployment
This looks up a new configuration options called `brooklyn.deployment.initializers` (comma separated list). If specified on a Brooklyn instance, all deployments will load and execute these initializers. Theses classes are expected to be `EntityInitializer`, if an error occur (either cast or anything else) then the deployment will fail.
The code will try to:
1. load the class from the default class loader.
2. if (1) fails, it will try to load the class from the `TypeRegistry`. This is to allow execution of custom initializers that might be installed in the catalog later on.
3. if (1) and (2) fails, then the deployment is aborted.
---
.../core/effector/AddDeploySensorsInitializer.java | 54 ++++++++++++
.../core/objs/proxy/InternalEntityFactory.java | 97 +++++++++++++++-------
2 files changed, 121 insertions(+), 30 deletions(-)
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/AddDeploySensorsInitializer.java b/core/src/main/java/org/apache/brooklyn/core/effector/AddDeploySensorsInitializer.java
new file mode 100644
index 0000000..0285cd0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/AddDeploySensorsInitializer.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.effector;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
+import org.apache.brooklyn.api.entity.EntityInitializer;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+import org.apache.brooklyn.core.sensor.Sensors;
+
+public class AddDeploySensorsInitializer implements EntityInitializer {
+ @Override
+ public void apply(EntityLocal entity) {
+ // We want to set the metadata only on the root node of an application
+ if (entity.getParent() != null) {
+ return;
+ }
+ EntitlementContext entitlementContext = Entitlements.getEntitlementContext();
+ AttributeSensor<String> sensor = Sensors.newSensor(
+ String.class,
+ "deployment.metadata",
+ "Metadata information about this particular deployment. Contains at least who triggered it and when.");
+ ((EntityInternal) entity).getMutableEntityType().addSensor(sensor);
+ try {
+ entity.sensors().set(sensor, new ObjectMapper().writeValueAsString(ImmutableMap.of(
+ "user", entitlementContext != null ? entitlementContext.user() : "Unknown",
+ "deploy_time", System.currentTimeMillis()
+ )));
+ } catch (JsonProcessingException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
index cf8821b..7b92138 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
@@ -31,8 +31,10 @@ import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.objs.SpecParameter;
import org.apache.brooklyn.api.policy.PolicySpec;
import org.apache.brooklyn.api.sensor.EnricherSpec;
+import org.apache.brooklyn.api.typereg.RegisteredType;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigConstraints;
+import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.config.ConstraintViolationException;
import org.apache.brooklyn.core.entity.*;
import org.apache.brooklyn.core.mgmt.BrooklynTags;
@@ -46,46 +48,59 @@ import org.apache.brooklyn.util.collections.MutableSet;
import org.apache.brooklyn.util.core.flags.FlagUtils;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.javalang.AggregateClassLoader;
import org.apache.brooklyn.util.javalang.Reflections;
import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nonnull;
import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
- * Creates entities (and proxies) of required types, given the
- *
+ * Creates entities (and proxies) of required types, given the
+ *
* This is an internal class for use by core-brooklyn. End-users are strongly discouraged from
* using this class directly.
- *
+ *
* Used in three situations:
* <ul>
* <li>Normal entity creation (through entityManager.createEntity)
* <li>rebind (i.e. Brooklyn restart, or promotion of HA standby manager node)
* <li>yaml parsing
* </ul>
- *
+ *
* @author aled
*/
public class InternalEntityFactory extends InternalFactory {
private static final Logger log = LoggerFactory.getLogger(InternalEntityFactory.class);
-
+
private final EntityTypeRegistry entityTypeRegistry;
private final InternalPolicyFactory policyFactory;
private final ClassLoaderCache classLoaderCache;
-
+
+ /**
+ * The initializers to be add to any application deployed by Brooklyn.
+ * e.g. <code>brooklyn.deployment.initializers=org.apache.brooklyn.core.effector.AddDeploySensorsInitializer</code>
+ * will automatically add tags to the root node of any deployed application.
+ */
+ public final static ConfigKey<String> DEFAULT_INITIALIZERS_CLASSNAMES = ConfigKeys.newStringConfigKey(
+ "brooklyn.deployment.initializers",
+ "Comma separated list of class names corresponding to Brooklyn Initializers to be automatically added and ran on every application deployed",
+ "");
+
public InternalEntityFactory(ManagementContextInternal managementContext, EntityTypeRegistry entityTypeRegistry, InternalPolicyFactory policyFactory) {
super(managementContext);
this.entityTypeRegistry = checkNotNull(entityTypeRegistry, "entityTypeRegistry");
@@ -103,7 +118,7 @@ public class InternalEntityFactory extends InternalFactory {
interfaces.addAll(Reflections.getAllInterfaces(spec.getType()));
}
interfaces.addAll(spec.getAdditionalInterfaces());
-
+
return createEntityProxy(interfaces, entity);
}
@@ -151,7 +166,7 @@ public class InternalEntityFactory extends InternalFactory {
/** creates a new entity instance from a spec, with all children, policies, etc,
* fully initialized ({@link AbstractEntity#init()} invoked) and ready for
- * management -- commonly the caller will next call
+ * management -- commonly the caller will next call
* {@link Entities#manage(Entity)} (if it's in a managed application)
* or {@link Entities#startManagement(org.apache.brooklyn.api.entity.Application, org.apache.brooklyn.api.mgmt.ManagementContext)}
* (if it's an application) */
@@ -162,7 +177,7 @@ public class InternalEntityFactory extends InternalFactory {
* It seems we need access to the parent (indeed the root application) when running some initializers (esp children initializers).
* <p>
* Now we do two passes, so that hierarchy is fully populated before initialization and policies.
- * (This is needed where some config or initializer might reference another entity by its ID, e.g. yaml $brooklyn:component("id").
+ * (This is needed where some config or initializer might reference another entity by its ID, e.g. yaml $brooklyn:component("id").
* Initialization is done in parent-first order with depth-first children traversal.
*/
@@ -171,7 +186,7 @@ public class InternalEntityFactory extends InternalFactory {
// (maps needed because we need the spec, and we need to keep the AbstractEntity to call init, not a proxy)
Map<String,Entity> entitiesByEntityId = MutableMap.of();
Map<String,EntitySpec<?>> specsByEntityId = MutableMap.of();
-
+
T entity = createEntityAndDescendantsUninitialized(0, spec, options, entitiesByEntityId, specsByEntityId);
try {
initEntityAndDescendants(entity.getId(), entitiesByEntityId, specsByEntityId, options);
@@ -190,7 +205,7 @@ public class InternalEntityFactory extends InternalFactory {
}
return entity;
}
-
+
private <T extends Entity> T createEntityAndDescendantsUninitialized(int depth, EntitySpec<T> spec, EntityManager.EntityCreationOptions options,
Map<String,Entity> entitiesByEntityId, Map<String,EntitySpec<?>> specsByEntityId) {
@@ -205,11 +220,11 @@ public class InternalEntityFactory extends InternalFactory {
}
Class<? extends T> clazz = getImplementedBy(spec);
-
+
entity = constructEntity(clazz, spec, depth==0 ? options.getRequiredUniqueId() : null);
-
+
loadUnitializedEntity(entity, spec, options);
-
+
List<NamedStringTag> upgradedFrom = BrooklynTags.findAllNamedStringTags(BrooklynTags.UPGRADED_FROM, spec.getTags());
if (!upgradedFrom.isEmpty()) {
log.warn("Entity "+entity.getId()+" created with upgraded type "+entity.getCatalogItemId()+" "+upgradedFrom+" (in "+entity.getApplicationId()+", under "+entity.getParent()+")");
@@ -230,7 +245,7 @@ public class InternalEntityFactory extends InternalFactory {
Entity child = createEntityAndDescendantsUninitialized(depth+1, childSpec, options, entitiesByEntityId, specsByEntityId);
entity.addChild(child);
}
-
+
for (Entity member: spec.getMembers()) {
if (!(entity instanceof Group)) {
throw new IllegalStateException("Entity "+entity+" must be a group to add members "+spec.getMembers());
@@ -243,13 +258,13 @@ public class InternalEntityFactory extends InternalFactory {
}
return entity;
-
+
} catch (Exception ex) {
options.onException(ex, Exceptions::propagate);
return entity;
}
}
-
+
@SuppressWarnings({ "unchecked", "rawtypes" })
protected <T extends Entity> T loadUnitializedEntity(final T entity, final EntitySpec<T> spec, EntityManager.EntityCreationOptions options) {
try {
@@ -321,11 +336,11 @@ public class InternalEntityFactory extends InternalFactory {
queue.addAll(e1.getChildren());
}
}
-
+
protected <T extends Entity> void initEntityAndDescendants(String entityId, final Map<String,Entity> entitiesByEntityId, final Map<String,EntitySpec<?>> specsByEntityId, EntityManager.EntityCreationOptions options) {
final Entity entity = entitiesByEntityId.get(entityId);
final EntitySpec<?> spec = specsByEntityId.get(entityId);
-
+
if (entity==null || spec==null) {
log.debug("Skipping initialization of "+entityId+" found as child of entity being initialized, "
+ "but this child is not one we created; likely it was created by an initializer, "
@@ -338,17 +353,17 @@ public class InternalEntityFactory extends InternalFactory {
validateDescendantConfig(entity, options);
/* Marked transient so that the task is not needlessly kept around at the highest level.
- * Note that the task is not normally visible in the GUI, because
+ * Note that the task is not normally visible in the GUI, because
* (a) while it is running, the entity is often parentless (and so not in the tree);
* and (b) when it is completed it is GC'd, as it is transient.
* However task info is available via the API if you know its ID,
- * and if better subtask querying is available it will be picked up as a background task
+ * and if better subtask querying is available it will be picked up as a background task
* of the parent entity creating this child entity
* (note however such subtasks are currently filtered based on parent entity so is excluded).
* <p>
* Some of these (initializers and enrichers) submit background scheduled tasks,
* which currently show up at the top level once the initializer task completes.
- * TODO It would be nice if these schedule tasks were grouped in a bucket!
+ * TODO It would be nice if these schedule tasks were grouped in a bucket!
*/
((EntityInternal)entity).getExecutionContext().get(Tasks.builder().dynamic(false).displayName("Entity initialization")
.tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
@@ -369,6 +384,12 @@ public class InternalEntityFactory extends InternalFactory {
}
((AbstractEntity)entity).addLocations(spec.getLocations());
+ List<EntityInitializer> initializers = Stream.concat(spec.getInitializers().stream(), getDefaultInitializers().stream())
+ .collect(Collectors.toList());
+ for (EntityInitializer initializer: initializers) {
+ initializer.apply((EntityInternal)entity);
+ }
+
for (EntityInitializer initializer: spec.getInitializers()) {
initializer.apply((EntityInternal)entity);
}
@@ -393,16 +414,16 @@ public class InternalEntityFactory extends InternalFactory {
}
}).build());
}
-
+
/**
* Constructs an entity, i.e. instantiate the actual class given a spec,
* and sets the entity's proxy. Used by this factory to {@link #createEntity(EntitySpec, org.apache.brooklyn.api.mgmt.EntityManager.EntityCreationOptions)}
* and also used during rebind.
- * <p>
+ * <p>
* If the entityId is provided, then uses that to override the entity's id,
* but that behaviour is deprecated.
* <p>
- * The new-style no-arg constructor is preferred, and
+ * The new-style no-arg constructor is preferred, and
* configuration from the {@link EntitySpec} is <b>not</b> normally applied,
* although for old-style entities flags from the spec are passed to the constructor.
* <p>
@@ -426,7 +447,7 @@ public class InternalEntityFactory extends InternalFactory {
}
checkNotNull(entityId, "entityId");
checkState(interfaces != null && !Iterables.isEmpty(interfaces), "must have at least one interface for entity %s:%s", clazz, entityId);
-
+
T entity = constructEntityImpl(clazz, null, null, entityId);
if (((AbstractEntity)entity).getProxy() == null) {
Entity proxy = managementContext.getEntityManager().getEntity(entity.getId());
@@ -442,10 +463,10 @@ public class InternalEntityFactory extends InternalFactory {
return entity;
}
- private <T extends Entity> T constructEntityImpl(Class<? extends T> clazz, EntitySpec<?> optionalSpec,
+ private <T extends Entity> T constructEntityImpl(Class<? extends T> clazz, EntitySpec<?> optionalSpec,
Map<String, ?> optionalConstructorFlags, String entityId) {
T entity = construct(clazz, optionalSpec, optionalConstructorFlags);
-
+
if (entityId!=null) {
FlagUtils.setFieldsFromFlags(ImmutableMap.of("id", entityId), entity);
}
@@ -465,7 +486,7 @@ public class InternalEntityFactory extends InternalFactory {
}
return super.constructOldStyle(clazz, flags);
}
-
+
private <T extends Entity> Class<? extends T> getImplementedBy(EntitySpec<T> spec) {
if (spec.getImplementation() != null) {
return spec.getImplementation();
@@ -473,4 +494,20 @@ public class InternalEntityFactory extends InternalFactory {
return entityTypeRegistry.getImplementedBy(spec.getType());
}
}
+
+ private List<EntityInitializer> getDefaultInitializers() {
+ return Arrays.stream(managementContext.getConfig().getConfig(DEFAULT_INITIALIZERS_CLASSNAMES).split(","))
+ .map(clazz -> {
+ try {
+ Class<?> initializerClass = getClass().getClassLoader().loadClass(clazz);
+ return (EntityInitializer) initializerClass.newInstance();
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ RegisteredType registeredType = managementContext.getTypeRegistry()
+ .getMaybe(clazz, null)
+ .orThrow("Failed to load default initializer with class name: " + clazz);
+ return managementContext.getTypeRegistry().create(registeredType, null, null);
+ }
+ })
+ .collect(Collectors.toList());
+ }
}