You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2022/10/22 00:01:38 UTC
[brooklyn-library] 02/02: ansible workflow step, and tidies to ansible
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 <al...@cloudsoft.io>
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