001/*
002 * Copyright 2025 devteam@scivics-lab.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing,
011 * software distributed under the License is distributed on an
012 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied.  See the License for the
014 * specific language governing permissions and limitations
015 * under the License.
016 */
017
018package com.scivicslab.actoriac.example;
019
020import java.io.FileInputStream;
021import java.util.logging.Level;
022import java.util.logging.Logger;
023
024import com.scivicslab.actoriac.NodeGroup;
025import com.scivicslab.actoriac.NodeGroupIIAR;
026import com.scivicslab.actoriac.NodeGroupInterpreter;
027import com.scivicslab.pojoactor.core.ActionResult;
028import com.scivicslab.pojoactor.workflow.IIActorSystem;
029
030/**
031 * Generic workflow runner for actor-IaC (Level 3).
032 *
033 * <p>This class provides a reusable entry point for executing workflows
034 * on infrastructure nodes. The actual operations are defined in external
035 * YAML/JSON/XML workflow files, not in Java code.</p>
036 *
037 * <h2>Usage</h2>
038 * <pre>
039 * java -jar actor-IaC.jar inventory.ini webservers workflow.yaml
040 * </pre>
041 *
042 * <h2>Actor Hierarchy</h2>
043 * <pre>
044 * IIActorSystem
045 *   └─ NodeGroupIIAR (parent)
046 *        ├─ NodeIIAR (child, autonomous agent)
047 *        ├─ NodeIIAR (child, autonomous agent)
048 *        └─ NodeIIAR (child, autonomous agent)
049 * </pre>
050 *
051 * <h2>Design Principle</h2>
052 * <p>This Java code is generic and does not need modification when
053 * changing targets or operations. All customization is done through:</p>
054 * <ul>
055 *   <li><strong>inventory.ini</strong> - Define hosts and groups</li>
056 *   <li><strong>workflow.yaml</strong> - Define operations to execute</li>
057 * </ul>
058 *
059 * @author devteam@scivics-lab.com
060 */
061public class WorkflowRunner {
062
063    private static final Logger LOG = Logger.getLogger(WorkflowRunner.class.getName());
064
065    private final IIActorSystem system;
066    private final NodeGroupIIAR nodeGroupActor;
067
068    /**
069     * Constructs a WorkflowRunner with the specified system and parent actor.
070     *
071     * @param system the IIActorSystem for workflow execution
072     * @param nodeGroupActor the NodeGroupIIAR parent actor
073     */
074    public WorkflowRunner(IIActorSystem system, NodeGroupIIAR nodeGroupActor) {
075        this.system = system;
076        this.nodeGroupActor = nodeGroupActor;
077    }
078
079    /**
080     * Main entry point for workflow execution.
081     *
082     * <p>Arguments:</p>
083     * <ol>
084     *   <li>inventory - Path to Ansible inventory file</li>
085     *   <li>group - Name of the host group to target</li>
086     *   <li>workflow - Path to workflow file (YAML/JSON/XML)</li>
087     * </ol>
088     *
089     * @param args command line arguments
090     * @throws Exception if execution fails
091     */
092    public static void main(String[] args) throws Exception {
093        if (args.length < 3) {
094            System.out.println("Usage: java -jar actor-IaC.jar <inventory> <group> <workflow>");
095            System.out.println();
096            System.out.println("Arguments:");
097            System.out.println("  inventory  - Path to Ansible inventory file (e.g., inventory.ini)");
098            System.out.println("  group      - Name of the host group to target (e.g., webservers)");
099            System.out.println("  workflow   - Path to workflow file (e.g., deploy.yaml)");
100            System.out.println();
101            System.out.println("Example:");
102            System.out.println("  java -jar actor-IaC.jar inventory.ini webservers deploy.yaml");
103            return;
104        }
105
106        String inventoryPath = args[0];
107        String groupName = args[1];
108        String workflowPath = args[2];
109
110        LOG.info("=== actor-IaC Workflow Runner ===");
111        LOG.log(Level.INFO, "Inventory: {0}", inventoryPath);
112        LOG.log(Level.INFO, "Group: {0}", groupName);
113        LOG.log(Level.INFO, "Workflow: {0}", workflowPath);
114
115        IIActorSystem system = new IIActorSystem("actor-iac-workflow");
116
117        try {
118            // Step 1: Create NodeGroup POJO (Level 1 functionality)
119            // Pure POJO creation, independent of IIActorSystem
120            NodeGroup nodeGroup = new NodeGroup.Builder()
121                .withInventory(new FileInputStream(inventoryPath))
122                .build();
123            LOG.info("NodeGroup POJO created");
124
125            // Step 2: Wrap NodeGroup with Interpreter (Level 3 functionality)
126            NodeGroupInterpreter nodeGroupInterpreter = new NodeGroupInterpreter(nodeGroup, system);
127            LOG.info("NodeGroupInterpreter created");
128
129            // Step 3: Convert to IIActor
130            NodeGroupIIAR nodeGroupActor = new NodeGroupIIAR("nodeGroup", nodeGroupInterpreter, system);
131            system.addIIActor(nodeGroupActor);
132            LOG.info("NodeGroupIIAR actor created");
133
134            // Run the workflow
135            WorkflowRunner runner = new WorkflowRunner(system, nodeGroupActor);
136            runner.run(groupName, workflowPath);
137
138            LOG.info("=== Complete ===");
139        } finally {
140            system.terminate();
141        }
142    }
143
144    /**
145     * Executes the workflow on the specified group.
146     *
147     * <p>Uses the apply method with runWorkflow to load and run workflows
148     * on all matching child actors in a single step.</p>
149     *
150     * @param groupName the name of the host group
151     * @param workflowPath the path to the workflow file
152     * @throws Exception if execution fails
153     */
154    public void run(String groupName, String workflowPath) throws Exception {
155        // Step 1: Create child NodeIIARs for the group
156        LOG.log(Level.INFO, "Creating node actors for group: {0}", groupName);
157        ActionResult createResult = nodeGroupActor.callByActionName(
158            "createNodeActors", "[\"" + groupName + "\"]");
159
160        if (!createResult.isSuccess()) {
161            LOG.log(Level.SEVERE, "Failed to create node actors: {0}", createResult.getResult());
162            return;
163        }
164        LOG.log(Level.INFO, "Create result: {0}", createResult.getResult());
165
166        // Step 2: Run workflow on all nodes using apply + runWorkflow
167        LOG.log(Level.INFO, "Running workflow on all nodes: {0}", workflowPath);
168        String runAction = String.format(
169            "{\"actor\": \"node-*\", \"method\": \"runWorkflow\", \"arguments\": [\"%s\"]}",
170            workflowPath);
171        ActionResult result = nodeGroupActor.callByActionName("apply", runAction);
172
173        LOG.info("Workflow execution result:");
174        LOG.log(Level.INFO, "  Success: {0}", result.isSuccess());
175        LOG.log(Level.INFO, "  Message: {0}", result.getResult());
176    }
177
178    /**
179     * Executes a single command on all nodes in the group.
180     *
181     * <p>This is a convenience method for simple use cases where
182     * a full workflow is not needed.</p>
183     *
184     * @param groupName the name of the host group
185     * @param command the command to execute
186     * @throws Exception if execution fails
187     */
188    public void executeCommand(String groupName, String command) throws Exception {
189        // Create child NodeIIARs if not already created
190        ActionResult createResult = nodeGroupActor.callByActionName(
191            "createNodeActors", "[\"" + groupName + "\"]");
192
193        if (!createResult.isSuccess()) {
194            LOG.log(Level.SEVERE, "Failed to create node actors: {0}", createResult.getResult());
195            return;
196        }
197
198        // Execute command on all nodes
199        LOG.log(Level.INFO, "Executing command on all nodes: {0}", command);
200        ActionResult result = nodeGroupActor.callByActionName(
201            "executeCommandOnAllNodes", "[\"" + command + "\"]");
202
203        LOG.log(Level.INFO, "Command result: {0}", result.getResult());
204    }
205}