This is an automated email from the ASF dual-hosted git repository. heneveld pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/brooklyn-library.git
commit d2fda1781f1db4619cb7dc4311234707813ea203 Author: Alex Heneveld <[email protected]> AuthorDate: Tue Oct 18 14:08:19 2022 +0100 ansible workflow step, and tidies to ansible --- .../entity/cm/ansible/AnsibleBashCommands.java | 33 ++--- .../brooklyn/entity/cm/ansible/AnsibleConfig.java | 4 +- .../entity/cm/ansible/AnsibleEntitySshDriver.java | 4 +- .../entity/cm/ansible/AnsiblePlaybookTasks.java | 17 ++- .../entity/cm/ansible/AnsibleSshWorkflowStep.java | 139 +++++++++++++++++++++ software/cm/ansible/src/main/resources/catalog.bom | 5 + 6 files changed, 179 insertions(+), 23 deletions(-) diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java index ffe74e44b..57ab0b7be 100644 --- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java +++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleBashCommands.java @@ -18,22 +18,27 @@ */ package org.apache.brooklyn.entity.cm.ansible; -import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_CURL; -import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_TAR; -import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_UNZIP; -import static org.apache.brooklyn.util.ssh.BashCommands.installExecutable; -import static org.apache.brooklyn.util.ssh.BashCommands.ifExecutableElse0; -import static org.apache.brooklyn.util.ssh.BashCommands.sudo; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.util.core.file.BrooklynOsCommands; +import org.apache.brooklyn.util.ssh.BashCommandsConfigurable; -import org.apache.brooklyn.util.ssh.BashCommands; +import static org.apache.brooklyn.util.ssh.BashCommands.sudo; public class AnsibleBashCommands { - public static final String INSTALL_ANSIBLE = - BashCommands.chain( - ifExecutableElse0("apt-add-repository",sudo("apt-add-repository -y ppa:ansible/ansible")), - INSTALL_CURL, - INSTALL_TAR, - INSTALL_UNZIP, - installExecutable("ansible")); + public static final String INSTALL_ANSIBLE(Entity entity) { + return INSTALL_ANSIBLE(BrooklynOsCommands.bash(entity, true)); + } + + static final String INSTALL_ANSIBLE(BashCommandsConfigurable cmds) { + return cmds.chain( + cmds.ifExecutableElse0("apt-add-repository",sudo("apt-add-repository -y ppa:ansible/ansible")), + cmds.INSTALL_CURL, + cmds.INSTALL_TAR, + cmds.INSTALL_UNZIP, + cmds.installExecutable("ansible")); + } + + public static final String INSTALL_ANSIBLE = INSTALL_ANSIBLE(BashCommandsConfigurable.newInstance()); + } diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java index a47ffebfa..a52f11971 100644 --- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java +++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleConfig.java @@ -34,11 +34,11 @@ public interface AnsibleConfig { @SetFromFlag("playbook") ConfigKey<String> ANSIBLE_PLAYBOOK = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbook", - "Playbook to be execute by Ansible"); + "Name to be used for a playbook to be execute by Ansible"); @SetFromFlag("playbook.yaml") ConfigKey<String> ANSIBLE_PLAYBOOK_YAML = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookYaml", - "Playbook to be execute by Ansible"); + "Playbook contents as YAML in a string, to be execute by Ansible"); @SetFromFlag("playbook.url") ConfigKey<String> ANSIBLE_PLAYBOOK_URL = ConfigKeys.newStringConfigKey("brooklyn.ansible.playbookUrl"); diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntitySshDriver.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntitySshDriver.java index 8846670dc..41948a264 100644 --- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntitySshDriver.java +++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleEntitySshDriver.java @@ -58,7 +58,7 @@ public class AnsibleEntitySshDriver extends AbstractSoftwareProcessSshDriver imp String playbookYaml = getEntity().config().get(AnsibleConfig.ANSIBLE_PLAYBOOK_YAML); if (playbookUrl != null && playbookYaml != null) { - throw new IllegalArgumentException( "You can not specify both "+ AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName() + + throw new IllegalArgumentException( "You cannot specify both "+ AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName() + " and " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " as arguments."); } @@ -79,7 +79,7 @@ public class AnsibleEntitySshDriver extends AbstractSoftwareProcessSshDriver imp } if (Strings.isNonBlank(playbookYaml)) { - DynamicTasks.queue(AnsiblePlaybookTasks.buildPlaybookFile(getRunDir(), playbookName)); + DynamicTasks.queue(AnsiblePlaybookTasks.buildPlaybookFile(getRunDir(), playbookName, playbookYaml)); } DynamicTasks.queue(AnsiblePlaybookTasks.runAnsible(getRunDir(), extraVars, playbookName)); } diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java index fffbcea25..84d2bada9 100644 --- a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java +++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsiblePlaybookTasks.java @@ -22,6 +22,7 @@ import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.mgmt.TaskFactory; import org.apache.brooklyn.core.effector.EffectorTasks; import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks; +import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; import org.apache.brooklyn.util.core.ResourceUtils; import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory; @@ -41,7 +42,9 @@ public class AnsiblePlaybookTasks { private static final String EXTRA_VARS_FILENAME = "extra_vars.yaml"; public static TaskFactory<?> installAnsible(String ansibleDirectory, boolean force) { - String installCmd = cdAndRun(ansibleDirectory, AnsibleBashCommands.INSTALL_ANSIBLE); + Entity entity = BrooklynTaskTags.getContextEntity(Tasks.current()); + String installCmd = cdAndRun(ansibleDirectory, entity!=null ? AnsibleBashCommands.INSTALL_ANSIBLE(entity) : AnsibleBashCommands.INSTALL_ANSIBLE); + if (!force) installCmd = BashCommands.alternatives("which ansible", installCmd); return ssh(installCmd).summary("install ansible"); } @@ -59,19 +62,23 @@ public class AnsiblePlaybookTasks { command); } + @Deprecated /** @deprecated since 1.1, not used; pass the yaml manually */ public static TaskFactory<?> buildPlaybookFile(final String ansibleDirectory, String playbook) { Entity entity = EffectorTasks.findEntity(); String yaml = entity.config().get(AnsibleConfig.ANSIBLE_PLAYBOOK_YAML); + return buildPlaybookFile(ansibleDirectory, playbook, yaml); + } + public static TaskFactory<?> buildPlaybookFile(final String ansibleDirectory, String playbook, String playbookYaml) { return Tasks.sequential("build ansible playbook file for "+ playbook, SshEffectorTasks.put(Urls.mergePaths(ansibleDirectory) + "/" + playbook + ".yaml") - .contents(yaml).createDirectory()); + .contents(playbookYaml).createDirectory()); } - public static TaskFactory<?> runAnsible(final String dir, Object extraVars, String playbookName) { - String cmd = sudo(String.format("ansible-playbook " + public static SshEffectorTasks.SshEffectorTaskFactory<Integer> runAnsible(final String dir, Object extraVars, String playbookName) { + String cmd = sudo("ansible-playbook " + optionalExtraVarsParameter(extraVars) - + " -b %s.yaml", playbookName)); + + String.format(" -b %s.yaml", playbookName)); if (LOG.isDebugEnabled()) { LOG.debug("Ansible command: {}", cmd); diff --git a/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleSshWorkflowStep.java b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleSshWorkflowStep.java new file mode 100644 index 000000000..4a6596858 --- /dev/null +++ b/software/cm/ansible/src/main/java/org/apache/brooklyn/entity/cm/ansible/AnsibleSshWorkflowStep.java @@ -0,0 +1,139 @@ +/* + * 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.entity.cm.ansible; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.apache.brooklyn.api.entity.drivers.DriverDependentEntity; +import org.apache.brooklyn.api.entity.drivers.EntityDriver; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.location.Locations; +import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils; +import org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils; +import org.apache.brooklyn.core.workflow.WorkflowStepDefinition; +import org.apache.brooklyn.core.workflow.WorkflowStepInstanceExecutionContext; +import org.apache.brooklyn.core.workflow.steps.SshWorkflowStep; +import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessDriver; +import org.apache.brooklyn.location.ssh.SshMachineLocation; +import org.apache.brooklyn.util.core.task.DynamicTasks; +import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.text.Strings; +import org.apache.brooklyn.util.yaml.Yamls; + +import java.util.Map; + +public class AnsibleSshWorkflowStep extends WorkflowStepDefinition { + + public static final String SHORTHAND = "${playbook_url} [ \" named \" ${playbook_name} ]"; + + public static final ConfigKey<String> RUN_DIR = ConfigKeys.newStringConfigKey("run_dir"); + public static final ConfigKey<String> INSTALL_DIR = ConfigKeys.newStringConfigKey("install_dir"); + + public static final ConfigKey<Boolean> INSTALL_ANSIBLE = ConfigKeys.newBooleanConfigKey("install"); + + public static final ConfigKey<String> ANSIBLE_PLAYBOOK = ConfigKeys.newStringConfigKey("playbook_name", "Local filename to use when installing the playbook"); + public static final ConfigKey<Object> ANSIBLE_PLAYBOOK_YAML = ConfigKeys.newConfigKey(Object.class, "playbook_yaml"); + public static final ConfigKey<String> ANSIBLE_PLAYBOOK_URL = ConfigKeys.newStringConfigKey("playbook_url"); + public static final ConfigKey<Object> ANSIBLE_VARS = ConfigKeys.newConfigKey(Object.class, "vars"); + + @Override + public void populateFromShorthand(String expression) { + populateFromShorthandTemplate(SHORTHAND, expression); + } + + @Override + protected Object doTaskBody(WorkflowStepInstanceExecutionContext context) { + SshMachineLocation machine = Locations.findUniqueSshMachineLocation(context.getEntity().getLocations()).orThrow("No SSH location available for workflow at " + context.getEntity()); + + Object extraVars = context.getInput(ANSIBLE_VARS); + String playbookName = context.getInput(ANSIBLE_PLAYBOOK); + + String playbookUrl = context.getInput(ANSIBLE_PLAYBOOK_URL); + Object playbookYamlO = context.getInput(ANSIBLE_PLAYBOOK_YAML); + + if (playbookUrl != null && playbookYamlO != null) { + throw new IllegalArgumentException( "You cannot specify both "+ AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName() + + " and " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " as arguments."); + } + + if (playbookUrl == null && playbookYamlO == null) { + throw new IllegalArgumentException("You have to specify either " + AnsibleConfig.ANSIBLE_PLAYBOOK_URL.getName() + + " or " + AnsibleConfig.ANSIBLE_PLAYBOOK_YAML.getName() + " as arguments."); + } + + String playbookYaml; + try { + playbookYaml = playbookYamlO==null ? null : playbookYamlO instanceof String ? (String) playbookYamlO : + BeanWithTypeUtils.newYamlMapper(context.getManagementContext(), false, null, false).writeValueAsString(playbookYamlO); + } catch (JsonProcessingException e) { + throw Exceptions.propagateAnnotated("Invalid YAML supplied for playbook", e); + } + if (playbookName==null) playbookName = "playbook-"+Strings.firstNonNull(playbookUrl, playbookYaml).hashCode(); + + if (!Boolean.FALSE.equals(context.getInput(INSTALL_ANSIBLE))) { + DynamicTasks.queue(AnsiblePlaybookTasks.installAnsible(getInstallDir(context), false)); + DynamicTasks.queue(AnsiblePlaybookTasks.setUpHostsFile(false)); + } + + if (extraVars != null) { + DynamicTasks.queue(AnsiblePlaybookTasks.configureExtraVars(getRunDir(context), extraVars, false)); + } + + if (Strings.isNonBlank(playbookUrl)) { + DynamicTasks.queue(AnsiblePlaybookTasks.installPlaybook(getRunDir(context), playbookName, playbookUrl)); + } + + if (Strings.isNonBlank(playbookYaml)) { + DynamicTasks.queue(AnsiblePlaybookTasks.buildPlaybookFile(getRunDir(context), playbookName, playbookYaml)); + } + + ProcessTaskFactory<Map<?,?>> tf = SshWorkflowStep.customizeProcessTaskFactory(context, AnsiblePlaybookTasks.runAnsible(getRunDir(context), extraVars, playbookName)); + return DynamicTasks.queue(tf).asTask().getUnchecked(); + } + + private String getRunDir(WorkflowStepInstanceExecutionContext context) { + String candidate = context.getInput(RUN_DIR); + if (candidate!=null) return candidate; + + if (context.getEntity() instanceof DriverDependentEntity) { + EntityDriver driver = ((DriverDependentEntity) context.getEntity()).getDriver(); + if (driver instanceof AbstractSoftwareProcessDriver) { + return ((AbstractSoftwareProcessDriver)driver).getRunDir(); + } + } + + return "./brooklyn-managed-ansible/install/"; + } + + private String getInstallDir(WorkflowStepInstanceExecutionContext context) { + String candidate = context.getInput(INSTALL_DIR); + if (candidate!=null) return candidate; + + if (context.getEntity() instanceof DriverDependentEntity) { + EntityDriver driver = ((DriverDependentEntity) context.getEntity()).getDriver(); + if (driver instanceof AbstractSoftwareProcessDriver) { + return ((AbstractSoftwareProcessDriver)driver).getInstallDir(); + } + } + + return "./brooklyn-managed-ansible/run-"+context.getEntity().getApplicationId()+"-"+context.getEntity().getId()+"/"; + } + +} diff --git a/software/cm/ansible/src/main/resources/catalog.bom b/software/cm/ansible/src/main/resources/catalog.bom index 8ebb5f75a..26a834539 100644 --- a/software/cm/ansible/src/main/resources/catalog.bom +++ b/software/cm/ansible/src/main/resources/catalog.bom @@ -25,3 +25,8 @@ brooklyn.catalog: type: org.apache.brooklyn.entity.cm.ansible.AnsibleEntity name: AnsibleEntity description: Software managed by Ansible CM + - id: ansible-ssh + format: java-type-name + itemType: bean + item: + type: org.apache.brooklyn.entity.cm.ansible.AnsibleSshWorkflowStep
