blob: fe46aa8a6e14c391964d2f77fc625511ab507e45 [file] [log] [blame]
/*
* 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.rest.resources;
import io.swagger.annotations.ApiParam;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.BasicConfigKey;
import org.apache.brooklyn.core.config.Sanitizer;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.entity.internal.EntityConfigMap;
import org.apache.brooklyn.core.mgmt.BrooklynTags;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
import org.apache.brooklyn.rest.api.EntityConfigApi;
import org.apache.brooklyn.rest.domain.ConfigSummary;
import org.apache.brooklyn.rest.filter.HaHotStateRequired;
import org.apache.brooklyn.rest.transform.ConfigTransformer;
import org.apache.brooklyn.rest.util.WebResourceUtils;
import org.apache.brooklyn.util.core.flags.TypeCoercions;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@HaHotStateRequired
public class EntityConfigResource extends AbstractBrooklynRestResource implements EntityConfigApi {
private static final Logger LOG = LoggerFactory.getLogger(EntityConfigResource.class);
@Override
public List<ConfigSummary> list(final String application, final String entityToken) {
final Entity entity = brooklyn().getEntity(application, entityToken);
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to see entity '%s'",
Entitlements.getEntitlementContext().user(), entity);
}
// TODO merge with keys which have values:
// ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap();
List<ConfigSummary> result = Lists.newArrayList();
for (ConfigKey<?> key : entity.getEntityType().getConfigKeys()) {
// Exclude config that user is not allowed to see
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CONFIG, new EntityAndItem<String>(entity, key.getName()))) {
LOG.trace("User {} not authorized to see config {} of entity {}; excluding from ConfigKey list results",
new Object[] {Entitlements.getEntitlementContext().user(), key.getName(), entity});
continue;
}
result.add(ConfigTransformer.of(key).on(entity).includeLinks(ui.getBaseUriBuilder(), true, true).transform());
}
return result;
}
// TODO support parameters ?show=value,summary&name=xxx &format={string,json,xml}
// (and in sensors class)
@Override
public Map<String, Object> batchConfigRead(String application, String entityToken,
Boolean useDisplayHints, Boolean skipResolution, Boolean suppressSecrets,
Boolean raw) {
// TODO: add test
Entity entity = brooklyn().getEntity(application, entityToken);
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to see entity '%s'",
Entitlements.getEntitlementContext().user(), entity);
}
// wrap in a task for better runtime view
return Entities.submit(entity, Tasks.<Map<String,Object>>builder().displayName("REST API batch config read")
.tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
.body(new BatchConfigRead(mgmt(), this, entity, useDisplayHints, skipResolution, suppressSecrets, raw)).build()).getUnchecked();
}
private static class BatchConfigRead implements Callable<Map<String,Object>> {
private final ManagementContext mgmt;
private final EntityConfigResource resource;
private final Entity entity;
private final Boolean useDisplayHints, skipResolution, suppressSecrets, raw;
public BatchConfigRead(ManagementContext mgmt, EntityConfigResource resource, Entity entity,
Boolean useDisplayHints, Boolean skipResolution, Boolean suppressSecrets,
@Deprecated Boolean raw) {
this.mgmt = mgmt;
this.resource = resource;
this.entity = entity;
this.useDisplayHints = useDisplayHints;
this.skipResolution = skipResolution;
this.suppressSecrets = suppressSecrets;
this.raw = raw;
}
@Override
public Map<String, Object> call() throws Exception {
// TODO on API for this, and other config (location, policy, etc), support requesting local v inherited and resolved v raw
Map<ConfigKey<?>, Object> source = ( (EntityConfigMap)((EntityInternal) entity).config().getInternalConfigMap() ).getAllConfigInheritedRawValuesIgnoringErrors();
Map<String, Object> result = Maps.newLinkedHashMap();
for (Map.Entry<ConfigKey<?>, Object> ek : source.entrySet()) {
ConfigKey<?> key = ek.getKey();
Object value = ek.getValue();
// Exclude config that user is not allowed to see
if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_CONFIG, new EntityAndItem<String>(entity, ek.getKey().getName()))) {
LOG.trace("User {} not authorized to see sensor {} of entity {}; excluding from current-state results",
new Object[] {Entitlements.getEntitlementContext().user(), ek.getKey().getName(), entity});
continue;
}
result.put(key.getName(),
resource.resolving(value, mgmt).preferJson(true).asJerseyOutermostReturnValue(false)
.useDisplayHints(useDisplayHints)
.skipResolution(skipResolution)
.suppressIfSecret(key.getName(), suppressSecrets)
.raw(raw)
.context(entity)
.immediately(true) // 2022-08 added immediately instead of timeout zero because the latter caused some oddities
.timeout(Duration.millis(500)) // shouldn't take this long, but if it does, under load, just bail
.renderAs(key)
.resolve());
}
return result;
}
}
@Override
public Object get(String application, String entityToken, String configKeyName,
Boolean useDisplayHints, Boolean skipResolution, Boolean suppressSecrets,
@Deprecated Boolean raw) {
return get(true, application, entityToken, configKeyName,
useDisplayHints, skipResolution, suppressSecrets,
raw);
}
@Override
public String getPlain(String application, String entityToken, String configKeyName,
Boolean useDisplayHints, Boolean skipResolution, Boolean suppressSecrets,
@Deprecated Boolean raw) {
return (String) get(false, application, entityToken, configKeyName,
useDisplayHints, skipResolution, suppressSecrets,
raw, true);
}
public Object get(boolean preferJson, String application, String entityToken, String configKeyName,
Boolean useDisplayHints, Boolean skipResolution, Boolean suppressSecrets,
@Deprecated Boolean raw) {
return get(preferJson, application, entityToken, configKeyName, useDisplayHints, skipResolution, suppressSecrets, raw, false);
}
public Object get(boolean preferJson, String application, String entityToken, String configKeyName,
Boolean useDisplayHints, Boolean skipResolution, Boolean suppressSecrets,
@Deprecated Boolean raw, boolean plain) {
Entity entity = brooklyn().getEntity(application, entityToken);
ConfigKey<?> ck = findConfig(entity, configKeyName);
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to see entity '%s'",
Entitlements.getEntitlementContext().user(), entity);
}
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CONFIG, new EntityAndItem<String>(entity, ck.getName()))) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to see entity '%s' config '%s'",
Entitlements.getEntitlementContext().user(), entity, ck.getName());
}
Object value = ((EntityInternal)entity).config().getRaw(ck).orNull();
return resolving(value).preferJson(preferJson).asJerseyOutermostReturnValue(true)
.useDisplayHints(useDisplayHints)
.skipResolution(skipResolution)
.suppressIfSecret(ck.getName(), suppressSecrets)
.raw(raw)
.context(entity).immediately(true).renderAs(ck).resolve();
}
private ConfigKey<?> findConfig(Entity entity, String configKeyName) {
ConfigKey<?> ck = entity.getEntityType().getConfigKey(configKeyName);
if (ck == null)
ck = new BasicConfigKey<Object>(Object.class, configKeyName);
return ck;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void setFromMap(String application, String entityToken, Boolean recurse, Map newValues) {
final Entity entity = brooklyn().getEntity(application, entityToken);
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to modify entity '%s'",
Entitlements.getEntitlementContext().user(), entity);
}
if (LOG.isDebugEnabled())
LOG.debug("REST user " + Entitlements.getEntitlementContext() + " setting configs " + newValues);
for (Object entry : newValues.entrySet()) {
String configName = Strings.toString(((Map.Entry) entry).getKey());
Object newValue = ((Map.Entry) entry).getValue();
ConfigKey ck = findConfig(entity, configName);
((EntityInternal) entity).config().set(ck, TypeCoercions.coerce(newValue, ck.getTypeToken()));
if (Boolean.TRUE.equals(recurse)) {
for (Entity e2 : Entities.descendantsWithoutSelf(entity)) {
((EntityInternal) e2).config().set(ck, newValue);
}
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void set(String application, String entityToken, String configName, Boolean recurse, Object newValue) {
final Entity entity = brooklyn().getEntity(application, entityToken);
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_ENTITY, entity)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to modify entity '%s'",
Entitlements.getEntitlementContext().user(), entity);
}
ConfigKey ck = findConfig(entity, configName);
LOG.debug("REST setting config " + configName + " on " + entity + " to " + newValue);
((EntityInternal) entity).config().set(ck, TypeCoercions.coerce(newValue, ck.getTypeToken()));
if (Boolean.TRUE.equals(recurse)) {
for (Entity e2 : Entities.descendantsWithoutSelf(entity)) {
((EntityInternal) e2).config().set(ck, newValue);
}
}
}
}