/** * KLUDGE - Until ACL is mandated entity passed should be decorated for equals check to pass. * existingEntity in config store will have teh decoration and equals check fails if entity passed * is not decorated for checking if entity already exists. * * @param entity entity */ private void decorateEntityWithACL(Entity entity) { if (SecurityUtil.isAuthorizationEnabled() || entity.getACL() != null) { return; // not necessary to decorate } final String proxyUser = CurrentUser.getUser(); final String defaultGroupName = CurrentUser.getPrimaryGroupName(); switch (entity.getEntityType()) { case CLUSTER: org.apache.falcon.entity.v0.cluster.ACL clusterACL = new org.apache.falcon.entity.v0.cluster.ACL(); clusterACL.setOwner(proxyUser); clusterACL.setGroup(defaultGroupName); ((org.apache.falcon.entity.v0.cluster.Cluster) entity).setACL(clusterACL); break; case FEED: org.apache.falcon.entity.v0.feed.ACL feedACL = new org.apache.falcon.entity.v0.feed.ACL(); feedACL.setOwner(proxyUser); feedACL.setGroup(defaultGroupName); ((org.apache.falcon.entity.v0.feed.Feed) entity).setACL(feedACL); break; case PROCESS: org.apache.falcon.entity.v0.process.ACL processACL = new org.apache.falcon.entity.v0.process.ACL(); processACL.setOwner(proxyUser); processACL.setGroup(defaultGroupName); ((org.apache.falcon.entity.v0.process.Process) entity).setACL(processACL); break; default: break; } }
private boolean isEntityFiltered( Entity entity, EntityList.EntityFilterByFields filter, Map.Entry<String, List<String>> pair) { switch (filter) { case TYPE: return !containsIgnoreCase(pair.getValue(), entity.getEntityType().toString()); case NAME: return !containsIgnoreCase(pair.getValue(), entity.getName()); case STATUS: return !containsIgnoreCase(pair.getValue(), getStatusString(entity)); case PIPELINES: if (!entity.getEntityType().equals(EntityType.PROCESS)) { throw FalconWebException.newException( "Invalid filterBy key for non process entities " + pair.getKey(), Response.Status.BAD_REQUEST); } return !EntityUtil.getPipelines(entity).contains(pair.getValue().get(0)); case CLUSTER: return !EntityUtil.getClustersDefined(entity).contains(pair.getValue().get(0)); case TAGS: return isFilteredByTags(getFilterByTags(pair.getValue()), EntityUtil.getTags(entity)); default: return false; } }
private List<Entity> obtainUpdateEntityLocks(Entity entity) throws FalconException { List<Entity> tokenList = new ArrayList<Entity>(); // first obtain lock for the entity for which update is issued. if (memoryLocks.acquireLock(entity)) { tokenList.add(entity); } else { throw new FalconException( "Looks like an update command is already issued for " + entity.toShortString()); } // now obtain locks for all dependent entities. Set<Entity> affectedEntities = EntityGraph.get().getDependents(entity); for (Entity e : affectedEntities) { if (memoryLocks.acquireLock(e)) { tokenList.add(e); } else { LOG.error( "Error while trying to acquire lock for {}. Releasing already obtained locks", e.toShortString()); throw new FalconException( "There are multiple update commands running for dependent entity " + e.toShortString()); } } return tokenList; }
protected synchronized Entity submitInternal(HttpServletRequest request, String type) throws IOException, FalconException { EntityType entityType = EntityType.getEnum(type); Entity entity = deserializeEntity(request, entityType); // KLUDGE - Until ACL is mandated entity passed should be decorated for equals check to pass decorateEntityWithACL(entity); Entity existingEntity = configStore.get(entityType, entity.getName()); if (existingEntity != null) { if (EntityUtil.equals(existingEntity, entity)) { return existingEntity; } throw new EntityAlreadyExistsException( entity.toShortString() + " already registered with configuration store. " + "Can't be submitted again. Try removing before submitting."); } SecurityUtil.tryProxy(entity); // proxy before validating since FS/Oozie needs to be proxied validate(entity); configStore.publish(entityType, entity); LOG.info("Submit successful: ({}): {}", type, entity.getName()); return entity; }
/** * Post an entity XML with entity type. Validates the XML which can be Process, Feed or * Dataendpoint * * @param type entity type * @return APIResule -Succeeded or Failed */ public APIResult validate(HttpServletRequest request, String type) { try { EntityType entityType = EntityType.getEnum(type); Entity entity = deserializeEntity(request, entityType); validate(entity); // Validate that the entity can be scheduled in the cluster if (entity.getEntityType().isSchedulable()) { Set<String> clusters = EntityUtil.getClustersDefinedInColos(entity); for (String cluster : clusters) { try { getWorkflowEngine().dryRun(entity, cluster); } catch (FalconException e) { throw new FalconException("dryRun failed on cluster " + cluster, e); } } } return new APIResult( APIResult.Status.SUCCEEDED, "Validated successfully (" + entityType + ") " + entity.getName()); } catch (Throwable e) { LOG.error("Validation failed for entity ({})", type, e); throw FalconWebException.newException(e, Response.Status.BAD_REQUEST); } }
@Override public String delete(Entity entity) throws FalconException { if (isActive(entity)) { EXECUTION_SERVICE.delete(entity); } // This should remove it from state store too as state store listens to config store changes. CONFIG_STORE.remove(entity.getEntityType(), entity.getName()); return "SUCCESS"; }
/** * Submit a new entity. Entities can be of type feed, process or data end points. Entity * definitions are validated structurally against schema and subsequently for other rules before * they are admitted into the system * * <p>Entity name acts as the key and an entity once added, can't be added again unless deleted. * * @param request - Servlet Request * @param type - entity type - feed, process or data end point * @param colo - applicable colo * @return result of the operation */ public APIResult submit(HttpServletRequest request, String type, String colo) { checkColo(colo); try { Entity entity = submitInternal(request, type); return new APIResult( APIResult.Status.SUCCEEDED, "Submit successful (" + type + ") " + entity.getName()); } catch (Throwable e) { LOG.error("Unable to persist entity object", e); throw FalconWebException.newException(e, Response.Status.BAD_REQUEST); } }
public static OozieEntityBuilder get(Entity entity) { switch (entity.getEntityType()) { case FEED: return new FeedBundleBuilder((Feed) entity); case PROCESS: return new ProcessBundleBuilder((Process) entity); default: } throw new IllegalArgumentException("Unhandled type: " + entity.getEntityType()); }
private void validateUpdate(Entity oldEntity, Entity newEntity) throws FalconException { if (oldEntity.getEntityType() != newEntity.getEntityType() || !oldEntity.equals(newEntity)) { throw new FalconException( oldEntity.toShortString() + " can't be updated with " + newEntity.toShortString()); } if (oldEntity.getEntityType() == EntityType.CLUSTER) { throw new FalconException("Update not supported for clusters"); } String[] props = oldEntity.getEntityType().getImmutableProperties(); for (String prop : props) { Object oldProp, newProp; try { oldProp = PropertyUtils.getProperty(oldEntity, prop); newProp = PropertyUtils.getProperty(newEntity, prop); } catch (Exception e) { throw new FalconException(e); } if (!ObjectUtils.equals(oldProp, newProp)) { throw new ValidationException( oldEntity.toShortString() + ": " + prop + " can't be changed"); } } }
/** * Returns the entity definition as an XML based on name. * * @param type entity type * @param entityName entity name * @return String */ public String getEntityDefinition(String type, String entityName) { try { EntityType entityType = EntityType.getEnum(type); Entity entity = configStore.get(entityType, entityName); if (entity == null) { throw new NoSuchElementException(entityName + " (" + type + ") not found"); } return entity.toString(); } catch (Throwable e) { LOG.error( "Unable to get entity definition from config store for ({}): {}", type, entityName, e); throw FalconWebException.newException(e, Response.Status.BAD_REQUEST); } }
@Override public String touch(Entity entity, String cluster, Boolean skipDryRun) throws FalconException { EntityID id = new EntityID(entity); // Ideally state store should have all entities, but, check anyway. if (STATE_STORE.entityExists(id)) { Date endTime = EntityUtil.getEndTime(entity, cluster); if (endTime.before(DateUtil.now())) { throw new FalconException( "Entity's end time " + SchemaHelper.formatDateUTC(endTime) + " is before current time. Entity can't be touch-ed as it has completed."); } Collection<InstanceState> instances = STATE_STORE.getExecutionInstances(entity, cluster, InstanceState.getRunningStates()); // touch should happen irrespective of the state the entity is in. DAGEngineFactory.getDAGEngine(cluster) .touch(entity, (skipDryRun == null) ? Boolean.FALSE : skipDryRun); StringBuilder builder = new StringBuilder(); builder .append(entity.toShortString()) .append("/Effective Time: ") .append(getEffectiveTime(entity, cluster, instances)); return builder.toString(); } throw new FalconException("Could not find entity " + id + " in state store."); }
private void canRemove(Entity entity) throws FalconException { Pair<String, EntityType>[] referencedBy = EntityIntegrityChecker.referencedBy(entity); if (referencedBy != null && referencedBy.length > 0) { StringBuilder messages = new StringBuilder(); for (Pair<String, EntityType> ref : referencedBy) { messages.append(ref).append("\n"); } throw new FalconException( entity.getName() + "(" + entity.getEntityType() + ") cant " + "be removed as it is referred by " + messages); } }
@Override public void onRemove(Entity entity) throws FalconException { if (entity.getEntityType().equals(EntityType.FEED)) { Feed feed = (Feed) entity; if (StringUtils.isEmpty(feed.getGroups())) { return; } String[] groups = feed.getGroups().split(","); for (String group : groups) { groupsMapping.get(group).getFeeds().remove(entity.getName()); if (groupsMapping.get(group).getFeeds().size() == 0) { groupsMapping.remove(group); } } } }
@Override public String update(Entity oldEntity, Entity newEntity, String cluster, Boolean skipDryRun) throws FalconException { org.apache.falcon.entity.v0.cluster.Cluster clusterEntity = ConfigurationStore.get().get(EntityType.CLUSTER, cluster); boolean entityUpdated = UpdateHelper.isEntityUpdated( oldEntity, newEntity, cluster, EntityUtil.getLatestStagingPath(clusterEntity, oldEntity)); StringBuilder result = new StringBuilder(); if (!entityUpdated) { // Ideally should throw an exception, but, keeping it backward-compatible. LOG.warn( "No relevant updates detected in the new entity definition for entity {}!", newEntity.getName()); return result.toString(); } Date oldEndTime = EntityUtil.getEndTime(oldEntity, cluster); Date newEndTime = EntityUtil.getEndTime(newEntity, cluster); if (newEndTime.before(DateUtil.now()) || newEndTime.before(oldEndTime)) { throw new FalconException( "New Entity's end time " + SchemaHelper.formatDateUTC(newEndTime) + " is before current time or before old end time. Entity can't be updated."); } // The steps required are the same as touch. DAGEngineFactory.getDAGEngine(cluster) .touch(newEntity, (skipDryRun == null) ? Boolean.FALSE : skipDryRun); // Additionally, update the executor. // The update will kick in for new instances created and not for READY/WAITING instances, as // with Oozie. Collection<InstanceState> instances = new ArrayList<>(); instances.add(STATE_STORE.getLastExecutionInstance(oldEntity, cluster)); EXECUTION_SERVICE.getEntityExecutor(oldEntity, cluster).update(newEntity); result .append(newEntity.toShortString()) .append("/Effective Time: ") .append(getEffectiveTime(newEntity, cluster, instances)); return result.toString(); }
private EntityElement getEntityElement(Entity entity, HashSet<String> fields) { EntityElement elem = new EntityElement(); elem.type = entity.getEntityType().toString(); elem.name = entity.getName(); if (fields.contains(EntityList.EntityFieldList.STATUS.name())) { elem.status = getStatusString(entity); } if (fields.contains(EntityList.EntityFieldList.PIPELINES.name())) { elem.pipeline = EntityUtil.getPipelines(entity); } if (fields.contains(EntityList.EntityFieldList.TAGS.name())) { elem.tag = EntityUtil.getTags(entity); } if (fields.contains(EntityList.EntityFieldList.CLUSTERS.name())) { elem.cluster = new ArrayList<String>(EntityUtil.getClustersDefined(entity)); } return elem; }
protected String getStatusString(Entity entity) { String statusString; try { statusString = getStatus(entity, entity.getEntityType()).name(); } catch (Throwable throwable) { // Unable to fetch statusString, setting it to unknown for backwards compatibility statusString = "UNKNOWN"; } return statusString; }
protected boolean isEntityAuthorized(Entity entity) { try { SecurityUtil.getAuthorizationProvider() .authorizeEntity( entity.getName(), entity.getEntityType().toString(), entity.getACL(), "list", CurrentUser.getAuthenticatedUGI()); } catch (Exception e) { LOG.info( "Authorization failed for entity=" + entity.getName() + " for user=" + CurrentUser.getUser(), e); return false; } return true; }
@Override public void onAdd(Entity entity) throws FalconException { if (entity.getEntityType().equals(EntityType.FEED)) { Feed feed = (Feed) entity; if (feed.getGroups() == null || feed.getGroups().equals("")) { return; } Set<FeedGroup> groupSet = getGroups(feed); addGroups(feed.getName(), groupSet); } }
@Override public void onChange(final Entity oldEntity, final Entity newEntity) throws FalconException { EntityType entityType = newEntity.getEntityType(); LOG.info("Updating lineage for entity: {}, type: {}", newEntity.getName(), entityType); try { new TransactionRetryHelper.Builder<Void>(getTransactionalGraph()) .perform( new TransactionWork<Void>() { @Override public Void execute(TransactionalGraph transactionalGraph) throws Exception { entityGraphBuilder.updateEntity(oldEntity, newEntity); transactionalGraph.commit(); return null; } }) .build() .exponentialBackoff(transactionRetries, transactionRetryDelayInMillis); } catch (Exception e) { getTransactionalGraph().rollback(); throw new FalconException(e); } }
protected Properties getEntityProperties(Entity myEntity) { Properties properties = new Properties(); switch (myEntity.getEntityType()) { case CLUSTER: org.apache.falcon.entity.v0.cluster.Properties clusterProps = ((Cluster) myEntity).getProperties(); if (clusterProps != null) { for (Property prop : clusterProps.getProperties()) { properties.put(prop.getName(), prop.getValue()); } } break; case FEED: org.apache.falcon.entity.v0.feed.Properties feedProps = ((Feed) myEntity).getProperties(); if (feedProps != null) { for (org.apache.falcon.entity.v0.feed.Property prop : feedProps.getProperties()) { properties.put(prop.getName(), prop.getValue()); } } break; case PROCESS: org.apache.falcon.entity.v0.process.Properties processProps = ((Process) myEntity).getProperties(); if (processProps != null) { for (org.apache.falcon.entity.v0.process.Property prop : processProps.getProperties()) { properties.put(prop.getName(), prop.getValue()); } } break; default: throw new IllegalArgumentException("Unhandled entity type " + myEntity.getEntityType()); } return properties; }
protected void verifyEntityProperties( Entity entity, Cluster cluster, Cluster srcCluster, WorkflowExecutionContext.EntityOperations operation, HashMap<String, String> props) throws Exception { Assert.assertEquals(props.get(WorkflowExecutionArgs.ENTITY_NAME.getName()), entity.getName()); Assert.assertEquals( props.get(WorkflowExecutionArgs.ENTITY_TYPE.getName()), entity.getEntityType().name()); if (WorkflowExecutionContext.EntityOperations.REPLICATE == operation) { Assert.assertEquals( props.get(WorkflowExecutionArgs.CLUSTER_NAME.getName()), cluster.getName() + WorkflowExecutionContext.CLUSTER_NAME_SEPARATOR + srcCluster.getName()); } else { Assert.assertEquals( props.get(WorkflowExecutionArgs.CLUSTER_NAME.getName()), cluster.getName()); } Assert.assertEquals( props.get(WorkflowExecutionArgs.LOG_DIR.getName()), getLogPath(cluster, entity)); Assert.assertEquals(props.get("falconDataOperation"), operation.name()); }
public void updateEntity(Entity oldEntity, Entity newEntity) { EntityType entityType = oldEntity.getEntityType(); switch (entityType) { case CLUSTER: updateClusterEntity((Cluster) oldEntity, (Cluster) newEntity); break; case PROCESS: updateProcessEntity((Process) oldEntity, (Process) newEntity); break; case FEED: updateFeedEntity((Feed) oldEntity, (Feed) newEntity); break; case DATASOURCE: updateDatasourceEntity((Datasource) oldEntity, (Datasource) newEntity); break; default: throw new IllegalArgumentException("Invalid EntityType " + entityType); } }
public void addEntity(Entity entity) { EntityType entityType = entity.getEntityType(); switch (entityType) { case CLUSTER: addClusterEntity((Cluster) entity); break; case PROCESS: addProcessEntity((Process) entity); break; case FEED: addFeedEntity((Feed) entity); break; case DATASOURCE: addDatasourceEntity((Datasource) entity); break; default: throw new IllegalArgumentException("Invalid EntityType " + entityType); } }
@SuppressWarnings({"unchecked", "rawtypes"}) private void validate(Entity entity) throws FalconException { EntityParser entityParser = EntityParserFactory.getParser(entity.getEntityType()); entityParser.validate(entity); }
protected List<Entity> getFilteredEntities( EntityType entityType, String nameSubsequence, String tagKeywords, Map<String, List<String>> filterByFieldsValues, String startDate, String endDate, String cluster) throws FalconException, IOException { Collection<String> entityNames = configStore.getEntities(entityType); if (entityNames.isEmpty()) { return Collections.emptyList(); } List<Entity> entities = new ArrayList<Entity>(); char[] subsequence = nameSubsequence.toLowerCase().toCharArray(); List<String> tagKeywordsList; if (StringUtils.isEmpty(tagKeywords)) { tagKeywordsList = new ArrayList<>(); } else { tagKeywordsList = getFilterByTags(Arrays.asList(tagKeywords.toLowerCase())); } for (String entityName : entityNames) { Entity entity; try { entity = configStore.get(entityType, entityName); if (entity == null) { continue; } } catch (FalconException e1) { LOG.error( "Unable to get list for entities for ({})", entityType.getEntityClass().getSimpleName(), e1); throw FalconWebException.newException(e1, Response.Status.BAD_REQUEST); } if (SecurityUtil.isAuthorizationEnabled() && !isEntityAuthorized(entity)) { // the user who requested list query has no permission to access this entity. Skip this // entity continue; } if (isFilteredByDatesAndCluster(entity, startDate, endDate, cluster)) { // this is for entity summary continue; } SecurityUtil.tryProxy(entity); // filter by fields if (isFilteredByFields(entity, filterByFieldsValues)) { continue; } // filter by subsequence of name if (subsequence.length > 0 && !matchesNameSubsequence(subsequence, entityName.toLowerCase())) { continue; } // filter by tag keywords if (!matchTagKeywords(tagKeywordsList, entity.getTags())) { continue; } entities.add(entity); } return entities; }
@Override // SUSPEND CHECKSTYLE CHECK ParameterNumberCheck public void handleRerun( String clusterName, String entityType, String entityName, String nominalTime, String runId, String wfId, String workflowUser, long msgReceivedTime) { try { Entity entity = EntityUtil.getEntity(entityType, entityName); Retry retry = getRetry(entity); if (retry == null) { LOG.warn( "Retry not configured for entity: {} ({}), ignoring failed retried", entityType, entity.getName()); return; } int attempts = retry.getAttempts(); Frequency delay = retry.getDelay(); PolicyType policy = retry.getPolicy(); int intRunId = Integer.parseInt(runId); if (attempts > intRunId) { AbstractRerunPolicy rerunPolicy = RerunPolicyFactory.getRetryPolicy(policy); long delayTime = rerunPolicy.getDelay(delay, Integer.parseInt(runId)); RetryEvent event = new RetryEvent( clusterName, wfId, msgReceivedTime, delayTime, entityType, entityName, nominalTime, intRunId, attempts, 0, workflowUser); offerToQueue(event); } else { LOG.warn( "All retry attempt failed out of configured: {} attempt for entity instance: {}:{} " + "And WorkflowId: {}", attempts, entityName, nominalTime, wfId); GenericAlert.alertRetryFailed( entityType, entityName, nominalTime, wfId, workflowUser, runId, "All retry attempt failed out of configured: " + attempts + " attempt for entity instance::"); } } catch (FalconException e) { LOG.error("Error during retry of entity instance {}:{}", entityName, nominalTime, e); GenericAlert.alertRetryFailed( entityType, entityName, nominalTime, wfId, workflowUser, runId, e.getMessage()); } }