/* * JBoss, Home of Professional Open Source. * Copyright 2015, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.cli.handlers; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import org.jboss.as.cli.CommandContext; import org.jboss.as.cli.CommandFormatException; import org.jboss.as.cli.CommandLineCompleter; import org.jboss.as.cli.CommandLineException; import org.jboss.as.cli.Util; import org.jboss.as.cli.accesscontrol.AccessRequirement; import org.jboss.as.cli.accesscontrol.AccessRequirementBuilder; import org.jboss.as.cli.impl.ArgumentWithListValue; import org.jboss.as.cli.impl.ArgumentWithValue; import org.jboss.as.cli.impl.ArgumentWithoutValue; import org.jboss.as.cli.impl.CommaSeparatedCompleter; import org.jboss.as.cli.impl.DefaultCompleter; import org.jboss.as.cli.impl.DefaultCompleter.CandidatesProvider; import org.jboss.as.cli.impl.PermittedCandidates; import org.jboss.as.cli.operation.ParsedCommandLine; import org.jboss.as.cli.parsing.ExpressionBaseState; import org.jboss.as.cli.parsing.ParsingState; import org.jboss.as.cli.parsing.WordCharacterHandler; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.client.OperationBuilder; import org.jboss.dmr.ModelNode; import org.jboss.dmr.Property; /** * * @author Alexey Loubyansky */ public class DeploymentOverlayHandler extends BatchModeCommandHandler {//CommandHandlerWithHelp { private static final String ADD = "add"; private static final String LINK = "link"; private static final String LIST_CONTENT = "list-content"; private static final String LIST_LINKS = "list-links"; private static final String REDEPLOY_AFFECTED = "redeploy-affected"; private static final String REMOVE = "remove"; private static final String UPLOAD = "upload"; private static final byte REDEPLOY_NONE = 0; private static final byte REDEPLOY_ONLY_AFFECTED = 1; private static final byte REDEPLOY_ALL = 2; private static final String WARN_MSG = "WARNING: redeployment is required on deployment "; private final ArgumentWithoutValue l; private final ArgumentWithValue action; private final ArgumentWithValue name; private final ArgumentWithListValue content; private final ArgumentWithListValue serverGroups; private final ArgumentWithoutValue allServerGroups; private final ArgumentWithoutValue allRelevantServerGroups; private final ArgumentWithListValue deployments; private final ArgumentWithoutValue redeployAffected; private final FilenameTabCompleter pathCompleter; private AccessRequirement generalListPermission; private AccessRequirement addPermission; private AccessRequirement linkPermission; private AccessRequirement removePermission; private AccessRequirement redeployPermission; private AccessRequirement listContentPermission; private AccessRequirement listLinksPermission; public DeploymentOverlayHandler(CommandContext ctx) { super(ctx, "deployment-overlay", true); l = new ArgumentWithoutValue(this, "-l") { @Override public boolean canAppearNext(CommandContext ctx) throws CommandFormatException { final String actionStr = action.getValue(ctx.getParsedCommandLine()); if(actionStr == null || LIST_CONTENT.equals(actionStr) || LIST_LINKS.equals(actionStr)) { return super.canAppearNext(ctx); } return false; } }; l.setAccessRequirement(generalListPermission); action = new ArgumentWithValue(this, new DefaultCompleter( PermittedCandidates.create(ADD, addPermission) .add(LINK, linkPermission) .add(LIST_CONTENT, listContentPermission) .add(LIST_LINKS, listLinksPermission) .add(REDEPLOY_AFFECTED, redeployPermission) .add(REMOVE, removePermission) .add(UPLOAD, addPermission)), 0, "--action"); name = new ArgumentWithValue(this, new DefaultCompleter(new CandidatesProvider(){ @Override public Collection<String> getAllCandidates(CommandContext ctx) { final ModelControllerClient client = ctx.getModelControllerClient(); if(client == null) { return Collections.emptyList(); } final ModelNode op = new ModelNode(); op.get(Util.OPERATION).set(Util.READ_CHILDREN_NAMES); op.get(Util.ADDRESS).setEmptyList(); op.get(Util.CHILD_TYPE).set(Util.DEPLOYMENT_OVERLAY); final ModelNode response; try { response = client.execute(op); } catch (IOException e) { return Collections.emptyList(); } final ModelNode result = response.get(Util.RESULT); if(!result.isDefined()) { return Collections.emptyList(); } final List<String> names = new ArrayList<String>(); for(ModelNode node : result.asList()) { names.add(node.asString()); } return names; }}), "--name"); name.addRequiredPreceding(action); name.setAccessRequirement(AccessRequirementBuilder.Factory.create(ctx).any() .requirement(addPermission) .requirement(removePermission) .requirement(linkPermission) .requirement(listContentPermission) .requirement(listLinksPermission) .requirement(redeployPermission) .build()); pathCompleter = FilenameTabCompleter.newCompleter(ctx); content = new ArgumentWithListValue(this, new CommandLineCompleter(){ @Override public int complete(CommandContext ctx, String buffer, int cursor, List<String> candidates) { final String actionStr = action.getValue(ctx.getParsedCommandLine()); if (ADD.equals(actionStr) || UPLOAD.equals(actionStr)) { // TODO add support for quoted paths int i = buffer.lastIndexOf(','); i = buffer.indexOf('=', i + 1); if (i < 0) { return -1; } final String path = buffer.substring(i + 1); int pathResult = pathCompleter.complete(ctx, path, path.length(), candidates); if (pathResult < 0) { return -1; } return i + 1 + pathResult; } else if(REMOVE.equals(actionStr)) { final String nameStr = name.getValue(ctx.getParsedCommandLine()); if(nameStr == null) { return -1; } final List<String> existing; try { existing = loadContentFor(ctx.getModelControllerClient(), nameStr); } catch (CommandLineException e) { return -1; } if(existing.isEmpty()) { return buffer.length(); } candidates.addAll(existing); if(buffer.isEmpty()) { return 0; } final String[] specified = buffer.split(",+"); candidates.removeAll(Arrays.asList(specified)); if(buffer.charAt(buffer.length() - 1) == ',') { return buffer.length(); } final String chunk = specified[specified.length - 1]; for(int i = 0; i < candidates.size(); ++i) { if(!candidates.get(i).startsWith(chunk)) { candidates.remove(i); } } return buffer.length() - chunk.length(); } else { return -1; } }}, "--content") { @Override public boolean canAppearNext(CommandContext ctx) throws CommandFormatException { final String actionStr = action.getValue(ctx.getParsedCommandLine()); if(actionStr == null) { return false; } if(ADD.equals(actionStr) || UPLOAD.equals(actionStr) || REMOVE.equals(actionStr)) { return super.canAppearNext(ctx); } return false; } @Override protected ParsingState initParsingState() { final ExpressionBaseState state = new ExpressionBaseState("EXPR", true, false); if(Util.isWindows()) { // to not require escaping FS name separator state.setDefaultHandler(WordCharacterHandler.IGNORE_LB_ESCAPE_OFF); } else { state.setDefaultHandler(WordCharacterHandler.IGNORE_LB_ESCAPE_ON); } return state; } }; content.addRequiredPreceding(name); content.addCantAppearAfter(l); serverGroups = new ArgumentWithListValue(this, new CommaSeparatedCompleter() { @Override protected Collection<String> getAllCandidates(CommandContext ctx) { return Util.getServerGroups(ctx.getModelControllerClient()); }} , "--server-groups") { @Override public boolean canAppearNext(CommandContext ctx) throws CommandFormatException { if(!ctx.isDomainMode()) { return false; } final String actionStr = action.getValue(ctx.getParsedCommandLine()); if(actionStr == null) { return false; } if(ADD.equals(actionStr) || LINK.equals(actionStr) || REMOVE.equals(actionStr) || LIST_LINKS.equals(actionStr)) { return super.canAppearNext(ctx); } return false; } }; serverGroups.addRequiredPreceding(name); allRelevantServerGroups = new ArgumentWithoutValue(this, "--all-relevant-server-groups") { @Override public boolean canAppearNext(CommandContext ctx) throws CommandFormatException { if(!ctx.isDomainMode()) { return false; } final String actionStr = action.getValue(ctx.getParsedCommandLine()); if(actionStr == null) { return false; } if(REMOVE.equals(actionStr)) { return super.canAppearNext(ctx); } return false; } }; allRelevantServerGroups.addRequiredPreceding(name); allRelevantServerGroups.addCantAppearAfter(serverGroups); serverGroups.addCantAppearAfter(allRelevantServerGroups); allServerGroups = new ArgumentWithoutValue(this, "--all-server-groups") { @Override public boolean canAppearNext(CommandContext ctx) throws CommandFormatException { if(!ctx.isDomainMode()) { return false; } final String actionStr = action.getValue(ctx.getParsedCommandLine()); if(actionStr == null) { return false; } if(ADD.equals(actionStr) || LINK.equals(actionStr)) { return super.canAppearNext(ctx); } return false; } }; allServerGroups.addRequiredPreceding(name); allServerGroups.addCantAppearAfter(serverGroups); serverGroups.addCantAppearAfter(allServerGroups); deployments = new ArgumentWithListValue(this, new CommaSeparatedCompleter() { @Override protected Collection<String> getAllCandidates(CommandContext ctx) { final String actionValue = action.getValue(ctx.getParsedCommandLine()); final ModelControllerClient client = ctx.getModelControllerClient(); if(REMOVE.equals(actionValue)) { final String overlay = name.getValue(ctx.getParsedCommandLine()); if(overlay == null) { return Collections.emptyList(); } try { if(ctx.isDomainMode()) { final String groupsStr = serverGroups.getValue(ctx.getParsedCommandLine()); if(groupsStr != null) { final String[] groups = groupsStr.split(",+"); if(groups.length == 1) { return filterLinks(loadLinkResources(client, overlay, groups[0])); } else if(groups.length > 1) { final Set<String> commonLinks = new HashSet<String>(); commonLinks.addAll(filterLinks(loadLinkResources(client, overlay, groups[0]))); for(int i = 1; i < groups.length; ++i) { commonLinks.retainAll(filterLinks(loadLinkResources(client, overlay, groups[i]))); } return commonLinks; } } } else { return filterLinks(loadLinkResources(client, overlay, null)); } } catch(CommandLineException e) { } return Collections.emptyList(); } return Util.getDeployments(client); }}, "--deployments") { @Override public boolean canAppearNext(CommandContext ctx) throws CommandFormatException { if(ctx.isDomainMode()) { if(serverGroups.isPresent(ctx.getParsedCommandLine()) || allServerGroups.isPresent(ctx.getParsedCommandLine())) { return super.canAppearNext(ctx); } return false; } final String actionStr = action.getValue(ctx.getParsedCommandLine()); if(actionStr == null) { return false; } if(ADD.equals(actionStr) || LINK.equals(actionStr) || REMOVE.equals(actionStr)) { return super.canAppearNext(ctx); } return false; } }; deployments.addRequiredPreceding(name); deployments.addCantAppearAfter(l); redeployAffected = new ArgumentWithoutValue(this, "--redeploy-affected") { @Override public boolean canAppearNext(CommandContext ctx) throws CommandFormatException { if(deployments.isPresent(ctx.getParsedCommandLine())) { return super.canAppearNext(ctx); } final String actionValue = action.getValue(ctx.getParsedCommandLine()); if(actionValue != null && (actionValue.equals(UPLOAD) || actionValue.equals(REMOVE))) { return super.canAppearNext(ctx); } return false; } }; redeployAffected.setAccessRequirement(redeployPermission); } @Override protected AccessRequirement setupAccessRequirement(CommandContext ctx) { generalListPermission = AccessRequirementBuilder.Factory.create(ctx).any() .operation(Util.DEPLOYMENT_OVERLAY + "=?", Util.READ_CHILDREN_NAMES).build(); addPermission = AccessRequirementBuilder.Factory.create(ctx) .any() .operation(Util.DEPLOYMENT_OVERLAY + "=?", Util.ADD) .operation(Util.DEPLOYMENT_OVERLAY + "=?/" + Util.CONTENT + "=?", Util.ADD) .build(); linkPermission = AccessRequirementBuilder.Factory.create(ctx) .any() .operation(Util.DEPLOYMENT_OVERLAY + "=?/" + Util.DEPLOYMENT + "=?", Util.ADD) .serverGroupOperation(Util.DEPLOYMENT_OVERLAY + "=?", Util.ADD) .serverGroupOperation(Util.DEPLOYMENT_OVERLAY + "=?/" + Util.DEPLOYMENT + "=?", Util.ADD) .build(); removePermission = AccessRequirementBuilder.Factory.create(ctx) .any() .operation(Util.DEPLOYMENT_OVERLAY + "=?", Util.REMOVE) .operation(Util.DEPLOYMENT_OVERLAY + "=?/" + Util.CONTENT + "=?", Util.REMOVE) .operation(Util.DEPLOYMENT_OVERLAY + "=?/" + Util.DEPLOYMENT + "=?", Util.REMOVE) .all() .serverGroupOperation(Util.DEPLOYMENT_OVERLAY + "=?/" + Util.DEPLOYMENT + "=?", Util.REMOVE) .serverGroupOperation(Util.DEPLOYMENT_OVERLAY + "=?", Util.REMOVE) .parent() .build(); redeployPermission = AccessRequirementBuilder.Factory.create(ctx).any() .operation(Util.DEPLOYMENT + "=?", Util.REDEPLOY) .serverGroupOperation(Util.DEPLOYMENT + "=?", Util.REDEPLOY + "a") .build(); listContentPermission = AccessRequirementBuilder.Factory.create(ctx).any() .operation(Util.DEPLOYMENT_OVERLAY + "=?", Util.READ_CHILDREN_NAMES) .build(); listLinksPermission = AccessRequirementBuilder.Factory.create(ctx) .any() .operation(Util.DEPLOYMENT_OVERLAY + "=?", Util.READ_CHILDREN_NAMES) .serverGroupOperation(Util.DEPLOYMENT_OVERLAY + "=?", Util.READ_CHILDREN_NAMES) .build(); return AccessRequirementBuilder.Factory.create(ctx) .any() .requirement(generalListPermission) .requirement(addPermission) .requirement(linkPermission) .requirement(removePermission) .requirement(redeployPermission) .requirement(listContentPermission) .requirement(listLinksPermission) .build(); } @Override public boolean isBatchMode(CommandContext ctx) { final String action = this.action.getValue(ctx.getParsedCommandLine()); if(action != null && (LIST_LINKS.equals(action) || LIST_CONTENT.equals(action))) { return false; } return super.isBatchMode(ctx); } @Override protected void doHandle(CommandContext ctx) throws CommandLineException { final ParsedCommandLine args = ctx.getParsedCommandLine(); if(!args.hasProperties() || l.isPresent(args) && args.getOtherProperties().isEmpty() && args.getPropertyNames().size() == 1) { // list registered overlays final ModelNode op = new ModelNode(); op.get(Util.ADDRESS).setEmptyList(); op.get(Util.OPERATION).set(Util.READ_CHILDREN_NAMES); op.get(Util.CHILD_TYPE).set(Util.DEPLOYMENT_OVERLAY); final ModelNode response; try { response = ctx.getModelControllerClient().execute(op); } catch (IOException e) { throw new CommandLineException("Failed to execute " + Util.READ_CHILDREN_NAMES, e); } final ModelNode result = response.get(Util.RESULT); if(!result.isDefined()) { final String descr = Util.getFailureDescription(response); if(descr != null) { throw new CommandLineException(descr); } throw new CommandLineException("The response of " + Util.READ_CHILDREN_NAMES + " is missing result: " + response); } if(l.isPresent(args)) { for(ModelNode node : result.asList()) { ctx.printLine(node.asString()); } } else { final List<String> names = new ArrayList<String>(); for(ModelNode node : result.asList()) { names.add(node.asString()); } ctx.printColumns(names); } return; } final String action = this.action.getValue(args, true); if(ADD.equals(action)) { add(ctx, true); } else if(UPLOAD.equals(action)) { upload(ctx, true); } else if(LIST_CONTENT.equals(action)) { listContent(ctx); } else if(LIST_LINKS.equals(action)) { listLinks(ctx); } else { super.doHandle(ctx); } } @Override protected ModelNode buildRequestWithoutHeaders(CommandContext ctx) throws CommandFormatException { final ParsedCommandLine args = ctx.getParsedCommandLine(); final String action = this.action.getValue(args, true); try { if (REMOVE.equals(action)) { return remove(ctx); } else if (LINK.equals(action)) { return link(ctx); } else if (REDEPLOY_AFFECTED.equals(action)) { return redeployAffected(ctx); } else if (ADD.equals(action)) { return add(ctx, false); } else if (UPLOAD.equals(action)) { return upload(ctx, false); } else { throw new CommandFormatException("Doesn't know how to build request for action '" + action + "'"); } } catch (CommandFormatException e) { throw e; } catch (CommandLineException e) { throw new CommandFormatException("Failed to build " + action + " request.", e); } } protected ModelNode redeployAffected(CommandContext ctx) throws CommandLineException { final ParsedCommandLine args = ctx.getParsedCommandLine(); assertNotPresent(serverGroups, args); assertNotPresent(allServerGroups, args); assertNotPresent(allRelevantServerGroups, args); assertNotPresent(content, args); assertNotPresent(deployments, args); assertNotPresent(redeployAffected, args); final String overlay = name.getValue(args, true); final ModelControllerClient client = ctx.getModelControllerClient(); final ModelNode redeployOp = new ModelNode(); redeployOp.get(Util.OPERATION).set(Util.COMPOSITE); redeployOp.get(Util.ADDRESS).setEmptyList(); final ModelNode steps = redeployOp.get(Util.STEPS); if(ctx.isDomainMode()) { for(String group : Util.getServerGroupsReferencingOverlay(overlay, client)) { addRemoveRedeployLinksSteps(ctx, client, steps, overlay, group, null, false, REDEPLOY_ALL); } } else { addRemoveRedeployLinksSteps(ctx, client, steps, overlay, null, null, false, REDEPLOY_ALL); } if(!steps.isDefined() || steps.asList().isEmpty()) { throw new CommandFormatException("None of the deployments affected."); } return redeployOp; /* try { final ModelNode result = client.execute(redeployOp); if (!Util.isSuccess(result)) { throw new CommandLineException(Util.getFailureDescription(result)); } } catch (IOException e) { throw new CommandFormatException("Failed to redeploy affected deployments", e); } */ } protected void listLinks(CommandContext ctx) throws CommandLineException { final ModelControllerClient client = ctx.getModelControllerClient(); final ParsedCommandLine args = ctx.getParsedCommandLine(); assertNotPresent(allRelevantServerGroups, args); assertNotPresent(content, args); assertNotPresent(deployments, args); assertNotPresent(redeployAffected, args); final String name = this.name.getValue(args, true); if(name == null) { throw new CommandFormatException(this.name + " is missing value."); } final String sg = serverGroups.getValue(ctx.getParsedCommandLine()); if(ctx.isDomainMode()) { final List<String> groups; if(sg == null) { //throw new CommandFormatException(serverGroups.getFullName() + " is missing value."); groups = Util.getServerGroups(client); } else { groups = Arrays.asList(sg.split(",+")); } if(groups.size() == 0) { throw new CommandFormatException(serverGroups.getFullName() + " is missing value."); } for(String group : groups) { final List<String> links = loadLinks(client, name, group); if(!links.isEmpty()) { ctx.printLine("SERVER GROUP: " + group + Util.LINE_SEPARATOR); ctx.printColumns(links); ctx.printLine(""); } } } else { final List<String> content = loadLinks(client, name, sg); if (l.isPresent(args)) { for (String contentPath : content) { ctx.printLine(contentPath); } } else { ctx.printColumns(content); } } } protected void listContent(CommandContext ctx) throws CommandLineException { final ModelControllerClient client = ctx.getModelControllerClient(); final ParsedCommandLine args = ctx.getParsedCommandLine(); assertNotPresent(serverGroups, args); assertNotPresent(allServerGroups, args); assertNotPresent(allRelevantServerGroups, args); assertNotPresent(deployments, args); assertNotPresent(content, args); assertNotPresent(redeployAffected, args); final String name = this.name.getValue(args, true); if(name == null) { throw new CommandFormatException(this.name.getFullName() + " is missing value."); } final List<String> content = loadContentFor(client, name); if(l.isPresent(args)) { for(String contentPath : content) { ctx.printLine(contentPath); } } else { ctx.printColumns(content); } } protected ModelNode remove(CommandContext ctx) throws CommandLineException { final ModelControllerClient client = ctx.getModelControllerClient(); final ParsedCommandLine args = ctx.getParsedCommandLine(); assertNotPresent(allServerGroups, args); final String name = this.name.getValue(args, true); if(name == null) { throw new CommandFormatException(this.name + " is missing value."); } final String contentStr = content.getValue(args); String deploymentStr = deployments.getValue(args); final String sgStr = serverGroups.getValue(args); final List<String> sg; if(sgStr == null) { if(allRelevantServerGroups.isPresent(args)) { sg = Util.getServerGroupsReferencingOverlay(name, client); } else { sg = null; } } else { sg = Arrays.asList(sgStr.split(",+")); if(sg.isEmpty()) { throw new CommandFormatException(serverGroups.getFullName() + " is missing value."); } } final ModelNode composite = new ModelNode(); composite.get(Util.OPERATION).set(Util.COMPOSITE); composite.get(Util.ADDRESS).setEmptyList(); final ModelNode steps = composite.get(Util.STEPS); byte redeploy = this.redeployAffected.isPresent(args) ? REDEPLOY_ONLY_AFFECTED : REDEPLOY_NONE; if (!redeployAffected.isPresent(args)) { printWarning(ctx, client, name, contentStr, deploymentStr); } // remove the content first and determine whether all the linked deployments // should be redeployed if(contentStr != null || deploymentStr == null && sg == null) { if(redeploy == REDEPLOY_ONLY_AFFECTED) { redeploy = REDEPLOY_ALL; } final List<String> contentList; if(contentStr == null) { contentList = loadContentFor(client, name); } else { contentList = java.util.Arrays.asList(contentStr.split(",+")); } for(String content : contentList) { final ModelNode op = new ModelNode(); ModelNode addr = op.get(Util.ADDRESS); addr.add(Util.DEPLOYMENT_OVERLAY, name); addr.add(Util.CONTENT, content); op.get(Util.OPERATION).set(Util.REMOVE); steps.add(op); } } if(deploymentStr != null || contentStr == null) { // remove the overlay links if(ctx.isDomainMode()) { if(deploymentStr == null) { final List<String> groups = sg == null ? Util.getServerGroupsReferencingOverlay(name, client) : sg; for(String group : groups) { addRemoveRedeployLinksSteps(ctx, client, steps, name, group, null, true, redeploy); } } else { if(ctx.isDomainMode() && sg == null) { throw new CommandFormatException(serverGroups.getFullName() + " or " + allRelevantServerGroups.getFullName() + " is required."); } final List<String> links = Arrays.asList(deploymentStr.split(",+")); for(String group : sg) { addRemoveRedeployLinksSteps(ctx, client, steps, name, group, links, true, redeploy); } } } else { if(deploymentStr == null) { // remove all addRemoveRedeployLinksSteps(ctx, client, steps, name, null, null, true, redeploy); } else { final List<String> links = Arrays.asList(deploymentStr.split(",+")); addRemoveRedeployLinksSteps(ctx, client, steps, name, null, links, true, redeploy); } } } else if(redeploy == REDEPLOY_ALL) { if(ctx.isDomainMode()) { for(String group : Util.getServerGroupsReferencingOverlay(name, client)) { addRemoveRedeployLinksSteps(ctx, client, steps, name, group, null, false, redeploy); } } else { addRemoveRedeployLinksSteps(ctx, client, steps, name, null, null, false, redeploy); } } if(contentStr == null && deploymentStr == null && sg == null) { final ModelNode op = new ModelNode(); op.get(Util.ADDRESS).add(Util.DEPLOYMENT_OVERLAY, name); op.get(Util.OPERATION).set(Util.REMOVE); steps.add(op); } return composite; /* try { final ModelNode result = client.execute(composite); if (!Util.isSuccess(result)) { ctx.printLine("failed request: " + composite.toString()); ctx.printLine("failed response: " + result.toString()); throw new CommandFormatException(Util.getFailureDescription(result)); } } catch (IOException e) { throw new CommandFormatException("Failed to remove overlay", e); } */ } // WFCORE-2045 print warning on CLI side when argument '--redeploy-affected' is not specified private void printWarning(CommandContext ctx, final ModelControllerClient client, final String name, final String content, final String deployment) throws CommandLineException { if (ctx.isDomainMode()) { for (String sgName : Util.getServerGroups(client)) { printWarningMessage(ctx, client, name, content, deployment, sgName); } } else { printWarningMessage(ctx, client, name, content, deployment, null); } } private void printWarningMessage(CommandContext ctx, final ModelControllerClient client, final String name, final String content, final String deployment, String sgName) throws CommandLineException { if (content == null && deployment != null) { // explicit deployment specified printLine(ctx, Arrays.asList(deployment.split(",+")), sgName); } else { final ModelNode linkResources = loadLinkResources(client, name, sgName); if (linkResources != null && !linkResources.keys().isEmpty()) { printLine(ctx, linkResources.keys(), sgName); } } } private void printLine(CommandContext ctx, final Collection<String> deployment, final String sgName) { String message = WARN_MSG + deployment; if (sgName == null) { ctx.printLine(message); } else { // domain mode with server group specified. ctx.printLine(message + " in server group " + sgName); } } protected ModelNode add(CommandContext ctx, boolean stream) throws CommandLineException { final ParsedCommandLine args = ctx.getParsedCommandLine(); assertNotPresent(allRelevantServerGroups, args); final String name = this.name.getValue(args, true); final String contentStr = content.getValue(args, true); final String[] contentPairs = contentStr.split(",+"); if(contentPairs.length == 0) { throw new CommandFormatException("Overlay content is not specified."); } final String[] contentNames = new String[contentPairs.length]; final File[] contentPaths = new File[contentPairs.length]; for(int i = 0; i < contentPairs.length; ++i) { final String pair = contentPairs[i]; final int equalsIndex = pair.indexOf('='); if(equalsIndex < 0) { throw new CommandFormatException("Content pair is not following archive-path=fs-path format: '" + pair + "'"); } contentNames[i] = pair.substring(0, equalsIndex); if(contentNames[i].length() == 0) { throw new CommandFormatException("The archive path is missing for the content '" + pair + "'"); } String path = pair.substring(equalsIndex + 1); if(path.length() == 0) { throw new CommandFormatException("The filesystem paths is missing for the content '" + pair + "'"); } path = pathCompleter.translatePath(path); final File f = new File(path); if(!f.exists()) { throw new CommandFormatException("Content file doesn't exist " + f.getAbsolutePath()); } contentPaths[i] = f; } final String[] deployments = getLinks(this.deployments, args); final ModelControllerClient client = ctx.getModelControllerClient(); final ModelNode composite = new ModelNode(); final OperationBuilder opBuilder = stream ? new OperationBuilder(composite, true) : null; composite.get(Util.OPERATION).set(Util.COMPOSITE); composite.get(Util.ADDRESS).setEmptyList(); final ModelNode steps = composite.get(Util.STEPS); // add the overlay ModelNode op = new ModelNode(); ModelNode address = op.get(Util.ADDRESS); address.add(Util.DEPLOYMENT_OVERLAY, name); op.get(Util.OPERATION).set(Util.ADD); steps.add(op); // add the content for (int i = 0; i < contentNames.length; ++i) { final String contentName = contentNames[i]; op = new ModelNode(); address = op.get(Util.ADDRESS); address.add(Util.DEPLOYMENT_OVERLAY, name); address.add(Util.CONTENT, contentName); op.get(Util.OPERATION).set(Util.ADD); if(opBuilder != null) { op.get(Util.CONTENT).get(Util.INPUT_STREAM_INDEX).set(i); opBuilder.addFileAsAttachment(contentPaths[i]); } else { op.get(Util.CONTENT).get(Util.BYTES).set(Util.readBytes(contentPaths[i])); } steps.add(op); } if(deployments != null) { if(ctx.isDomainMode()) { final List<String> sg = getServerGroupsToLink(ctx); for(String group : sg) { // here we don't need a separate check whether the overlay is linked // from the server group since it is created in the same op. op = new ModelNode(); address = op.get(Util.ADDRESS); address.add(Util.SERVER_GROUP, group); address.add(Util.DEPLOYMENT_OVERLAY, name); op.get(Util.OPERATION).set(Util.ADD); steps.add(op); addAddRedeployLinksSteps(ctx, steps, name, group, deployments, false); } } else { addAddRedeployLinksSteps(ctx, steps, name, null, deployments, false); } } else if(ctx.isDomainMode() && (serverGroups.isPresent(args) || allServerGroups.isPresent(args))) { throw new CommandFormatException("server groups are specified but " + this.deployments.getFullName() + " is not."); } if(opBuilder == null) { return composite; } try { final ModelNode result = client.execute(opBuilder.build()); if (!Util.isSuccess(result)) { throw new CommandFormatException(Util.getFailureDescription(result)); } } catch (IOException e) { throw new CommandFormatException("Failed to add overlay", e); } return null; } protected ModelNode link(CommandContext ctx) throws CommandLineException { final ParsedCommandLine args = ctx.getParsedCommandLine(); assertNotPresent(allRelevantServerGroups, args); final String name = this.name.getValue(args, true); final String[] deployments = getLinks(this.deployments, args); if(deployments == null) { throw new CommandFormatException(this.deployments.getFullName() + " is required."); } final ModelNode composite = new ModelNode(); composite.get(Util.OPERATION).set(Util.COMPOSITE); composite.get(Util.ADDRESS).setEmptyList(); final ModelNode steps = composite.get(Util.STEPS); final ModelControllerClient client = ctx.getModelControllerClient(); if(ctx.isDomainMode()) { final List<String> sg = getServerGroupsToLink(ctx); for(String group : sg) { if(!Util.isValidPath(client, Util.SERVER_GROUP, group, Util.DEPLOYMENT_OVERLAY, name)) { final ModelNode op = new ModelNode(); final ModelNode address = op.get(Util.ADDRESS); address.add(Util.SERVER_GROUP, group); address.add(Util.DEPLOYMENT_OVERLAY, name); op.get(Util.OPERATION).set(Util.ADD); steps.add(op); } addAddRedeployLinksSteps(ctx, steps, name, group, deployments, false); } } else { addAddRedeployLinksSteps(ctx, steps, name, null, deployments, false); } return composite; /* try { final ModelNode result = client.execute(composite); if (!Util.isSuccess(result)) { throw new CommandFormatException(Util.getFailureDescription(result)); } } catch (IOException e) { throw new CommandFormatException("Failed to link overlay", e); } */ } protected ModelNode upload(CommandContext ctx, boolean stream) throws CommandLineException { final ParsedCommandLine args = ctx.getParsedCommandLine(); final String name = this.name.getValue(args, true); if(!Util.isValidPath(ctx.getModelControllerClient(), Util.DEPLOYMENT_OVERLAY, name)) { throw new CommandLineException("Deployment overlay " + name + " does not exist."); } final String contentStr = content.getValue(args, true); final String[] contentPairs = contentStr.split(",+"); if(contentPairs.length == 0) { throw new CommandFormatException("Overlay content is not specified."); } final String[] contentNames = new String[contentPairs.length]; final File[] contentPaths = new File[contentPairs.length]; for(int i = 0; i < contentPairs.length; ++i) { final String pair = contentPairs[i]; final int equalsIndex = pair.indexOf('='); if(equalsIndex < 0) { throw new CommandFormatException("Content pair is not following archive-path=fs-path format: '" + pair + "'"); } contentNames[i] = pair.substring(0, equalsIndex); if(contentNames[i].length() == 0) { throw new CommandFormatException("The archive path is missing for the content '" + pair + "'"); } String path = pair.substring(equalsIndex + 1); if(path.length() == 0) { throw new CommandFormatException("The filesystem paths is missing for the content '" + pair + "'"); } path = pathCompleter.translatePath(path); final File f = new File(path); if(!f.exists()) { throw new CommandFormatException("Content file doesn't exist " + f.getAbsolutePath()); } contentPaths[i] = f; } final String deploymentsStr = deployments.getValue(args); if(deploymentsStr != null) { throw new CommandFormatException(deployments.getFullName() + " can't be used in combination with upload."); } final ModelControllerClient client = ctx.getModelControllerClient(); final ModelNode composite = new ModelNode(); final OperationBuilder opBuilder = stream ? new OperationBuilder(composite, true) : null; composite.get(Util.OPERATION).set(Util.COMPOSITE); composite.get(Util.ADDRESS).setEmptyList(); final ModelNode steps = composite.get(Util.STEPS); // add the content for (int i = 0; i < contentNames.length; ++i) { final ModelNode op = new ModelNode(); ModelNode address = op.get(Util.ADDRESS); address.add(Util.DEPLOYMENT_OVERLAY, name); address.add(Util.CONTENT, contentNames[i]); op.get(Util.OPERATION).set(Util.ADD); if(opBuilder != null) { op.get(Util.CONTENT).get(Util.INPUT_STREAM_INDEX).set(i); opBuilder.addFileAsAttachment(contentPaths[i]); } else { op.get(Util.CONTENT).get(Util.BYTES).set(Util.readBytes(contentPaths[i])); } steps.add(op); } if(redeployAffected.isPresent(args)) { if(ctx.isDomainMode()) { for(String sgName : Util.getServerGroups(client)) { addRemoveRedeployLinksSteps(ctx, client, steps, name, sgName, null, false, REDEPLOY_ALL); } } else { addRemoveRedeployLinksSteps(ctx, client, steps, name, null, null, false, REDEPLOY_ALL); } } else { printWarning(ctx, client, name, contentStr, null); } if(opBuilder == null) { return composite; } try { final ModelNode result = client.execute(opBuilder.build()); if (!Util.isSuccess(result)) { throw new CommandFormatException(Util.getFailureDescription(result)); } } catch (IOException e) { throw new CommandFormatException("Failed to add overlay", e); } return null; } protected List<String> loadContentFor(final ModelControllerClient client, final String overlay) throws CommandLineException { final List<String> contentList; final ModelNode op = new ModelNode(); op.get(Util.ADDRESS).add(Util.DEPLOYMENT_OVERLAY, overlay); op.get(Util.OPERATION).set(Util.READ_CHILDREN_NAMES); op.get(Util.CHILD_TYPE).set(Util.CONTENT); final ModelNode response; try { response = client.execute(op); } catch (IOException e) { throw new CommandLineException("Failed to load the list of the existing content for overlay " + overlay, e); } final ModelNode result = response.get(Util.RESULT); if(!result.isDefined()) { throw new CommandLineException("Failed to load the list of the existing content for overlay " + overlay + ": " + response); } contentList = new ArrayList<String>(); for(ModelNode node : result.asList()) { contentList.add(node.asString()); } return contentList; } protected List<String> loadLinks(final ModelControllerClient client, String overlay, String serverGroup) throws CommandLineException { final ModelNode op = new ModelNode(); final ModelNode addr = op.get(Util.ADDRESS); if(serverGroup != null) { addr.add(Util.SERVER_GROUP, serverGroup); } addr.add(Util.DEPLOYMENT_OVERLAY, overlay); op.get(Util.OPERATION).set(Util.READ_CHILDREN_NAMES); op.get(Util.CHILD_TYPE).set(Util.DEPLOYMENT); final ModelNode response; try { response = client.execute(op); } catch (IOException e) { throw new CommandLineException("Failed to load the list of deployments for overlay " + overlay, e); } final ModelNode result = response.get(Util.RESULT); if(!result.isDefined()) { final String descr = Util.getFailureDescription(response); if(descr != null && descr.contains("WFLYCTL0216")) { // resource doesn't exist return Collections.emptyList(); } throw new CommandLineException("Failed to load the list of deployments for overlay " + overlay + ": " + response); } final List<String> contentList = new ArrayList<String>(); for(ModelNode node : result.asList()) { contentList.add(node.asString()); } return contentList; } protected ModelNode loadLinkResources(final ModelControllerClient client, String overlay, String serverGroup) throws CommandLineException { final ModelNode op = new ModelNode(); final ModelNode addr = op.get(Util.ADDRESS); if(serverGroup != null) { addr.add(Util.SERVER_GROUP, serverGroup); } addr.add(Util.DEPLOYMENT_OVERLAY, overlay); op.get(Util.OPERATION).set(Util.READ_CHILDREN_RESOURCES); op.get(Util.CHILD_TYPE).set(Util.DEPLOYMENT); final ModelNode response; try { response = client.execute(op); } catch (IOException e) { throw new CommandLineException("Failed to load the list of deployments for overlay " + overlay, e); } final ModelNode result = response.get(Util.RESULT); if(!result.isDefined()) { final String descr = Util.getFailureDescription(response); if(descr != null && (descr.contains("WFLYCTL0216") || descr.contains("WFLYCTL0202"))) { // resource doesn't exist return null; } throw new CommandLineException("Failed to load the list of deployments for overlay " + overlay + ": " + response); } return result; } protected void addRedeployStep(final ModelNode steps, final String deployment, String serverGroup) { final ModelNode step = new ModelNode(); final ModelNode address = step.get(Util.ADDRESS); if(serverGroup != null) { address.add(Util.SERVER_GROUP, serverGroup); } address.add(Util.DEPLOYMENT, deployment); step.get(Util.OPERATION).set(Util.REDEPLOY); steps.add(step); } protected String[] getLinks(ArgumentWithValue linksArg, final ParsedCommandLine args) throws CommandFormatException { final String deploymentsStr = linksArg.getValue(args); final String[] deployments; if(deploymentsStr == null) { deployments = null; } else { deployments = deploymentsStr.split(",+"); if(deployments.length == 0) { throw new CommandFormatException(linksArg.getFullName() + " is missing value."); } } return deployments; } protected List<String> getServerGroupsToLink(CommandContext ctx) throws CommandFormatException { final List<String> sg; final String sgStr = serverGroups.getValue(ctx.getParsedCommandLine()); if(allServerGroups.isPresent(ctx.getParsedCommandLine())) { if(sgStr != null) { throw new CommandFormatException("Only one of " + allServerGroups.getFullName() + " or " + serverGroups.getFullName() + " can be specified at a time."); } sg = Util.getServerGroups(ctx.getModelControllerClient()); if(sg.isEmpty()) { throw new CommandFormatException("No server group is available."); } } else { if(sgStr == null) { throw new CommandFormatException(serverGroups.getFullName() + " or " + allServerGroups.getFullName() + " must be specified."); } sg = Arrays.asList(sgStr.split(",+")); if(sg.isEmpty()) { throw new CommandFormatException(serverGroups.getFullName() + " is missing value."); } } return sg; } protected void addAddRedeployLinksSteps(CommandContext ctx, ModelNode steps, String overlay, String serverGroup, String[] links, boolean regexp) throws CommandLineException { boolean warn = false; for(String link : links) { final ModelNode op = new ModelNode(); final ModelNode address = op.get(Util.ADDRESS); if(serverGroup != null) { address.add(Util.SERVER_GROUP, serverGroup); } address.add(Util.DEPLOYMENT_OVERLAY, overlay); address.add(Util.DEPLOYMENT, link); op.get(Util.OPERATION).set(Util.ADD); steps.add(op); if (redeployAffected.isPresent(ctx.getParsedCommandLine())) { final List<String> matchingDeployments = Util.getMatchingDeployments(ctx.getModelControllerClient(), link, serverGroup, true); if (!matchingDeployments.isEmpty()) { if (serverGroup == null) { for (String deployment : matchingDeployments) { final ModelNode step = new ModelNode(); final ModelNode addr = step.get(Util.ADDRESS); addr.add(Util.DEPLOYMENT, deployment); step.get(Util.OPERATION).set(Util.REDEPLOY); steps.add(step); } } else { for (String deployment : matchingDeployments) { final ModelNode step = new ModelNode(); final ModelNode addr = step.get(Util.ADDRESS); addr.add(Util.SERVER_GROUP, serverGroup); addr.add(Util.DEPLOYMENT, deployment); step.get(Util.OPERATION).set(Util.REDEPLOY); steps.add(step); } } } } else { warn = true; } } if (warn) { String warningMsg = serverGroup == null ? WARN_MSG + Arrays.toString(links) : WARN_MSG + Arrays.toString(links) + " in server group " + serverGroup; ctx.printLine(warningMsg); } } protected void addRemoveRedeployLinksSteps(CommandContext ctx, ModelControllerClient client, ModelNode steps, String overlay, String sgName, List<String> specifiedLinks, boolean removeLinks, byte redeploy) throws CommandLineException { final ModelNode linkResources = loadLinkResources(client, overlay, sgName); if(linkResources == null) { return; } if(linkResources.keys().isEmpty()) { return; } if(removeLinks) { final Iterator<String> linkNames; if(specifiedLinks != null) { linkNames = specifiedLinks.iterator(); } else { linkNames = linkResources.keys().iterator(); } while(linkNames.hasNext()) { final String linkName = linkNames.next(); final ModelNode op = new ModelNode(); final ModelNode addr = op.get(Util.ADDRESS); if(sgName != null) { addr.add(Util.SERVER_GROUP, sgName); } addr.add(Util.DEPLOYMENT_OVERLAY, overlay); addr.add(Util.DEPLOYMENT, linkName); op.get(Util.OPERATION).set(Util.REMOVE); steps.add(op); } if(specifiedLinks == null && sgName != null) { // this is only for the domain mode for the specific server group // TODO specified links may cover all, in which case it wouldn't clean this one final ModelNode op = new ModelNode(); final ModelNode addr = op.get(Util.ADDRESS); addr.add(Util.SERVER_GROUP, sgName); addr.add(Util.DEPLOYMENT_OVERLAY, overlay); op.get(Util.OPERATION).set(Util.REMOVE); steps.add(op); } } // redeploy final Iterator<String> linkNames; if(redeploy == REDEPLOY_ALL) { linkNames = linkResources.keys().iterator(); } else if(redeploy == REDEPLOY_ONLY_AFFECTED && specifiedLinks != null) { linkNames = specifiedLinks.iterator(); } else { return; } final List<String> sgDeployments = Util.getDeployments(client, sgName); while(linkNames.hasNext() && !sgDeployments.isEmpty()) { final String linkName = linkNames.next(); final ModelNode link = linkResources.get(linkName); if(!link.isDefined()) { final StringBuilder buf = new StringBuilder(); buf.append(linkName); buf.append(" not found among the registered links "); if(sgName != null) { buf.append("for server group ").append(sgName).append(' '); } buf.append(linkResources.keys()); throw new CommandFormatException(buf.toString()); } addRedeploySteps(steps, sgName, linkName, link, sgDeployments); } } protected void addRedeploySteps(ModelNode steps, String serverGroup, String linkName, ModelNode link, List<String> remainingDeployments) { final Pattern pattern = Pattern.compile(Util.wildcardToJavaRegex(linkName)); final Iterator<String> i = remainingDeployments.iterator(); while (i.hasNext()) { final String deployment = i.next(); if (pattern.matcher(deployment).matches()) { i.remove(); addRedeployStep(steps, deployment, serverGroup); } } } protected void assertNotPresent(ArgumentWithoutValue arg, ParsedCommandLine args) throws CommandFormatException { if(arg.isPresent(args)) { throw new CommandFormatException(arg.getFullName() + " is not allowed with action '" + action.getValue(args) + "'"); } } protected List<String> filterLinks(final ModelNode linkResources) { if(linkResources != null && !linkResources.keys().isEmpty()) { final List<Property> links = linkResources.asPropertyList(); final List<String> linkNames = new ArrayList<String>(links.size()); for (Property link : links) { linkNames.add(link.getName()); } return linkNames; } return Collections.emptyList(); } }